RecyclerView Widget 使用

栏目: Android · 发布时间: 6年前

内容简介:如果数据不确定的情况下需要 自行组装 服务端返回的接口数据 不同类型的数据需要设置不同的 ItemViewType利用多Item 类型 将数据泛型多样化在 getItemCount() 方法返回 多于展示列表的 position 0 header itemDatas.size()+1 footer
  • 加载列表
  • 多类型 Item 适配加载列表
  • header footer 适配加载列表
  • 上拉加载更多 下拉刷新
  • 分组 列表
  • 分组 悬浮列表
  • 拖拽排序
  • 侧滑 展开Menu 菜单

加载列表

/**
 * RecyclerView.Adapter<T> 封装
 * 实现 基本的初始化 方法 增 删 数据
 */
public abstract class BaseAdapter<D extends Object, VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> {
        private Context mContext;
        private ArrayList<D> mds;
    
        public ArrayList<D> getMds() {
            return mds;
        }
    
        public Context getmContext() {
            return mContext;
        }
    
        public BaseAdapter(Context context) {
            mContext = context;
            this.mds = new ArrayList<>();
        }
    
        public BaseAdapter(Context context, ArrayList<D> mds) {
            mContext = context;
            this.mds = mds;
        }
    
        @Override
        @NonNull
        public VH onCreateViewHolder(ViewGroup parent, int viewType) {
            return onCreateChildViewHolder(parent, viewType);
        }
    
        protected abstract VH onCreateChildViewHolder(ViewGroup parent, int viewType);
    
        public void initMDatas(ArrayList<D> ds) {
            if (ds == null) return;
            if (ds.size() == 0) return;
            mds.clear();
            mds.addAll(ds);
            notifyDataSetChanged();
        }
    
        public void initMDatasToOne(ArrayList<D> ds) {
            if (ds == null) return;
            if (ds.size() == 0) return;
            mds.clear();
            mds.addAll(0, ds);
            notifyDataSetChanged();
        }
    
        public void initMData(D d) {
            if (d == null) return;
            mds.add(d);
            notifyDataSetChanged();
        }
    
        public void initMDataToOne(D d) {
            if (d == null) return;
            mds.add(0, d);
            notifyDataSetChanged();
        }
    
        public void addDataModel(ArrayList<D> ds) {
            if (ds == null || ds.size() == 0) return;
            mds.addAll(ds);
            notifyDataSetChanged();
        }
    
        public void addStartDataModel(ArrayList<D> ds) {
            if (ds == null || ds.size() == 0) return;
            mds.addAll(0, ds);
            notifyDataSetChanged();
        }
    
        public void addDataModel(D d) {
            if (d == null) return;
            mds.add(d);
            notifyDataSetChanged();
        }
    
        public void deleteMDatas() {
            mds.clear();
            notifyDataSetChanged();
        }
    
        @Override
        public int getItemCount() {
            return mds != null ? mds.size() : 0;
        }
    
        @Override
        public void onBindViewHolder(@NonNull VH holder, int position) {
            D d = mds.get(position);
            onBindDataViewHolder(holder, position, d);
        }
    
        protected abstract void onBindDataViewHolder(VH holder, int position, D d);
    }
    
    //初始化RecyclerView 
    //设置LayouerManager
    //绑定Adapter
    mRecyclerView.setHasFixedSize(true);
    mRecyclerView.setLayoutManager(new GridLayoutManager(this, 4));
    groupingAdapter = new GroupingAdapter(this);
    mRecyclerView.setAdapter(groupingAdapter);
复制代码

# 多类型 Item 适配加载列表

RecyclerView Widget 使用

如果数据不确定的情况下需要 自行组装 服务端返回的接口数据 不同类型的数据需要设置不同的 ItemViewType

/**
     * 设置Item 类型多样化条目
     */
    @Override
    public int getItemViewType(int position) {
        ArrayList<RecycleViewItemData> mds = getMds();
        if (mds != null && mds.size() > 0) {
            return mds.get(position).getItemType();
        } else
            return super.getItemViewType(position);
    }
    
    /**
     * 根据viewType
     * 设置 ViewHolder 多样化条目
     */
   @Override
    protected RecyclerView.ViewHolder onCreateChildViewHolder(ViewGroup parent, int viewType) {
        if (viewType == RecycleViewItemData.TYPE_ZERO)
            return new IndicatorViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.indicator_item, parent, false));
        if (viewType == RecycleViewItemData.TYPE_ONE)
            return new GroupingViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.grouping_item, parent, false));
        return null;
    }
    
    /**
     * 如果layoutManager 是GridLayoutManager 需要实现
     * setSpanSizeLookup() 接口 返回 spanSize
     */
    @Override
    public void onAttachedToRecyclerView(@NonNull final RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
        RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager) {
            final GridLayoutManager gridManager = (GridLayoutManager) layoutManager;
            gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
                @Override
                public int getSpanSize(int position) {
                    if (getItemViewType(position) == RecycleViewItemData.TYPE_ONE)
                        return 1;
                    else
                        return 4;
                }
            });
        }
    }
复制代码

# header fooder 适配加载列表

# 方法1:

利用多Item 类型 将数据泛型多样化

# 方法2:

在 getItemCount() 方法返回 多于展示列表的 position 0 header itemDatas.size()+1 footer

@Override
    public int getItemCount() {
        return mds != null ? mds.size() : 0;
    }
    
    @Override
    public int getItemCount() {
        return mds != null ? mds.size()+1 : 0; // header 
    }
    
    @Override
    public int getItemCount() {
        return mds != null ? mds.size()+2 : 0; // header footer
    }
复制代码

# 上拉加载更多 下拉刷新

待续。。。

# 分组折叠 列表

待续。。。

# 分组 悬浮列表

待续。。。

# 拖拽排序

待续。。。

# 侧滑 展开Menu 菜单

RecyclerView Widget 使用
  • 实现RecyclerView OnItemTouchListener 接口 获取 Item 的 Touch 事件
/**
     * An OnItemTouchListener allows the application to intercept touch events in progress at the
     * view hierarchy level of the RecyclerView before those touch events are considered for
     * RecyclerView's own scrolling behavior.
     *
     * <p>This can be useful for applications that wish to implement various forms of gestural
     * manipulation of item views within the RecyclerView. OnItemTouchListeners may intercept
     * a touch interaction already in progress even if the RecyclerView is already handling that
     * gesture stream itself for the purposes of scrolling.</p>
     *
     * @see SimpleOnItemTouchListener
     */
    public interface OnItemTouchListener {
        /**
         * Silently observe and/or take over touch events sent to the RecyclerView
         * before they are handled by either the RecyclerView itself or its child views.
         *
         * <p>The onInterceptTouchEvent methods of each attached OnItemTouchListener will be run
         * in the order in which each listener was added, before any other touch processing
         * by the RecyclerView itself or child views occurs.</p>
         *
         * @param e MotionEvent describing the touch event. All coordinates are in
         *          the RecyclerView's coordinate system.
         * @return true if this OnItemTouchListener wishes to begin intercepting touch events, false
         *         to continue with the current behavior and continue observing future events in
         *         the gesture.
         */
        boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e);

        /**
         * Process a touch event as part of a gesture that was claimed by returning true from
         * a previous call to {@link #onInterceptTouchEvent}.
         *
         * @param e MotionEvent describing the touch event. All coordinates are in
         *          the RecyclerView's coordinate system.
         */
        void onTouchEvent(RecyclerView rv, MotionEvent e);

        /**
         * Called when a child of RecyclerView does not want RecyclerView and its ancestors to
         * intercept touch events with
         * {@link ViewGroup#onInterceptTouchEvent(MotionEvent)}.
         *
         * @param disallowIntercept True if the child does not want the parent to
         *            intercept touch events.
         * @see ViewParent#requestDisallowInterceptTouchEvent(boolean)
         */
        void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);
    }
复制代码
  • 实现接口
public static class OnSwipeItemTouchListener implements RecyclerView.OnItemTouchListener {
        private SwipeItemLayout mCaptureItem;
        private float mLastMotionX;
        private float mLastMotionY;
        private VelocityTracker mVelocityTracker;

        private int mActivePointerId;

        private int mTouchSlop;
        private int mMaximumVelocity;

        private boolean mDealByParent;
        private boolean mIsProbeParent;

        public OnSwipeItemTouchListener(Context context) {
            ViewConfiguration configuration = ViewConfiguration.get(context);
            mTouchSlop = configuration.getScaledTouchSlop();
            mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
            mActivePointerId = -1;
            mDealByParent = false;
            mIsProbeParent = false;
        }

        @Override
        public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent ev) {

            if (mIsProbeParent)
                return false;

            boolean intercept = false;
            final int action = ev.getActionMasked();

            if (mVelocityTracker == null) {
                mVelocityTracker = VelocityTracker.obtain();
            }
            mVelocityTracker.addMovement(ev);

            switch (action) {
                case MotionEvent.ACTION_DOWN: {
                    mActivePointerId = ev.getPointerId(0);
                    final float x = ev.getX();
                    final float y = ev.getY();
                    mLastMotionX = x;
                    mLastMotionY = y;

                    boolean pointOther = false;
                    SwipeItemLayout pointItem = null;
                    //首先知道ev针对的是哪个item
                    View pointView = findTopChildUnder(rv, (int) x, (int) y);
                    if (pointView == null || !(pointView instanceof SwipeItemLayout)) {
                        //可能是head view或bottom view
                        pointOther = true;
                    } else
                        pointItem = (SwipeItemLayout) pointView;

                    //此时的pointOther=true,意味着点击的view为空或者点击的不是item
                    //还没有把点击的是item但是不是capture item给过滤出来
                    if (!pointOther && (mCaptureItem == null || mCaptureItem != pointItem))
                        pointOther = true;

                    //点击的是capture item
                    if (!pointOther) {
                        Mode touchMode = mCaptureItem.getTouchMode();

                        //如果它在fling,就转为drag
                        //需要拦截,并且requestDisallowInterceptTouchEvent
                        boolean disallowIntercept = false;
                        if (touchMode == Mode.FLING) {
                            mCaptureItem.setTouchMode(Mode.DRAG);
                            disallowIntercept = true;
                            intercept = true;
                        } else {//如果是expand的,就不允许parent拦截
                            mCaptureItem.setTouchMode(Mode.TAP);
                            if (mCaptureItem.isOpen())
                                disallowIntercept = true;
                        }

                        if (disallowIntercept) {
                            final ViewParent parent = rv.getParent();
                            if (parent != null)
                                parent.requestDisallowInterceptTouchEvent(true);
                        }
                    } else {//capture item为null或者与point item不一样
                        //直接将其close掉
                        if (mCaptureItem != null && mCaptureItem.isOpen()) {
                            mCaptureItem.close();
                            mCaptureItem = null;
                            intercept = true;
                        }

                        if (pointItem != null) {
                            mCaptureItem = pointItem;
                            mCaptureItem.setTouchMode(Mode.TAP);
                        } else
                            mCaptureItem = null;
                    }

                    //如果parent处于fling状态,此时,parent就会转为drag。此时,应该将后续move都交给parent处理
                    mIsProbeParent = true;
                    mDealByParent = rv.onInterceptTouchEvent(ev);
                    mIsProbeParent = false;
                    if (mDealByParent)
                        intercept = false;
                    break;
                }

                case MotionEvent.ACTION_POINTER_DOWN: {
                    final int actionIndex = ev.getActionIndex();
                    mActivePointerId = ev.getPointerId(actionIndex);

                    mLastMotionX = ev.getX(actionIndex);
                    mLastMotionY = ev.getY(actionIndex);
                    break;
                }

                case MotionEvent.ACTION_POINTER_UP: {
                    final int actionIndex = ev.getActionIndex();
                    final int pointerId = ev.getPointerId(actionIndex);
                    if (pointerId == mActivePointerId) {
                        final int newIndex = actionIndex == 0 ? 1 : 0;
                        mActivePointerId = ev.getPointerId(newIndex);

                        mLastMotionX = ev.getX(newIndex);
                        mLastMotionY = ev.getY(newIndex);
                    }
                    break;
                }

                //down时,已经将capture item定下来了。所以,后面可以安心考虑event处理
                case MotionEvent.ACTION_MOVE: {
                    final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
                    if (activePointerIndex == -1)
                        break;

                    //在down时,就被认定为parent的drag,所以,直接交给parent处理即可
                    if (mDealByParent) {
                        if (mCaptureItem != null && mCaptureItem.isOpen())
                            mCaptureItem.close();
                        return false;
                    }

                    final int x = (int) (ev.getX(activePointerIndex) + .5f);
                    final int y = (int) ((int) ev.getY(activePointerIndex) + .5f);

                    int deltaX = (int) (x - mLastMotionX);
                    int deltaY = (int) (y - mLastMotionY);
                    final int xDiff = Math.abs(deltaX);
                    final int yDiff = Math.abs(deltaY);

                    if (mCaptureItem != null && !mDealByParent) {
                        Mode touchMode = mCaptureItem.getTouchMode();

                        if (touchMode == Mode.TAP) {
                            //如果capture item是open的,下拉有两种处理方式:
                            //  1、下拉后,直接close item
                            //  2、只要是open的,就拦截所有它的消息,这样如果点击open的,就只能滑动该capture item
                            //网易邮箱,在open的情况下,下拉直接close
                            //QQ,在open的情况下,下拉也是close。但是,做的不够好,没有达到该效果。
                            if (xDiff > mTouchSlop && xDiff > yDiff) {
                                mCaptureItem.setTouchMode(Mode.DRAG);
                                final ViewParent parent = rv.getParent();
                                parent.requestDisallowInterceptTouchEvent(true);

                                deltaX = deltaX > 0 ? deltaX - mTouchSlop : deltaX + mTouchSlop;
                            } else {// if(yDiff>mTouchSlop){
                                mIsProbeParent = true;
                                boolean isParentConsume = rv.onInterceptTouchEvent(ev);
                                mIsProbeParent = false;
                                if (isParentConsume) {
                                    //表明不是水平滑动,即不判定为SwipeItemLayout的滑动
                                    //但是,可能是下拉刷新SwipeRefreshLayout或者RecyclerView的滑动
                                    //一般的下拉判定,都是yDiff>mTouchSlop,所以,此处这么写不会出问题
                                    //这里这么做以后,如果判定为下拉,就直接close
                                    mDealByParent = true;
                                    mCaptureItem.close();
                                }
                            }
                        }

                        touchMode = mCaptureItem.getTouchMode();
                        if (touchMode == Mode.DRAG) {
                            intercept = true;
                            mLastMotionX = x;
                            mLastMotionY = y;

                            //对capture item进行拖拽
                            mCaptureItem.trackMotionScroll(deltaX);
                        }
                    }
                    break;
                }

                case MotionEvent.ACTION_UP:
                    if (mCaptureItem != null) {
                        Mode touchMode = mCaptureItem.getTouchMode();
                        if (touchMode == Mode.DRAG) {
                            final VelocityTracker velocityTracker = mVelocityTracker;
                            velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                            int xVel = (int) velocityTracker.getXVelocity(mActivePointerId);
                            mCaptureItem.fling(xVel);

                            intercept = true;
                        }
                    }
                    cancel();
                    break;

                case MotionEvent.ACTION_CANCEL:
                    if (mCaptureItem != null)
                        mCaptureItem.revise();
                    cancel();
                    break;
            }

            return intercept;
        }

        @Override
        public void onTouchEvent(RecyclerView rv, MotionEvent ev) {
            final int action = ev.getActionMasked();
            final int actionIndex = ev.getActionIndex();

            if (mVelocityTracker == null) {
                mVelocityTracker = VelocityTracker.obtain();
            }
            mVelocityTracker.addMovement(ev);

            switch (action) {
                case MotionEvent.ACTION_POINTER_DOWN:
                    mActivePointerId = ev.getPointerId(actionIndex);

                    mLastMotionX = ev.getX(actionIndex);
                    mLastMotionY = ev.getY(actionIndex);
                    break;

                case MotionEvent.ACTION_POINTER_UP:
                    final int pointerId = ev.getPointerId(actionIndex);
                    if (pointerId == mActivePointerId) {
                        final int newIndex = actionIndex == 0 ? 1 : 0;
                        mActivePointerId = ev.getPointerId(newIndex);

                        mLastMotionX = ev.getX(newIndex);
                        mLastMotionY = ev.getY(newIndex);
                    }
                    break;

                //down时,已经将capture item定下来了。所以,后面可以安心考虑event处理
                case MotionEvent.ACTION_MOVE: {
                    final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
                    if (activePointerIndex == -1)
                        break;

                    final float x = ev.getX(activePointerIndex);
                    final float y = (int) ev.getY(activePointerIndex);

                    int deltaX = (int) (x - mLastMotionX);

                    if (mCaptureItem != null && mCaptureItem.getTouchMode() == Mode.DRAG) {
                        mLastMotionX = x;
                        mLastMotionY = y;

                        //对capture item进行拖拽
                        mCaptureItem.trackMotionScroll(deltaX);
                    }
                    break;
                }

                case MotionEvent.ACTION_UP:
                    if (mCaptureItem != null) {
                        Mode touchMode = mCaptureItem.getTouchMode();
                        if (touchMode == Mode.DRAG) {
                            final VelocityTracker velocityTracker = mVelocityTracker;
                            velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                            int xVel = (int) velocityTracker.getXVelocity(mActivePointerId);
                            mCaptureItem.fling(xVel);
                        }
                    }
                    cancel();
                    break;

                case MotionEvent.ACTION_CANCEL:
                    if (mCaptureItem != null)
                        mCaptureItem.revise();

                    cancel();
                    break;

            }
        }

        @Override
        public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
        }

        void cancel() {
            mDealByParent = false;
            mActivePointerId = -1;
            if (mVelocityTracker != null) {
                mVelocityTracker.recycle();
                mVelocityTracker = null;
            }
        }

    }
复制代码
  • SwipeItemLayout author:liyi
/**
 * Author: liyi
 * Date:2017/2/16.
 * description: 侧滑 菜单
 */
public class SwipeItemLayout extends ViewGroup {

    enum Mode {
        RESET, DRAG, FLING, TAP
    }

    private Mode mTouchMode;
    private ViewGroup mMainView;
    private ViewGroup mSideView;
    private ScrollRunnable mScrollRunnable;
    private int mScrollOffset;
    private int mMaxScrollOffset;
    private boolean mInLayout;
    private boolean mIsLaidOut;

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

    public SwipeItemLayout(Context context, AttributeSet attrs) {
        super(context, attrs);

        mTouchMode = Mode.RESET;
        mScrollOffset = 0;
        mIsLaidOut = false;
        mScrollRunnable = new ScrollRunnable(context);
    }

    public boolean isOpen() {
        return mScrollOffset != 0;
    }

    Mode getTouchMode() {
        return mTouchMode;
    }

    void setTouchMode(Mode mode) {
        switch (mTouchMode) {
            case FLING:
                mScrollRunnable.abort();
                break;
            case RESET:
                break;
        }

        mTouchMode = mode;
    }

    public void open() {
        if (mScrollOffset != -mMaxScrollOffset) {
            //正在open,不需要处理
            if (mTouchMode == Mode.FLING && mScrollRunnable.isScrollToLeft())
                return;

            //当前正在向右滑,abort
            if (mTouchMode == Mode.FLING /*&& !mScrollRunnable.mScrollToLeft*/)
                mScrollRunnable.abort();

            mScrollRunnable.startScroll(mScrollOffset, -mMaxScrollOffset);
        }
    }

    public void close() {
        if (mScrollOffset != 0) {
            //正在close,不需要处理
            if (mTouchMode == Mode.FLING && !mScrollRunnable.isScrollToLeft())
                return;

            //当前正向左滑,abort
            if (mTouchMode == Mode.FLING /*&& mScrollRunnable.mScrollToLeft*/)
                mScrollRunnable.abort();

            mScrollRunnable.startScroll(mScrollOffset, 0);
        }
    }

    void fling(int xVel) {
        mScrollRunnable.startFling(mScrollOffset, xVel);
    }

    void revise() {
        if (mScrollOffset < -mMaxScrollOffset / 2)
            open();
        else
            close();
    }

    boolean trackMotionScroll(int deltaX) {
        if (deltaX == 0)
            return false;

        boolean over = false;
        int newLeft = mScrollOffset + deltaX;
        if ((deltaX > 0 && newLeft > 0) || (deltaX < 0 && newLeft < -mMaxScrollOffset)) {
            over = true;
            newLeft = Math.min(newLeft, 0);
            newLeft = Math.max(newLeft, -mMaxScrollOffset);
        }

        offsetChildrenLeftAndRight(newLeft - mScrollOffset);
        mScrollOffset = newLeft;
        return over;
    }

    private boolean ensureChildren() {
        int childCount = getChildCount();

        if (childCount != 2)
            return false;

        View childView = getChildAt(0);
        if (!(childView instanceof ViewGroup))
            return false;
        mMainView = (ViewGroup) childView;

        childView = getChildAt(1);
        if (!(childView instanceof ViewGroup))
            return false;
        mSideView = (ViewGroup) childView;
        return true;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (!ensureChildren())
            throw new RuntimeException("SwipeItemLayout的子视图不符合规定");

        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        MarginLayoutParams lp = null;
        int horizontalMargin, verticalMargin;
        int horizontalPadding = getPaddingLeft() + getPaddingRight();
        int verticalPadding = getPaddingTop() + getPaddingBottom();

        lp = (MarginLayoutParams) mMainView.getLayoutParams();
        horizontalMargin = lp.leftMargin + lp.rightMargin;
        verticalMargin = lp.topMargin + lp.bottomMargin;
        measureChildWithMargins(mMainView,
                widthMeasureSpec, horizontalMargin + horizontalPadding,
                heightMeasureSpec, verticalMargin + verticalPadding);

        if (widthMode == MeasureSpec.AT_MOST)
            widthSize = Math.min(widthSize, mMainView.getMeasuredWidth() + horizontalMargin + horizontalPadding);
        else if (widthMode == MeasureSpec.UNSPECIFIED)
            widthSize = mMainView.getMeasuredWidth() + horizontalMargin + horizontalPadding;

        if (heightMode == MeasureSpec.AT_MOST)
            heightSize = Math.min(heightSize, mMainView.getMeasuredHeight() + verticalMargin + verticalPadding);
        else if (heightMode == MeasureSpec.UNSPECIFIED)
            heightSize = mMainView.getMeasuredHeight() + verticalMargin + verticalPadding;

        setMeasuredDimension(widthSize, heightSize);

        //side layout大小为自身实际大小
        lp = (MarginLayoutParams) mSideView.getLayoutParams();
        verticalMargin = lp.topMargin + lp.bottomMargin;
        mSideView.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
                MeasureSpec.makeMeasureSpec(getMeasuredHeight() - verticalMargin - verticalPadding, MeasureSpec.EXACTLY));
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (!ensureChildren())
            throw new RuntimeException("SwipeItemLayout的子视图不符合规定");

        mInLayout = true;

        int pl = getPaddingLeft();
        int pt = getPaddingTop();
        int pr = getPaddingRight();
        int pb = getPaddingBottom();

        MarginLayoutParams mainLp = (MarginLayoutParams) mMainView.getLayoutParams();
        MarginLayoutParams sideParams = (MarginLayoutParams) mSideView.getLayoutParams();

        int childLeft = pl + mainLp.leftMargin;
        int childTop = pt + mainLp.topMargin;
        int childRight = getWidth() - (pr + mainLp.rightMargin);
        int childBottom = getHeight() - (mainLp.bottomMargin + pb);
        mMainView.layout(childLeft, childTop, childRight, childBottom);

        childLeft = childRight + sideParams.leftMargin;
        childTop = pt + sideParams.topMargin;
        childRight = childLeft + sideParams.leftMargin + sideParams.rightMargin + mSideView.getMeasuredWidth();
        childBottom = getHeight() - (sideParams.bottomMargin + pb);
        mSideView.layout(childLeft, childTop, childRight, childBottom);

        mMaxScrollOffset = mSideView.getWidth() + sideParams.leftMargin + sideParams.rightMargin;
        mScrollOffset = mScrollOffset < -mMaxScrollOffset / 2 ? -mMaxScrollOffset : 0;

        offsetChildrenLeftAndRight(mScrollOffset);
        mInLayout = false;
        mIsLaidOut = true;
    }

    void offsetChildrenLeftAndRight(int delta) {
        ViewCompat.offsetLeftAndRight(mMainView, delta);
        ViewCompat.offsetLeftAndRight(mSideView, delta);
    }

    @Override
    public void requestLayout() {
        if (!mInLayout) {
            super.requestLayout();
        }
    }

    @Override
    protected LayoutParams generateDefaultLayoutParams() {
        return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
    }

    @Override
    protected LayoutParams generateLayoutParams(LayoutParams p) {
        return p instanceof MarginLayoutParams ? p : new MarginLayoutParams(p);
    }

    @Override
    protected boolean checkLayoutParams(LayoutParams p) {
        return p instanceof MarginLayoutParams && super.checkLayoutParams(p);
    }

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(), attrs);
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();

        if (mScrollOffset != 0 && mIsLaidOut) {
            offsetChildrenLeftAndRight(-mScrollOffset);
            mScrollOffset = 0;
        } else
            mScrollOffset = 0;
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();

        if (mScrollOffset != 0 && mIsLaidOut) {
            offsetChildrenLeftAndRight(-mScrollOffset);
            mScrollOffset = 0;
        } else
            mScrollOffset = 0;
        removeCallbacks(mScrollRunnable);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        final int action = ev.getActionMasked();
        //click main view,但是它处于open状态,所以,不需要点击效果,直接拦截不调用click listener
        switch (action) {
            case MotionEvent.ACTION_DOWN: {
                final int x = (int) ev.getX();
                final int y = (int) ev.getY();
                View pointView = findTopChildUnder(this, x, y);
                if (pointView != null && pointView == mMainView && mScrollOffset != 0)
                    return true;
                break;
            }

            case MotionEvent.ACTION_MOVE:
            case MotionEvent.ACTION_CANCEL:
                break;

            case MotionEvent.ACTION_UP: {
                final int x = (int) ev.getX();
                final int y = (int) ev.getY();
                View pointView = findTopChildUnder(this, x, y);
                if (pointView != null && pointView == mMainView && mTouchMode == Mode.TAP && mScrollOffset != 0)
                    return true;
            }
        }

        return false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        final int action = ev.getActionMasked();
        //click main view,但是它处于open状态,所以,不需要点击效果,直接拦截不调用click listener
        switch (action) {
            case MotionEvent.ACTION_DOWN: {
                final int x = (int) ev.getX();
                final int y = (int) ev.getY();
                View pointView = findTopChildUnder(this, x, y);
                if (pointView != null && pointView == mMainView && mScrollOffset != 0)
                    return true;
                break;
            }

            case MotionEvent.ACTION_MOVE:
            case MotionEvent.ACTION_CANCEL:
                break;

            case MotionEvent.ACTION_UP: {
                final int x = (int) ev.getX();
                final int y = (int) ev.getY();
                View pointView = findTopChildUnder(this, x, y);
                if (pointView != null && pointView == mMainView && mTouchMode == Mode.TAP && mScrollOffset != 0) {
                    close();
                    return true;
                }
            }
        }

        return false;
    }

    @Override
    protected void onVisibilityChanged(View changedView, int visibility) {
        super.onVisibilityChanged(changedView, visibility);
        if (getVisibility() != View.VISIBLE) {
            mScrollOffset = 0;
            invalidate();
        }
    }

    private static final Interpolator sInterpolator = new Interpolator() {
        @Override
        public float getInterpolation(float t) {
            t -= 1.0f;
            return t * t * t * t * t + 1.0f;
        }
    };

    class ScrollRunnable implements Runnable {
        private static final int FLING_DURATION = 200;
        private Scroller mScroller;
        private boolean mAbort;
        private int mMinVelocity;
        private boolean mScrollToLeft;

        ScrollRunnable(Context context) {
            mScroller = new Scroller(context, sInterpolator);
            mAbort = false;
            mScrollToLeft = false;

            ViewConfiguration configuration = ViewConfiguration.get(context);
            mMinVelocity = configuration.getScaledMinimumFlingVelocity();
        }

        void startScroll(int startX, int endX) {
            if (startX != endX) {
                Log.e("scroll - startX - endX", "" + startX + " " + endX);
                setTouchMode(Mode.FLING);
                mAbort = false;
                mScrollToLeft = endX < startX;
                mScroller.startScroll(startX, 0, endX - startX, 0, 400);
                ViewCompat.postOnAnimation(SwipeItemLayout.this, this);
            }
        }

        void startFling(int startX, int xVel) {
            Log.e("fling - startX", "" + startX);

            if (xVel > mMinVelocity && startX != 0) {
                startScroll(startX, 0);
                return;
            }

            if (xVel < -mMinVelocity && startX != -mMaxScrollOffset) {
                startScroll(startX, -mMaxScrollOffset);
                return;
            }

            startScroll(startX, startX > -mMaxScrollOffset / 2 ? 0 : -mMaxScrollOffset);
        }

        void abort() {
            if (!mAbort) {
                mAbort = true;
                if (!mScroller.isFinished()) {
                    mScroller.abortAnimation();
                    removeCallbacks(this);
                }
            }
        }

        //是否正在滑动需要另外判断
        boolean isScrollToLeft() {
            return mScrollToLeft;
        }

        @Override
        public void run() {
            Log.e("abort", Boolean.toString(mAbort));
            if (!mAbort) {
                boolean more = mScroller.computeScrollOffset();
                int curX = mScroller.getCurrX();
                Log.e("curX", "" + curX);

                boolean atEdge = trackMotionScroll(curX - mScrollOffset);
                if (more && !atEdge) {
                    ViewCompat.postOnAnimation(SwipeItemLayout.this, this);
                    return;
                }

                if (atEdge) {
                    removeCallbacks(this);
                    if (!mScroller.isFinished())
                        mScroller.abortAnimation();
                    setTouchMode(Mode.RESET);
                }

                if (!more) {
                    setTouchMode(Mode.RESET);
                    //绝对不会出现这种意外的!!!可以注释掉
                    if (mScrollOffset != 0) {
                        if (Math.abs(mScrollOffset) > mMaxScrollOffset / 2)
                            mScrollOffset = -mMaxScrollOffset;
                        else
                            mScrollOffset = 0;
                        ViewCompat.postOnAnimation(SwipeItemLayout.this, this);
                    }
                }
            }
        }
    }

    public static class OnSwipeItemTouchListener implements RecyclerView.OnItemTouchListener {
        private SwipeItemLayout mCaptureItem;
        private float mLastMotionX;
        private float mLastMotionY;
        private VelocityTracker mVelocityTracker;

        private int mActivePointerId;

        private int mTouchSlop;
        private int mMaximumVelocity;

        private boolean mDealByParent;
        private boolean mIsProbeParent;

        public OnSwipeItemTouchListener(Context context) {
            ViewConfiguration configuration = ViewConfiguration.get(context);
            mTouchSlop = configuration.getScaledTouchSlop();
            mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
            mActivePointerId = -1;
            mDealByParent = false;
            mIsProbeParent = false;
        }

        @Override
        public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent ev) {

            if (mIsProbeParent)
                return false;

            boolean intercept = false;
            final int action = ev.getActionMasked();

            if (mVelocityTracker == null) {
                mVelocityTracker = VelocityTracker.obtain();
            }
            mVelocityTracker.addMovement(ev);

            switch (action) {
                case MotionEvent.ACTION_DOWN: {
                    mActivePointerId = ev.getPointerId(0);
                    final float x = ev.getX();
                    final float y = ev.getY();
                    mLastMotionX = x;
                    mLastMotionY = y;

                    boolean pointOther = false;
                    SwipeItemLayout pointItem = null;
                    //首先知道ev针对的是哪个item
                    View pointView = findTopChildUnder(rv, (int) x, (int) y);
                    if (pointView == null || !(pointView instanceof SwipeItemLayout)) {
                        //可能是head view或bottom view
                        pointOther = true;
                    } else
                        pointItem = (SwipeItemLayout) pointView;

                    //此时的pointOther=true,意味着点击的view为空或者点击的不是item
                    //还没有把点击的是item但是不是capture item给过滤出来
                    if (!pointOther && (mCaptureItem == null || mCaptureItem != pointItem))
                        pointOther = true;

                    //点击的是capture item
                    if (!pointOther) {
                        Mode touchMode = mCaptureItem.getTouchMode();

                        //如果它在fling,就转为drag
                        //需要拦截,并且requestDisallowInterceptTouchEvent
                        boolean disallowIntercept = false;
                        if (touchMode == Mode.FLING) {
                            mCaptureItem.setTouchMode(Mode.DRAG);
                            disallowIntercept = true;
                            intercept = true;
                        } else {//如果是expand的,就不允许parent拦截
                            mCaptureItem.setTouchMode(Mode.TAP);
                            if (mCaptureItem.isOpen())
                                disallowIntercept = true;
                        }

                        if (disallowIntercept) {
                            final ViewParent parent = rv.getParent();
                            if (parent != null)
                                parent.requestDisallowInterceptTouchEvent(true);
                        }
                    } else {//capture item为null或者与point item不一样
                        //直接将其close掉
                        if (mCaptureItem != null && mCaptureItem.isOpen()) {
                            mCaptureItem.close();
                            mCaptureItem = null;
                            intercept = true;
                        }

                        if (pointItem != null) {
                            mCaptureItem = pointItem;
                            mCaptureItem.setTouchMode(Mode.TAP);
                        } else
                            mCaptureItem = null;
                    }

                    //如果parent处于fling状态,此时,parent就会转为drag。此时,应该将后续move都交给parent处理
                    mIsProbeParent = true;
                    mDealByParent = rv.onInterceptTouchEvent(ev);
                    mIsProbeParent = false;
                    if (mDealByParent)
                        intercept = false;
                    break;
                }

                case MotionEvent.ACTION_POINTER_DOWN: {
                    final int actionIndex = ev.getActionIndex();
                    mActivePointerId = ev.getPointerId(actionIndex);

                    mLastMotionX = ev.getX(actionIndex);
                    mLastMotionY = ev.getY(actionIndex);
                    break;
                }

                case MotionEvent.ACTION_POINTER_UP: {
                    final int actionIndex = ev.getActionIndex();
                    final int pointerId = ev.getPointerId(actionIndex);
                    if (pointerId == mActivePointerId) {
                        final int newIndex = actionIndex == 0 ? 1 : 0;
                        mActivePointerId = ev.getPointerId(newIndex);

                        mLastMotionX = ev.getX(newIndex);
                        mLastMotionY = ev.getY(newIndex);
                    }
                    break;
                }

                //down时,已经将capture item定下来了。所以,后面可以安心考虑event处理
                case MotionEvent.ACTION_MOVE: {
                    final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
                    if (activePointerIndex == -1)
                        break;

                    //在down时,就被认定为parent的drag,所以,直接交给parent处理即可
                    if (mDealByParent) {
                        if (mCaptureItem != null && mCaptureItem.isOpen())
                            mCaptureItem.close();
                        return false;
                    }

                    final int x = (int) (ev.getX(activePointerIndex) + .5f);
                    final int y = (int) ((int) ev.getY(activePointerIndex) + .5f);

                    int deltaX = (int) (x - mLastMotionX);
                    int deltaY = (int) (y - mLastMotionY);
                    final int xDiff = Math.abs(deltaX);
                    final int yDiff = Math.abs(deltaY);

                    if (mCaptureItem != null && !mDealByParent) {
                        Mode touchMode = mCaptureItem.getTouchMode();

                        if (touchMode == Mode.TAP) {
                            //如果capture item是open的,下拉有两种处理方式:
                            //  1、下拉后,直接close item
                            //  2、只要是open的,就拦截所有它的消息,这样如果点击open的,就只能滑动该capture item
                            //网易邮箱,在open的情况下,下拉直接close
                            //QQ,在open的情况下,下拉也是close。但是,做的不够好,没有达到该效果。
                            if (xDiff > mTouchSlop && xDiff > yDiff) {
                                mCaptureItem.setTouchMode(Mode.DRAG);
                                final ViewParent parent = rv.getParent();
                                parent.requestDisallowInterceptTouchEvent(true);

                                deltaX = deltaX > 0 ? deltaX - mTouchSlop : deltaX + mTouchSlop;
                            } else {// if(yDiff>mTouchSlop){
                                mIsProbeParent = true;
                                boolean isParentConsume = rv.onInterceptTouchEvent(ev);
                                mIsProbeParent = false;
                                if (isParentConsume) {
                                    //表明不是水平滑动,即不判定为SwipeItemLayout的滑动
                                    //但是,可能是下拉刷新SwipeRefreshLayout或者RecyclerView的滑动
                                    //一般的下拉判定,都是yDiff>mTouchSlop,所以,此处这么写不会出问题
                                    //这里这么做以后,如果判定为下拉,就直接close
                                    mDealByParent = true;
                                    mCaptureItem.close();
                                }
                            }
                        }

                        touchMode = mCaptureItem.getTouchMode();
                        if (touchMode == Mode.DRAG) {
                            intercept = true;
                            mLastMotionX = x;
                            mLastMotionY = y;

                            //对capture item进行拖拽
                            mCaptureItem.trackMotionScroll(deltaX);
                        }
                    }
                    break;
                }

                case MotionEvent.ACTION_UP:
                    if (mCaptureItem != null) {
                        Mode touchMode = mCaptureItem.getTouchMode();
                        if (touchMode == Mode.DRAG) {
                            final VelocityTracker velocityTracker = mVelocityTracker;
                            velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                            int xVel = (int) velocityTracker.getXVelocity(mActivePointerId);
                            mCaptureItem.fling(xVel);

                            intercept = true;
                        }
                    }
                    cancel();
                    break;

                case MotionEvent.ACTION_CANCEL:
                    if (mCaptureItem != null)
                        mCaptureItem.revise();
                    cancel();
                    break;
            }

            return intercept;
        }

        @Override
        public void onTouchEvent(RecyclerView rv, MotionEvent ev) {
            final int action = ev.getActionMasked();
            final int actionIndex = ev.getActionIndex();

            if (mVelocityTracker == null) {
                mVelocityTracker = VelocityTracker.obtain();
            }
            mVelocityTracker.addMovement(ev);

            switch (action) {
                case MotionEvent.ACTION_POINTER_DOWN:
                    mActivePointerId = ev.getPointerId(actionIndex);

                    mLastMotionX = ev.getX(actionIndex);
                    mLastMotionY = ev.getY(actionIndex);
                    break;

                case MotionEvent.ACTION_POINTER_UP:
                    final int pointerId = ev.getPointerId(actionIndex);
                    if (pointerId == mActivePointerId) {
                        final int newIndex = actionIndex == 0 ? 1 : 0;
                        mActivePointerId = ev.getPointerId(newIndex);

                        mLastMotionX = ev.getX(newIndex);
                        mLastMotionY = ev.getY(newIndex);
                    }
                    break;

                //down时,已经将capture item定下来了。所以,后面可以安心考虑event处理
                case MotionEvent.ACTION_MOVE: {
                    final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
                    if (activePointerIndex == -1)
                        break;

                    final float x = ev.getX(activePointerIndex);
                    final float y = (int) ev.getY(activePointerIndex);

                    int deltaX = (int) (x - mLastMotionX);

                    if (mCaptureItem != null && mCaptureItem.getTouchMode() == Mode.DRAG) {
                        mLastMotionX = x;
                        mLastMotionY = y;

                        //对capture item进行拖拽
                        mCaptureItem.trackMotionScroll(deltaX);
                    }
                    break;
                }

                case MotionEvent.ACTION_UP:
                    if (mCaptureItem != null) {
                        Mode touchMode = mCaptureItem.getTouchMode();
                        if (touchMode == Mode.DRAG) {
                            final VelocityTracker velocityTracker = mVelocityTracker;
                            velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                            int xVel = (int) velocityTracker.getXVelocity(mActivePointerId);
                            mCaptureItem.fling(xVel);
                        }
                    }
                    cancel();
                    break;

                case MotionEvent.ACTION_CANCEL:
                    if (mCaptureItem != null)
                        mCaptureItem.revise();

                    cancel();
                    break;

            }
        }

        @Override
        public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
        }

        void cancel() {
            mDealByParent = false;
            mActivePointerId = -1;
            if (mVelocityTracker != null) {
                mVelocityTracker.recycle();
                mVelocityTracker = null;
            }
        }

    }

    static View findTopChildUnder(ViewGroup parent, int x, int y) {
        final int childCount = parent.getChildCount();
        for (int i = childCount - 1; i >= 0; i--) {
            final View child = parent.getChildAt(i);
            if (x >= child.getLeft() && x < child.getRight()
                    && y >= child.getTop() && y < child.getBottom()) {
                return child;
            }
        }
        return null;
    }

    public static void closeAllItems(RecyclerView recyclerView) {
        for (int i = 0; i < recyclerView.getChildCount(); i++) {
            View child = recyclerView.getChildAt(i);
            if (child instanceof SwipeItemLayout) {
                SwipeItemLayout swipeItemLayout = (SwipeItemLayout) child;
                if (swipeItemLayout.isOpen())
                    swipeItemLayout.close();
            }
        }
    }

}
复制代码

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Mission Python

Mission Python

Sean McManus / No Starch Press / 2018-9-18 / GBP 24.99

Launch into coding with Mission Python, a space-themed guide to building a complete computer game in Python. You'll learn programming fundamentals like loops, strings, and lists as you build Escape!, ......一起来看看 《Mission Python》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具