com.lfq.pulltorefresh.library.sideslip.SwipeMenuLayout.java Source code

Java tutorial

Introduction

Here is the source code for com.lfq.pulltorefresh.library.sideslip.SwipeMenuLayout.java

Source

/*
 * Copyright 2016 Yan Zhenjie
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.lfq.pulltorefresh.library.sideslip;

import android.content.Context;
import android.content.res.TypedArray;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.FrameLayout;
import android.widget.OverScroller;
import android.widget.TextView;

import com.lfq.pulltorefresh.library.R;

import java.util.ArrayList;

/**
 * Created by Yan Zhenjie on 2016/7/27.
 */
public class SwipeMenuLayout extends FrameLayout implements SwipeSwitch {

    public static final int DEFAULT_SCROLLER_DURATION = 200;

    private int mContentViewId = 0;
    private int mRightViewId = 0;

    private float mOpenPercent = 0.5f;
    private int mScrollerDuration = DEFAULT_SCROLLER_DURATION;

    private int mScaledTouchSlop;
    private int mLastX;
    private int mLastY;
    private int mDownX;
    private int mDownY;
    private View mContentView;
    private SwipeRightHorizontal mSwipeRightHorizontal;
    private SwipeHorizontal mSwipeCurrentHorizontal;
    private boolean shouldResetSwipe;
    private boolean mDragging;
    private boolean swipeEnable = true;
    private OverScroller mScroller;
    private VelocityTracker mVelocityTracker;
    private int mScaledMinimumFlingVelocity;
    private int mScaledMaximumFlingVelocity;

    public SwipeMenuLayout(Context context) {
        this(context, null);
    }

    public SwipeMenuLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SwipeMenuLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SwipeMenuLayout);
        mContentViewId = typedArray.getResourceId(R.styleable.SwipeMenuLayout_contentViewId, mContentViewId);
        mRightViewId = typedArray.getResourceId(R.styleable.SwipeMenuLayout_rightViewId, mRightViewId);
        typedArray.recycle();

        ViewConfiguration mViewConfig = ViewConfiguration.get(getContext());
        /**
         *  getScaledTouchSlop????????viewpager???
         */
        mScaledTouchSlop = mViewConfig.getScaledTouchSlop();
        mScroller = new OverScroller(getContext());
        mScaledMinimumFlingVelocity = mViewConfig.getScaledMinimumFlingVelocity();
        mScaledMaximumFlingVelocity = mViewConfig.getScaledMaximumFlingVelocity();
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        if (mRightViewId != 0 && mSwipeRightHorizontal == null) {
            View view = findViewById(mRightViewId);
            mSwipeRightHorizontal = new SwipeRightHorizontal(view);
        }
        if (mContentViewId != 0 && mContentView == null) {
            mContentView = findViewById(mContentViewId);
        } else {
            TextView errorView = new TextView(getContext());
            errorView.setClickable(true);
            errorView.setGravity(Gravity.CENTER);
            errorView.setTextSize(16);
            errorView.setText("You may not have set the ContentView.");
            mContentView = errorView;
            addView(mContentView);
        }
    }

    /**
     * Set whether open swipe. Default is true.
     *
     * @param swipeEnable true open, otherwise false.
     */
    public void setSwipeEnable(boolean swipeEnable) {
        this.swipeEnable = swipeEnable;
    }

    /**
     * Open the swipe function of the Item?
     *
     * @return open is true, otherwise is false.
     */
    public boolean isSwipeEnable() {
        return swipeEnable;
    }

    /**
     * Set open percentage.
     *
     * @param openPercent such as 0.5F.
     */
    public void setOpenPercent(float openPercent) {
        this.mOpenPercent = openPercent;
    }

    /**
     * Get open percentage.
     *
     * @return such as 0.5F.
     */
    public float getOpenPercent() {
        return mOpenPercent;
    }

    /**
     * The duration of the set.
     *
     * @param scrollerDuration such as 500.
     */
    public void setScrollerDuration(int scrollerDuration) {
        this.mScrollerDuration = scrollerDuration;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        boolean isIntercepted = super.onInterceptTouchEvent(ev);
        int action = ev.getAction();
        switch (action) {
        case MotionEvent.ACTION_DOWN:
            mDownX = mLastX = (int) ev.getX();
            mDownY = (int) ev.getY();
            isIntercepted = false;
            break;
        case MotionEvent.ACTION_MOVE:
            int disX = (int) (ev.getX() - mDownX);
            int disY = (int) (ev.getY() - mDownY);
            isIntercepted = Math.abs(disX) > mScaledTouchSlop && Math.abs(disX) > Math.abs(disY);
            break;
        case MotionEvent.ACTION_UP:
            isIntercepted = false;
            if (isMenuOpen() && mSwipeCurrentHorizontal.isClickOnContentView(getWidth(), ev.getX())) {
                smoothCloseMenu();
                isIntercepted = true;
            }
            break;
        case MotionEvent.ACTION_CANCEL:
            isIntercepted = false;
            if (!mScroller.isFinished())
                mScroller.abortAnimation();
            break;
        }
        return isIntercepted;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (mVelocityTracker == null)
            mVelocityTracker = VelocityTracker.obtain();
        mVelocityTracker.addMovement(ev);
        int dx;
        int dy;
        int action = ev.getAction();
        switch (action) {
        case MotionEvent.ACTION_DOWN:
            mLastX = (int) ev.getX();
            mLastY = (int) ev.getY();
            break;
        case MotionEvent.ACTION_MOVE:
            if (!isSwipeEnable())
                break;
            int disX = (int) (mLastX - ev.getX());
            int disY = (int) (mLastY - ev.getY());
            if (!mDragging && Math.abs(disX) > mScaledTouchSlop && Math.abs(disX) > Math.abs(disY)) {
                mDragging = true;
            }
            if (mDragging) {
                // x view
                if (mSwipeCurrentHorizontal == null || shouldResetSwipe) {
                    //                        if (disX < 0) {
                    //                            if (mSwipeLeftHorizontal != null)
                    //                                mSwipeCurrentHorizontal = mSwipeLeftHorizontal;
                    //                            else
                    //                                mSwipeCurrentHorizontal = mSwipeRightHorizontal;
                    //                        } else {
                    //                            if (mSwipeRightHorizontal != null)
                    //                                mSwipeCurrentHorizontal = mSwipeRightHorizontal;
                    //                            else
                    //                                mSwipeCurrentHorizontal = mSwipeLeftHorizontal;
                    //                        }
                    mSwipeCurrentHorizontal = mSwipeRightHorizontal;
                }
                scrollBy(disX, 0);
                mLastX = (int) ev.getX();
                mLastY = (int) ev.getY();
                shouldResetSwipe = false;
            }
            break;
        case MotionEvent.ACTION_UP:
            dx = (int) (mDownX - ev.getX());
            dy = (int) (mDownY - ev.getY());
            mDragging = false;
            mVelocityTracker.computeCurrentVelocity(1000, mScaledMaximumFlingVelocity);
            int velocityX = (int) mVelocityTracker.getXVelocity();
            int velocity = Math.abs(velocityX);
            if (velocity > mScaledMinimumFlingVelocity) {
                if (mSwipeCurrentHorizontal != null) {
                    int duration = getSwipeDuration(ev, velocity);
                    if (mSwipeCurrentHorizontal instanceof SwipeRightHorizontal) {
                        if (velocityX < 0) {
                            smoothOpenMenu(duration);
                        } else {
                            smoothCloseMenu(duration);
                        }
                    } else {
                        if (velocityX > 0) {
                            smoothOpenMenu(duration);
                        } else {
                            smoothCloseMenu(duration);
                        }
                    }
                    ViewCompat.postInvalidateOnAnimation(this);
                }
            } else {
                judgeOpenClose(dx, dy);
            }
            mVelocityTracker.clear();
            mVelocityTracker.recycle();
            mVelocityTracker = null;
            if (Math.abs(mDownX - ev.getX()) > mScaledTouchSlop || Math.abs(mDownY - ev.getY()) > mScaledTouchSlop
                    || isMenuOpen()) {
                ev.setAction(MotionEvent.ACTION_CANCEL);
                super.onTouchEvent(ev);
                return true;
            }
            break;
        case MotionEvent.ACTION_CANCEL:
            mDragging = false;
            if (!mScroller.isFinished()) {
                mScroller.abortAnimation();
            } else {
                dx = (int) (mDownX - ev.getX());
                dy = (int) (mDownY - ev.getY());
                judgeOpenClose(dx, dy);
            }
            break;
        }
        return super.onTouchEvent(ev);
    }

    /**
     * compute finish duration.
     *
     * @param ev       up event.
     * @param velocity velocity x.
     * @return finish duration.
     */
    private int getSwipeDuration(MotionEvent ev, int velocity) {
        int sx = getScrollX();
        int dx = (int) (ev.getX() - sx);
        final int width = mSwipeCurrentHorizontal.getMenuWidth();
        final int halfWidth = width / 2;
        final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dx) / width);
        final float distance = halfWidth + halfWidth * distanceInfluenceForSnapDuration(distanceRatio);
        int duration;
        if (velocity > 0) {
            duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
        } else {
            final float pageDelta = (float) Math.abs(dx) / width;
            duration = (int) ((pageDelta + 1) * 100);
        }
        duration = Math.min(duration, mScrollerDuration);
        return duration;
    }

    float distanceInfluenceForSnapDuration(float f) {
        f -= 0.5f; // center the values about 0.
        f *= 0.3f * Math.PI / 2.0f;
        return (float) Math.sin(f);
    }

    private void judgeOpenClose(int dx, int dy) {
        if (mSwipeCurrentHorizontal != null) {
            if (Math.abs(getScrollX()) >= (mSwipeCurrentHorizontal.getMenuView().getWidth() * mOpenPercent)) { // auto open
                if (Math.abs(dx) > mScaledTouchSlop || Math.abs(dy) > mScaledTouchSlop) { // swipe up
                    if (isMenuOpenNotEqual())
                        smoothCloseMenu();
                    else
                        smoothOpenMenu();
                } else { // normal up
                    if (isMenuOpen())
                        smoothCloseMenu();
                    else
                        smoothOpenMenu();
                }
            } else { // auto close
                smoothCloseMenu();
            }
        }
    }

    @Override
    public void scrollTo(int x, int y) {
        if (mSwipeCurrentHorizontal == null) {
            super.scrollTo(x, y);
        } else {
            SwipeHorizontal.Checker checker = mSwipeCurrentHorizontal.checkXY(x, y);
            shouldResetSwipe = checker.shouldResetSwipe;
            if (checker.x != getScrollX()) {
                super.scrollTo(checker.x, checker.y);
            }
        }
    }

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset() && mSwipeCurrentHorizontal != null) {
            if (mSwipeCurrentHorizontal instanceof SwipeRightHorizontal) {
                scrollTo(Math.abs(mScroller.getCurrX()), 0);
                invalidate();
            } else {
                scrollTo(-Math.abs(mScroller.getCurrX()), 0);
                invalidate();
            }
        }
    }

    @Override
    public boolean isMenuOpen() {
        return isRightMenuOpen();
    }

    @Override
    public boolean isRightMenuOpen() {
        return mSwipeRightHorizontal != null && mSwipeRightHorizontal.isMenuOpen(getScrollX());
    }

    @Override
    public boolean isMenuOpenNotEqual() {
        return isRightMenuOpenNotEqual();
    }

    @Override
    public boolean isRightMenuOpenNotEqual() {
        return mSwipeRightHorizontal != null && mSwipeRightHorizontal.isMenuOpenNotEqual(getScrollX());
    }

    @Override
    public void smoothOpenRightMenu() {
        smoothOpenRightMenu(mScrollerDuration);
    }

    @Override
    public void smoothOpenRightMenu(int duration) {
        if (mSwipeRightHorizontal != null) {
            mSwipeCurrentHorizontal = mSwipeRightHorizontal;
            smoothOpenMenu(duration);
        }
    }

    @Override
    public void smoothOpenMenu() {
        smoothOpenMenu(mScrollerDuration);
    }

    private void smoothOpenMenu(int duration) {
        if (mSwipeCurrentHorizontal != null) {
            mSwipeCurrentHorizontal.autoOpenMenu(mScroller, getScrollX(), duration);
            invalidate();
        }
    }

    @Override
    public void smoothCloseRightMenu() {
        if (mSwipeRightHorizontal != null) {
            mSwipeCurrentHorizontal = mSwipeRightHorizontal;
            smoothCloseMenu();
        }
    }

    @Override
    public void smoothCloseMenu(int duration) {
        if (mSwipeCurrentHorizontal != null) {
            mSwipeCurrentHorizontal.autoCloseMenu(mScroller, getScrollX(), duration);
            invalidate();
        }
    }

    @Override
    public void smoothCloseMenu() {
        smoothCloseMenu(mScrollerDuration);
    }

    private final ArrayList<View> mMatchParentChildren = new ArrayList<>(1);

    boolean mMeasureAllChildren = false;

    //    @Override
    //    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    //        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    //        int count = getChildCount();
    //
    //        final boolean measureMatchParentChildren = MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY || MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
    //        mMatchParentChildren.clear();
    //
    //        int maxHeight = 0;
    //        int maxWidth = 0;
    //        int childState = 0;
    //
    //        for (int i = 0; i < count; i++) {
    //            final View child = getChildAt(i);
    //            if (mMeasureAllChildren || child.getVisibility() != GONE) {
    //                measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
    //                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
    //                maxWidth = Math.max(maxWidth, child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
    //                maxHeight = Math.max(maxHeight, child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
    //                childState = combineMeasuredStates(childState, child.getMeasuredState());
    //                if (measureMatchParentChildren) {
    //                    if (lp.width == LayoutParams.MATCH_PARENT || lp.height == LayoutParams.MATCH_PARENT) {
    //                        mMatchParentChildren.add(child);
    //                    }
    //                }
    //            }
    //        }
    //
    //        // Check against our minimum height and width
    //        maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
    //        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
    //
    //        // Check against our foreground's minimum height and width
    //        final Drawable drawable = getForeground();
    //        if (drawable != null) {
    //            maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
    //            maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
    //        }
    //
    //        setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), resolveSizeAndState(maxHeight, heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT));
    //
    //        count = mMatchParentChildren.size();
    //        if (count > 1) {
    //            for (int i = 0; i < count; i++) {
    //                final View child = mMatchParentChildren.get(i);
    //                final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
    //
    //                final int childWidthMeasureSpec;
    //                if (lp.width == LayoutParams.MATCH_PARENT) {
    //                    final int width = Math.max(0, getMeasuredWidth() - lp.leftMargin - lp.rightMargin);
    //                    childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
    //                } else {
    //                    childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, lp.leftMargin + lp.rightMargin, lp.width);
    //                }
    //
    //                final int childHeightMeasureSpec;
    //                if (lp.height == LayoutParams.MATCH_PARENT) {
    //                    final int height = Math.max(0, getMeasuredHeight() - lp.topMargin - lp.bottomMargin);
    //                    childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
    //                } else {
    //                    childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, lp.topMargin + lp.bottomMargin, lp.height);
    //                }
    //                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    //            }
    //        }
    //    }

    //    @Override
    //    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    //        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    //        int contentViewHeight = 0;
    //        if (mContentView != null) {
    //            contentViewHeight = mContentView.getMeasuredHeightAndState();
    //        }
    //
    //        if (mSwipeLeftHorizontal != null) {
    //            View leftMenu = mSwipeLeftHorizontal.getMenuView();
    //            int menuViewHeight = contentViewHeight == 0 ? leftMenu.getMeasuredHeightAndState() : contentViewHeight;
    //        }
    //
    //        if (mSwipeRightHorizontal != null) {
    //            View rightMenu = mSwipeRightHorizontal.getMenuView();
    //            int menuViewHeight = contentViewHeight == 0 ? rightMenu.getMeasuredHeightAndState() : contentViewHeight;
    //        }
    //    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int contentViewHeight = 0;
        if (mContentView != null) {
            int contentViewWidth = mContentView.getMeasuredWidthAndState();
            contentViewHeight = mContentView.getMeasuredHeightAndState();
            LayoutParams lp = (LayoutParams) mContentView.getLayoutParams();
            int start = getPaddingLeft();
            int top = getPaddingTop() + lp.topMargin;
            mContentView.layout(start, top, start + contentViewWidth, top + contentViewHeight);
        }

        if (mSwipeRightHorizontal != null) {
            View rightMenu = mSwipeRightHorizontal.getMenuView();
            int menuViewWidth = rightMenu.getMeasuredWidthAndState();
            //            int menuViewHeight = contentViewHeight == 0 ? rightMenu.getMeasuredHeightAndState() : contentViewHeight;
            int menuViewHeight = rightMenu.getMeasuredHeightAndState();
            LayoutParams lp = (LayoutParams) rightMenu.getLayoutParams();
            int top = getPaddingTop() + lp.topMargin;

            int parentViewWidth = getMeasuredWidthAndState();
            rightMenu.layout(parentViewWidth, top, parentViewWidth + menuViewWidth, top + menuViewHeight);
        }
    }

}