Android Open Source - LearnByHeart Unfoldable View






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;
/*  w w w  .java 2 s . c om*/
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.FrameLayout;

/**
 * View that provides ability to switch between 2 diffrent views (cover view & details view) with fold animation.
 * <p/>
 * It is implemented as subclass of FoldableListLayout with only 2 views to scroll between.
 */
public class UnfoldableView extends FoldableListLayout {

    private View mDefaultDetailsPlaceHolderView;
    private View mDefaultCoverPlaceHolderView;

    private View mDetailsView, mCoverView;
    private View mDetailsPlaceHolderView, mCoverPlaceHolderView;
    private CoverHolderLayout mCoverHolderLayout;

    private View mScheduledCoverView, mScheduledDetailsView;

    private ViewGroup.LayoutParams mDetailsViewParams, mCoverViewParams;
    private int mDetailsViewParamWidth, mDetailsViewParamHeight, mCoverViewParamWidth, mCoverViewParamHeight;
    private Rect mCoverViewPosition, mDetailsViewPosition;

    private Adapter mAdapter;

    private float mLastFoldRotation;
    private boolean mIsUnfolding;
    private boolean mIsFoldingBack;
    private boolean mIsUnfolded;

    private OnFoldingListener mListener;

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

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

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

    private void init(Context context) {
        mCoverHolderLayout = new CoverHolderLayout(context);
        mDefaultDetailsPlaceHolderView = new View(context);
        mDefaultCoverPlaceHolderView = new View(context);
        mAdapter = new Adapter();
    }

    public void setOnFoldingListener(OnFoldingListener listener) {
        mListener = listener;
    }

    private void setDetailsViewInternal(View detailsView) {
        // saving details view data
        mDetailsView = detailsView;
        mDetailsViewParams = detailsView.getLayoutParams();
        mDetailsViewParamWidth = mDetailsViewParams.width;
        mDetailsViewParamHeight = mDetailsViewParams.height;

        // getting details view positions on screen
        mDetailsViewPosition = getViewGlobalPosition(detailsView);

        // creating placeholder to show in place of details view
        mDetailsPlaceHolderView = createDetailsPlaceHolderView();

        // setting precise width/height params and switching details view with it's placeholder
        mDetailsViewParams.width = mDetailsViewPosition.width();
        mDetailsViewParams.height = mDetailsViewPosition.height();
        switchViews(detailsView, mDetailsPlaceHolderView, mDetailsViewParams);
    }

    private void clearDetailsViewInternal() {
        if (mDetailsView == null) return; // nothing to do

        // restoring original width/height params and adding cover view back to it's place
        mDetailsViewParams.width = mDetailsViewParamWidth;
        mDetailsViewParams.height = mDetailsViewParamHeight;
        switchViews(mDetailsPlaceHolderView, mDetailsView, mDetailsViewParams);

        // clearing references
        mDetailsView = null;
        mDetailsViewParams = null;
        mDetailsViewPosition = null;
        mDetailsPlaceHolderView = null;
    }

    private void setCoverViewInternal(View coverView) {
        // saving cover view data
        mCoverView = coverView;
        mCoverViewParams = coverView.getLayoutParams();
        mCoverViewParamWidth = mCoverViewParams.width;
        mCoverViewParamHeight = mCoverViewParams.height;

        // getting cover view positions on screen
        mCoverViewPosition = getViewGlobalPosition(coverView);

        // creating placeholder to show in place of cover view
        mCoverPlaceHolderView = createCoverPlaceHolderView();

        // setting precise width/height params and switching cover view with it's placeholder
        mCoverViewParams.width = mCoverViewPosition.width();
        mCoverViewParams.height = mCoverViewPosition.height();
        switchViews(coverView, mCoverPlaceHolderView, mCoverViewParams);

        // moving cover view into special cover view holder (for unfold animation)
        mCoverHolderLayout.setView(coverView, mCoverViewPosition.width(), mCoverViewPosition.height());
    }

    private void clearCoverViewInternal() {
        if (mCoverView == null) return; // nothing to do

        // freeing coverView so we can add it back to it's palce
        mCoverHolderLayout.clearView();

        // restoring original width/height params and adding cover view back to it's place
        mCoverViewParams.width = mCoverViewParamWidth;
        mCoverViewParams.height = mCoverViewParamHeight;
        switchViews(mCoverPlaceHolderView, mCoverView, mCoverViewParams);

        // clearing references
        mCoverView = null;
        mCoverViewParams = null;
        mCoverViewPosition = null;
        mCoverPlaceHolderView = null;
    }

    public void changeCoverView(View coverView) {
        if (mCoverView == null || mCoverView == coverView) return; // nothing to do
        clearCoverViewInternal();
        setCoverViewInternal(coverView);
    }

    protected View createDetailsPlaceHolderView() {
        return mDefaultDetailsPlaceHolderView;
    }

    protected View createCoverPlaceHolderView() {
        return mDefaultCoverPlaceHolderView;
    }

    /**
     * Starting unfold animation for given views
     */
    public void unfold(View coverView, View detailsView) {
        if (mCoverView == coverView && mDetailsView == detailsView) return; // already in place

        if ((mCoverView != null && mCoverView != coverView) || (mDetailsView != null && mDetailsView != detailsView)) {
            // cover or details view is differ - closing details and schedule reopening
            mScheduledDetailsView = detailsView;
            mScheduledCoverView = coverView;
            foldBack();
            return;
        }

        setCoverViewInternal(coverView);
        setDetailsViewInternal(detailsView);

        // initializing foldable views
        setAdapter(mAdapter);

        // starting unfold animation
        scrollToPosition(1);
    }

    public void foldBack() {
        scrollToPosition(0);
    }

    private void onFoldedBack() {
        // clearing all foldable views
        setAdapter(null);

        clearCoverViewInternal();
        clearDetailsViewInternal();

        // clearing translations
        setTranslationX(0);
        setTranslationY(0);

        if (mScheduledCoverView != null && mScheduledDetailsView != null) {
            View scheduledDetails = mScheduledDetailsView;
            View scheduledCover = mScheduledCoverView;
            mScheduledDetailsView = mScheduledCoverView = null;
            unfold(scheduledCover, scheduledDetails);
        }
    }

    public boolean isUnfolding() {
        return mIsUnfolding;
    }

    public boolean isFoldingBack() {
        return mIsFoldingBack;
    }

    public boolean isUnfolded() {
        return mIsUnfolded;
    }

    @Override
    public void setFoldRotation(float rotation, boolean isFromUser) {
        super.setFoldRotation(rotation, isFromUser);
        if (mCoverView == null || mDetailsView == null) return; // nothing we can do here

        rotation = getFoldRotation(); // parent view will correctly keep rotation in bounds for us

        // translating from cover's position to details position
        float stage = rotation / 180; // from 0 = only cover view, to 1 - only details view

        float fromX = mCoverViewPosition.centerX();
        float toX = mDetailsViewPosition.centerX();

        float fromY = mCoverViewPosition.top;
        float toY = mDetailsViewPosition.centerY();

        setTranslationX((fromX - toX) * (1 - stage));
        setTranslationY((fromY - toY) * (1 - stage));

        // tracking states

        float lastRotatation = mLastFoldRotation;
        mLastFoldRotation = rotation;

        if (mListener != null) mListener.onFoldProgress(this, stage);

        if (rotation > lastRotatation && !mIsUnfolding) {
            mIsUnfolding = true;
            mIsFoldingBack = false;
            mIsUnfolded = false;

            if (mListener != null) mListener.onUnfolding(this);
        }

        if (rotation < lastRotatation && !mIsFoldingBack) {
            mIsUnfolding = false;
            mIsFoldingBack = true;
            mIsUnfolded = false;

            if (mListener != null) mListener.onFoldingBack(this);
        }

        if (rotation == 180 && !mIsUnfolded) {
            mIsUnfolding = false;
            mIsFoldingBack = false;
            mIsUnfolded = true;

            if (mListener != null) mListener.onUnfolded(this);
        }

        if (rotation == 0 && mIsFoldingBack) {
            mIsUnfolding = false;
            mIsFoldingBack = false;
            mIsUnfolded = false;

            onFoldedBack();
            if (mListener != null) mListener.onFoldedBack(this);
        }
    }

    @Override
    protected void onFoldRotationChanged(FoldableItemLayout layout, int position) {
        super.onFoldRotationChanged(layout, position);

        float stage = getFoldRotation() / 180; // from 0 = only cover view, to 1 - only details view

        float coverW = mCoverViewPosition.width();
        float detailsW = mDetailsViewPosition.width();

        if (position == 0) { // cover view
            // scaling cover view from origin size to the size (width) of the details view
            float coverScale = 1 - (1 - detailsW / coverW) * stage;
            layout.setScale(coverScale);
        } else { // details view
            // scaling details view from cover's size to the original size
            float detailsScale = 1 - (1 - coverW / detailsW) * (1 - stage);
            layout.setScale(detailsScale);

            float dH = mDetailsViewPosition.height() / 2 - mCoverViewPosition.height() * detailsW / coverW;
            float translationY = stage < 0.5f ? -dH * (1 - 2 * stage) : 0;

            layout.setRollingDistance(translationY);
        }
    }

    private void switchViews(View origin, View replacement, ViewGroup.LayoutParams params) {
        ViewGroup parent = (ViewGroup) origin.getParent();

        if (params == null) params = origin.getLayoutParams();

        int index = parent.indexOfChild(origin);
        parent.removeViewAt(index);
        parent.addView(replacement, index, params);
    }

    private Rect getViewGlobalPosition(View view) {
        int[] location = new int[2];
        view.getLocationOnScreen(location);
        return new Rect(location[0], location[1], location[0] + view.getWidth(), location[1] + view.getHeight());
    }


    /**
     * Simple adapter that will alternate between cover view holder layout and details layout
     */
    private class Adapter extends BaseAdapter {

        @Override
        public int getCount() {
            return 2;
        }

        @Override
        public Object getItem(int i) {
            return null;
        }

        @Override
        public long getItemId(int i) {
            return i;
        }

        @Override
        public View getView(int i, View recycledView, ViewGroup parent) {
            return i == 0 ? mCoverHolderLayout : mDetailsView;
        }
    }


    /**
     * Cover view holder layout. It can contain at most one child which will be positioned in the top|center_horisontal
     * location of bottom half of the view.
     */
    private static class CoverHolderLayout extends FrameLayout {

        private final Rect mVisibleBounds = new Rect();

        private CoverHolderLayout(Context context) {
            super(context);
        }

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            int h = getMeasuredHeight();
            setPadding(0, h / 2, 0, 0);
        }

        @Override
        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
            super.onLayout(changed, left, top, right, bottom);

            // Collecting visible bounds of child view, it will be used to correctly draw shadows and to
            // improve drawing performance
            View view = getView();
            if (view != null) {
                mVisibleBounds.set(view.getLeft(), view.getTop(),
                        view.getLeft() + view.getWidth(), view.getTop() + view.getHeight());

                FoldableItemLayout foldableLayout = findParentFoldableLayout();
                if (foldableLayout != null) foldableLayout.setLayoutVisibleBounds(mVisibleBounds);
            } else {
                mVisibleBounds.set(0, 0, 0, 0);
            }
        }

        private FoldableItemLayout findParentFoldableLayout() {
            ViewGroup parent = this;
            while (parent != null) {
                parent = (ViewGroup) parent.getParent();
                if (parent instanceof FoldableItemLayout) {
                    return (FoldableItemLayout) parent;
                }
            }
            return null;
        }

        private void setView(View view, int w, int h) {
            removeAllViews();
            LayoutParams params = new LayoutParams(w, h, Gravity.CENTER_HORIZONTAL);
            addView(view, params);
        }

        private View getView() {
            return getChildCount() > 0 ? getChildAt(0) : null;
        }

        private void clearView() {
            removeAllViews();
        }

    }

    public interface OnFoldingListener {
        void onUnfolding(UnfoldableView unfoldableView);

        void onUnfolded(UnfoldableView unfoldableView);

        void onFoldingBack(UnfoldableView unfoldableView);

        void onFoldedBack(UnfoldableView unfoldableView);

        void onFoldProgress(UnfoldableView unfoldableView, float progress);
    }

    public static class SimpleFoldingListener implements OnFoldingListener {
        @Override
        public void onUnfolding(UnfoldableView unfoldableView) {
        }

        @Override
        public void onUnfolded(UnfoldableView unfoldableView) {
        }

        @Override
        public void onFoldingBack(UnfoldableView unfoldableView) {
        }

        @Override
        public void onFoldedBack(UnfoldableView unfoldableView) {
        }

        @Override
        public void onFoldProgress(UnfoldableView unfoldableView, float progress) {
        }
    }

}




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