Android Open Source - LearnByHeart Foldable Item Layout






From Project

Back to project page LearnByHeart.

License

The source code is released under:

Apache License

If you think the Android project LearnByHeart listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package com.mps.learn.pb.ui.views;
//from   www  . ja  v a2  s  .co  m
import android.content.Context;
import android.graphics.*;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import com.mps.learn.pb.ui.views.shading.FoldShading;

/**
 * Layout that provides basic funcitionality: splitting view into 2 parts, splitted parts syncronouse rotations and so on
 */
public class FoldableItemLayout extends FrameLayout {

    private static final int CAMERA_DISTANCE = 48;
    private static final float CAMERA_DISTANCE_MAGIC_FACTOR = 8f / CAMERA_DISTANCE;

    private boolean mIsAutoScaleEnabled;

    private BaseLayout mBaseLayout;
    private PartView mTopPart, mBottomPart;

    private int mWidth, mHeight;
    private Bitmap mCacheBitmap;

    private boolean mIsInTransformation;

    private float mFoldRotation;
    private float mScale;
    private float mRollingDistance;

    public FoldableItemLayout(Context context) {
        super(context);
        init(context);
    }

    public FoldableItemLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public FoldableItemLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context);
    }

    private void init(Context context) {
        mBaseLayout = new BaseLayout(this);

        mTopPart = new PartView(this, Gravity.TOP);
        mBottomPart = new PartView(this, Gravity.BOTTOM);

        setInTransformation(false);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mBaseLayout.moveInflatedChildren(this, 3); // skipping mBaseLayout & mTopPart & mBottomPart views
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        mWidth = w;
        mHeight = h;

        if (mCacheBitmap != null) {
            mCacheBitmap.recycle();
            mCacheBitmap = null;
        }

        mCacheBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);

        mBaseLayout.setCacheCanvas(new Canvas(mCacheBitmap));

        mTopPart.setCacheBitmap(mCacheBitmap);
        mBottomPart.setCacheBitmap(mCacheBitmap);
    }

    /**
     * Fold rotation value in degrees
     */
    public void setFoldRotation(float rotation) {
        mFoldRotation = rotation;

        mTopPart.applyFoldRotation(rotation);
        mBottomPart.applyFoldRotation(rotation);

        setInTransformation(rotation != 0);

        if (mIsAutoScaleEnabled) {
            float viewScale = 1.0f;
            if (mWidth > 0) {
                float dW = (float) (mHeight * Math.abs(Math.sin(Math.toRadians(rotation)))) * CAMERA_DISTANCE_MAGIC_FACTOR;
                viewScale = mWidth / (mWidth + dW);
            }

            setScale(viewScale);
        }
    }

    public float getFoldRotation() {
        return mFoldRotation;
    }

    public void setScale(float scale) {
        mScale = scale;
        mTopPart.applyScale(scale);
        mBottomPart.applyScale(scale);
    }

    public float getScale() {
        return mScale;
    }

    /**
     * Translation preserving middle line splitting
     */
    public void setRollingDistance(float distance) {
        mRollingDistance = distance;
        mTopPart.applyRollingDistance(distance, mScale);
        mBottomPart.applyRollingDistance(distance, mScale);
    }

    public float getRollingDistance() {
        return mRollingDistance;
    }

    private void setInTransformation(boolean isInTransformation) {
        if (mIsInTransformation == isInTransformation) return;
        mIsInTransformation = isInTransformation;

        mBaseLayout.setDrawToCache(isInTransformation);
        mTopPart.setVisibility(isInTransformation ? VISIBLE : INVISIBLE);
        mBottomPart.setVisibility(isInTransformation ? VISIBLE : INVISIBLE);
    }

    public void setAutoScaleEnabled(boolean isAutoScaleEnabled) {
        mIsAutoScaleEnabled = isAutoScaleEnabled;
    }

    public FrameLayout getBaseLayout() {
        return mBaseLayout;
    }

    public void setLayoutVisibleBounds(Rect visibleBounds) {
        mTopPart.setVisibleBounds(visibleBounds);
        mBottomPart.setVisibleBounds(visibleBounds);
    }

    public void setFoldShading(FoldShading shading) {
        mTopPart.setFoldShading(shading);
        mBottomPart.setFoldShading(shading);
    }


    /**
     * View holder layout that can draw itself into given canvas
     */
    private static class BaseLayout extends FrameLayout {

        private Canvas mCacheCanvas;
        private boolean mIsDrawToCache;

        @SuppressWarnings("deprecation")
        private BaseLayout(FoldableItemLayout layout) {
            super(layout.getContext());

            int matchParent = ViewGroup.LayoutParams.MATCH_PARENT;
            LayoutParams params = new LayoutParams(matchParent, matchParent);
            layout.addView(this, params);

            // Moving background
            this.setBackgroundDrawable(layout.getBackground());
            layout.setBackgroundDrawable(null);

            setWillNotDraw(false);
        }

        private void moveInflatedChildren(FoldableItemLayout layout, int firstSkippedItems) {
            while (layout.getChildCount() > firstSkippedItems) {
                View view = layout.getChildAt(firstSkippedItems);
                LayoutParams params = (LayoutParams) view.getLayoutParams();
                layout.removeViewAt(firstSkippedItems);
                addView(view, params);
            }
        }

        @Override
        public void draw(Canvas canvas) {
            if (mIsDrawToCache) {
                mCacheCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
                super.draw(mCacheCanvas);
            } else {
                super.draw(canvas);
            }
        }

        private void setCacheCanvas(Canvas cacheCanvas) {
            mCacheCanvas = cacheCanvas;
        }

        private void setDrawToCache(boolean drawToCache) {
            if (mIsDrawToCache == drawToCache) return;
            mIsDrawToCache = drawToCache;
            invalidate();
        }

    }

    /**
     * Splitted part view. It will draw top or bottom part of cached bitmap and overlay shadows.
     * Also it contains main logic for all transformations (fold rotation, scale, "rolling distance").
     */
    private static class PartView extends View {

        private final int mGravity;

        private Bitmap mBitmap;
        private final Rect mBitmapBounds = new Rect();

        private float mClippingFactor = 0.5f;

        private final Paint mBitmapPaint;

        private Rect mVisibleBounds;

        private int mInternalVisibility;
        private int mExtrenalVisibility;

        private float mLocalFoldRotation;
        private FoldShading mShading;

        public PartView(FoldableItemLayout parent, int gravity) {
            super(parent.getContext());
            mGravity = gravity;

            final int matchParent = LayoutParams.MATCH_PARENT;
            parent.addView(this, new LayoutParams(matchParent, matchParent));
            setCameraDistance(CAMERA_DISTANCE * getResources().getDisplayMetrics().densityDpi);

            mBitmapPaint = new Paint();
            mBitmapPaint.setDither(true);
            mBitmapPaint.setFilterBitmap(true);

            setWillNotDraw(false);
        }

        private void setCacheBitmap(Bitmap bitmap) {
            mBitmap = bitmap;
            calculateBitmapBounds();
        }

        private void setVisibleBounds(Rect visibleBounds) {
            mVisibleBounds = visibleBounds;
            calculateBitmapBounds();
        }

        private void setFoldShading(FoldShading shading) {
            mShading = shading;
        }

        private void calculateBitmapBounds() {
            if (mBitmap == null) {
                mBitmapBounds.set(0, 0, 0, 0);
            } else {
                int h = mBitmap.getHeight();
                int w = mBitmap.getWidth();

                int top = mGravity == Gravity.TOP ? 0 : (int) (h * (1 - mClippingFactor) - 0.5f);
                int bottom = mGravity == Gravity.TOP ? (int) (h * mClippingFactor + 0.5f) : h;

                mBitmapBounds.set(0, top, w, bottom);
                if (mVisibleBounds != null) {
                    if (!mBitmapBounds.intersect(mVisibleBounds)) {
                        mBitmapBounds.set(0, 0, 0, 0); // no intersection
                    }
                }
            }

            invalidate();
        }

        private void applyFoldRotation(float rotation) {
            float position = rotation;
            while (position < 0) position += 360;
            position %= 360;
            if (position > 180) position -= 360; // now poistion within (-180; 180]

            float rotationX = 0;
            boolean isVisible = true;

            if (mGravity == Gravity.TOP) {
                if (position <= -90 || position == 180) { // (-180; -90] || {180} - will not show
                    isVisible = false;
                } else if (position < 0) { // (-90; 0) - applying rotation
                    rotationX = position;
                }
                // [0; 180) - holding still
            } else {
                if (position >= 90) { // [90; 180] - will not show
                    isVisible = false;
                } else if (position > 0) { // (0; 90) - applying rotation
                    rotationX = position;
                }
                // else: (-180; 0] - holding still
            }

            setRotationX(rotationX);

            mInternalVisibility = isVisible ? VISIBLE : INVISIBLE;
            applyVisibility();

            mLocalFoldRotation = position;

            invalidate(); // needed to draw shadow overlay
        }

        private void applyScale(float scale) {
            setScaleX(scale);
            setScaleY(scale);
        }

        private void applyRollingDistance(float distance, float scale) {
            // applying translation
            setTranslationY((int) (distance * scale + 0.5f));

            // computing clipping for top view (bottom clipping will be 1 - topClipping)
            final int h = getHeight() / 2;
            final float topClipping = h == 0 ? 0.5f : (h - distance) / h / 2;

            mClippingFactor = mGravity == Gravity.TOP ? topClipping : 1f - topClipping;

            calculateBitmapBounds();
        }

        @Override
        public void setVisibility(int visibility) {
            mExtrenalVisibility = visibility;
            applyVisibility();
        }

        private void applyVisibility() {
            super.setVisibility(mExtrenalVisibility == VISIBLE ? mInternalVisibility : mExtrenalVisibility);
        }

        @Override
        public void draw(Canvas canvas) {
            if (mShading != null) mShading.onPreDraw(canvas, mBitmapBounds, mLocalFoldRotation, mGravity);
            if (mBitmap != null) canvas.drawBitmap(mBitmap, mBitmapBounds, mBitmapBounds, mBitmapPaint);
            if (mShading != null) mShading.onPostDraw(canvas, mBitmapBounds, mLocalFoldRotation, mGravity);
        }

    }

}




Java Source Code List

com.mps.learn.pb.App.java
com.mps.learn.pb.PhraseManager.java
com.mps.learn.pb.adapter.AllPhraseAdapter.java
com.mps.learn.pb.alarm.ReminderReceiver.java
com.mps.learn.pb.alarm.ReminderUtil.java
com.mps.learn.pb.db.DataSourcePhrase.java
com.mps.learn.pb.db.DatabaseHelper.java
com.mps.learn.pb.db.PhraseReaderContract.java
com.mps.learn.pb.gcm.GcmBroadcastReceiver.java
com.mps.learn.pb.gcm.GcmIntentService.java
com.mps.learn.pb.http.DataUpdaterCallback.java
com.mps.learn.pb.http.DataUpdaterTask.java
com.mps.learn.pb.http.HttpConstant.java
com.mps.learn.pb.http.PhraseParser.java
com.mps.learn.pb.model.PhraseBook.java
com.mps.learn.pb.model.PhraseModel.java
com.mps.learn.pb.ui.activities.AddPhraseActivity.java
com.mps.learn.pb.ui.activities.MainActivity.java
com.mps.learn.pb.ui.activities.PhraseViewActivity.java
com.mps.learn.pb.ui.activities.SettingActivity.java
com.mps.learn.pb.ui.factivities.BaseActivity.java
com.mps.learn.pb.ui.factivities.FActivity.java
com.mps.learn.pb.ui.factivities.FoldableListActivity.java
com.mps.learn.pb.ui.factivities.ItemsAdapter.java
com.mps.learn.pb.ui.factivities.PaintingsAdapter.java
com.mps.learn.pb.ui.factivities.SpannableBuilder.java
com.mps.learn.pb.ui.factivities.UnfoldableDetailsActivity.java
com.mps.learn.pb.ui.views.FoldableItemLayout.java
com.mps.learn.pb.ui.views.FoldableListLayout.java
com.mps.learn.pb.ui.views.UnfoldableView.java
com.mps.learn.pb.ui.views.shading.FoldShading.java
com.mps.learn.pb.ui.views.shading.GlanceFoldShading.java
com.mps.learn.pb.ui.views.shading.SimpleFoldShading.java
com.mps.learn.pb.util.CommonConstants.java
com.mps.learn.pb.util.Debugger.java