美文网首页Android自定义View我爱编程
Android自定义View(四)侧滑布局

Android自定义View(四)侧滑布局

作者: digtal_ | 来源:发表于2018-08-09 17:25 被阅读47次

    利用Scroller来写一个侧滑布局效果如下:

    ezgif-5-2511a3329a.gif

    1.自定义一个Viewgroup在构造方法初始化参数

            mScroller = new Scroller(context);
            //滑动临界值
            ViewConfiguration viewConfiguration = ViewConfiguration.get(context);
            mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(viewConfiguration);
    

    2.在onMeasure方法中测量子孩子大小

            if (!mIsMeasure) {
                for (int i = 0; i < getChildCount(); i++) {
                    View view = getChildAt(i);
                    measureChild(view, widthMeasureSpec, heightMeasureSpec);
                }
                mIsMeasure = true;
            }
    

    3.在onLayout布局中初始化子孩子的位置里面有三个view分别是文本内容,置顶以及删除

      @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            for (int i = 0; i < getChildCount(); i++) {
                View view = getChildAt(i);
                if (i == 0) {
                    mTextWidth = view.getMeasuredWidth();
                    view.layout(0, 0, mTextWidth, view.getMeasuredHeight());
                } else if (i == 1) {
                  //置顶文本的宽度
                    mStickWidth = view.getMeasuredWidth();
                    view.layout(mTextWidth, 0, mTextWidth + mStickWidth, view.getMeasuredHeight());
    
                } else if (i == 2) {
              //删除文本的宽度
                    mDeleteWidth = view.getMeasuredWidth();
                    view.layout(mTextWidth + mStickWidth, 0, mTextWidth + mStickWidth + mDeleteWidth, view.getMeasuredHeight());
                }
            }
        }
    

    4.在onInterceptTouchEvent方法中处理滑动事件,如果是横向滑动我们就拦截事件自己去处理事件

        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            switch (ev.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    mDownX = (int) ev.getRawX();
                    break;
                case MotionEvent.ACTION_MOVE:
                    mMovex = (int) ev.getRawX();
                    float diff = Math.abs(mMovex - mDownX);
                    // 当手指拖动值大于TouchSlop值时,认为应该进行滚动,拦截子控件的事件
                    mLastMove = mMovex;
                    if (diff > mTouchSlop) {
                        return true;
                    }
                    break;
            }
            return super.onInterceptTouchEvent(ev);
        }
    

    5.在onTouchEvent方法中处理滑动事件,用ScrollBy方法来移动控件,移动过程中对移动范围进行限制
    move事件中:

                   mMovex = (int) event.getX();
                    scrollBy(mLastMove - mMovex, 0);
                    if (getScrollX() <= 0) {
                        scrollTo(0, 0);
                        return true;
                    }
                    if (getScrollX() >= mDeleteWidth + mStickWidth) {
                        scrollTo(mDeleteWidth + mStickWidth, 0);
                        return true;
                    }
                    mLastMove = mMovex;
    

    当手指抬起的时候根据移动的位置来判断是否是打开还是关闭getScrollX()方法可以得到移动的x值,大于置顶和删除文本宽度一半时就是打开状态否则就是关闭

     if (getScrollX() >= mDeleteWidth) {//open
          dx = mDeleteWidth + mStickWidth - getScrollX();
         if (mOnStateChangeListener != null) {
            mOnStateChangeListener.onOpen(this);
          }
       } else {//close
          dx = 0 - getScrollX();
           mOnStateChangeListener.onClose(this);
        }
     mScroller.startScroll(getScrollX(), 0, dx, 0);
     invalidate();
    

    6.在配合Recycleview使用的时候要注意的地方:

    在onTouchEvent方法中move事件要请求父控件不要拦截否则收不到up事件

     //请求父容器不要拦截事件
    getParent().requestDisallowInterceptTouchEvent(true);
    

    在侧滑时一个条目时其他条目都要关闭这里利用adapter进行对当前滑动条目的记录和已经打开的条目记录,然后在move的时候进行判断

     case MotionEvent.ACTION_MOVE:
        if (getAdapter().getOpenView() != null && getAdapter().getOpenView() != this) {
           getAdapter().getOpenView().close();
           getAdapter().setOpenView(null);
           return true;
           }
         if (getAdapter().getCurrentDeleteView() !=null && getAdapter().getCurrentDeleteView() != this) {
            return true;
         }
    

    完整代码:

    public class DeleteView extends ViewGroup {
    
        private Scroller mScroller;
        private int mDownX;
        private int mMovex;
        private int mTouchSlop;
        private boolean mIsMeasure = false;
        private int mTextWidth;
        private int mStickWidth;
        private int mDeleteWidth;
        private int mLastMove;
    
        public DeleteView(Context context) {
            this(context, null);
        }
    
        public DeleteView(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
            mScroller = new Scroller(context);
            //滑动临界值
            ViewConfiguration viewConfiguration = ViewConfiguration.get(context);
            mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(viewConfiguration);
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            if (!mIsMeasure) {
                for (int i = 0; i < getChildCount(); i++) {
                    View view = getChildAt(i);
                    measureChild(view, widthMeasureSpec, heightMeasureSpec);
                }
                mIsMeasure = true;
            }
        }
        
        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            for (int i = 0; i < getChildCount(); i++) {
                View view = getChildAt(i);
                if (i == 0) {
                    mTextWidth = view.getMeasuredWidth();
                    view.layout(0, 0, mTextWidth, view.getMeasuredHeight());
                } else if (i == 1) {
                    mStickWidth = view.getMeasuredWidth();
                    view.layout(mTextWidth, 0, mTextWidth + mStickWidth, view.getMeasuredHeight());
    
                } else if (i == 2) {
                    mDeleteWidth = view.getMeasuredWidth();
                    view.layout(mTextWidth + mStickWidth, 0, mTextWidth + mStickWidth + mDeleteWidth, view.getMeasuredHeight());
    
                }
    
            }
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            switch (ev.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    mDownX = (int) ev.getRawX();
                    break;
                case MotionEvent.ACTION_MOVE:
                    mMovex = (int) ev.getRawX();
                    float diff = Math.abs(mMovex - mDownX);
                    // 当手指拖动值大于TouchSlop值时,认为应该进行滚动,拦截子控件的事件
                    mLastMove = mMovex;
                    if (diff > mTouchSlop) {
                        return true;
                    }
                    break;
            }
            return super.onInterceptTouchEvent(ev);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()) {
    
                case MotionEvent.ACTION_MOVE:
                    if (getAdapter().getOpenView() != null && getAdapter().getOpenView() != this) {
                        getAdapter().getOpenView().close();
                        getAdapter().setOpenView(null);
                        return true;
                    }
                    if (getAdapter().getCurrentDeleteView() !=null && getAdapter().getCurrentDeleteView() != this) {
                        return true;
                    }
                    getAdapter().setCurrentDeleteView(this);
                    //请求父容器不要拦截事件
                    getParent().requestDisallowInterceptTouchEvent(true);
                    mMovex = (int) event.getX();
                    scrollBy(mLastMove - mMovex, 0);
                    if (getScrollX() <= 0) {
                        scrollTo(0, 0);
                        return true;
                    }
                    if (getScrollX() >= mDeleteWidth + mStickWidth) {
                        scrollTo(mDeleteWidth + mStickWidth, 0);
                        return true;
                    }
                    mLastMove = mMovex;
                    break;
                case MotionEvent.ACTION_UP:
                    LogUtils.LogE("up");
                    int dx = 0;
                    if (getScrollX() >= mDeleteWidth) {//open
                        dx = mDeleteWidth + mStickWidth - getScrollX();
                        if (mOnStateChangeListener != null) {
                            mOnStateChangeListener.onOpen(this);
                        }
                    } else {//close
                        dx = 0 - getScrollX();
                        mOnStateChangeListener.onClose(this);
                    }
    
                    mScroller.startScroll(getScrollX(), 0, dx, 0);
                    invalidate();
                    getAdapter().setCurrentDeleteView(null);
                    break;
            }
            return true;
        }
    
        @Override
        public void computeScroll() {
            if (mScroller.computeScrollOffset()) {
                scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
                invalidate();
            }
    
        }
    
        public interface onStateChangeListener {
            void onOpen(DeleteView deleteView);
    
            void onClose(DeleteView deleteView);
        }
    
        private onStateChangeListener mOnStateChangeListener;
    
        public void setOnStateChangeListener(onStateChangeListener onStateChangeListener) {
            mOnStateChangeListener = onStateChangeListener;
        }
    
        public void open() {
            scrollTo(mStickWidth + mDeleteWidth, 0);
        }
    
        public void close() {
            scrollTo(0, 0);
        }
    
        /**
         * 得到适配器
         *
         * @return
         */
        private DeleteAdapter getAdapter() {
            return (DeleteAdapter) ((RecyclerView) getParent()).getAdapter();
        }
    
    

    Adapter代码:

    public class DeleteAdapter extends RecyclerView.Adapter {
    
    
        private List<CartModel> mDatas;
    
        private DeleteView mDeleteView;
        private DeleteView mOpenView;
    
        public DeleteAdapter(List<CartModel> list) {
            mDatas = list;
        }
    
    
        public void setData(List<CartModel> list) {
            mDatas = list;
            notifyDataSetChanged();
        }
    
        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    
            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_delete, parent, false);
            MyHolder holder = new MyHolder(view);
    
            return holder;
        }
    
        @Override
        public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
            MyHolder myHolder = (MyHolder) holder;
            myHolder.setItem(position);
    
            ((DeleteView) holder.itemView).setOnStateChangeListener(new DeleteView.onStateChangeListener() {
                @Override
                public void onOpen(DeleteView deleteView) {
                    mOpenView = deleteView;
                }
    
                @Override
                public void onClose(DeleteView deleteView) {
    
                }
            });
    
            myHolder.delete.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(v.getContext(),"删除"+position,Toast.LENGTH_SHORT).show();
                    mDatas.remove(position);
                    getOpenView().close();
                    notifyDataSetChanged();
                }
            });
            myHolder.stick.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    CartModel cartModel = mDatas.get(position);
                    Toast.makeText(v.getContext(),"置顶"+position,Toast.LENGTH_SHORT).show();
                    mDatas.remove(cartModel);
                    mDatas.add(0,cartModel);
                    getOpenView().close();
                    notifyDataSetChanged();
                }
            });
        }
    
        /**
         * 得到当前滑动的view
         *
         * @return
         */
        public DeleteView getCurrentDeleteView() {
            return mDeleteView;
        }
    
        /**
         * 得到当前滑动的view
         *
         * @return
         */
        public void setCurrentDeleteView(DeleteView deleteView) {
            this.mDeleteView = deleteView;
        }
    
        public DeleteView getOpenView() {
            return mOpenView;
        }
    
        public void setOpenView(DeleteView openView) {
            mOpenView = openView;
        }
    
        @Override
        public int getItemCount() {//加的一是脚步局
            return mDatas.size();
        }
    
    
        class MyHolder extends RecyclerView.ViewHolder {
            TextView title;
            TextView stick;
            TextView delete;
    
            public MyHolder(View itemView) {
                super(itemView);
                title = itemView.findViewById(R.id.tv_title);
                stick = itemView.findViewById(R.id.tv_stick);
                delete = itemView.findViewById(R.id.tv_delete);
    
            }
    
            public void setItem(int position) {
                CartModel cartModel = mDatas.get(position);
                title.setText(cartModel.getName());
    
            }
        }
    }
    
    

    相关文章

      网友评论

        本文标题:Android自定义View(四)侧滑布局

        本文链接:https://www.haomeiwen.com/subject/msdcbftx.html