Android 自定义LinearLayout实现滑动下拉抽屉的功能(解决滑动冲突)
发布日期:2021-06-29 11:46:25 浏览次数:3 分类:技术文章

本文共 7416 字,大约阅读时间需要 24 分钟。

本篇我们来解决上一篇的遗留问题:自定义LinearLayout与RecyclerView的滑动冲突。

没看过上一篇文章的,可以先去看一下:

先看效果图:

一、解决思路

当手指一开始滑动时,触摸事件会被传递给RecyclerView去处理。并且在滑动列表数据的时候,我们确实是想让RecyclerView去处理触摸事件。经过长时间尝试,我发现用onInterceptTouchEvent方法让LinearLayout拦截事件是很难实现图中的效果。所以我换了一种解决思路:重写RecyclerView的onTouchEvent方法

我们不再去处理触摸的拦截事件,全部交由RecyclerView处理,并且当滑动到RecyclerView顶部并继续手指往下滑时,就手动调用LinearLayout的scrollTo或scrollBy来让它进行相应的滑动,而不是在LinearLayout的触摸事件里做逻辑处理。

当我们滑到抽屉页面时,自然而然触摸事件就自动交给了自定义LinearLayout处理。

二、代码解释

下面我给出代码,并解释每一步的操作

创建MyRecyclerView类并继承RecyclerView:

public class MyRecyclerView extends RecyclerView {    public MyRecyclerView(@NonNull Context context) {        super(context);    }    public MyRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {        super(context, attrs);    }    public MyRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);    }    @Override    public boolean onTouchEvent(MotionEvent e) {        return super.onTouchEvent(e);    }}

因为在滑动的时候,RecyclerView需要提醒外界是否手指向下滑动或者滑动完毕,所以需要开放接口:

public interface OnScroll{    //向下滑动时回调    float scrollPullDown(int dy);    //手指离开屏幕时回调    void eventUp(int dy);}private OnScroll onScroll;public void setOnScroll(OnScroll onScroll){    this.onScroll=onScroll;}

当手指向上滑动时,肯定是处在滑动列表数据的状态,我们不用去做处理。当下滑的时候,可能是在滑动列表,也有可能是在滑动LinearLayout,这里就需要去判断一下。下面我们重写RecyclerView的onTouchEvent方法:

private float lastPosY,moveY;   //lastPosY:手指最后滑动到的y轴位置;moveY:手指总共滑动的垂直距离@Overridepublic boolean onTouchEvent(MotionEvent e) {    switch (e.getAction()){        case MotionEvent.ACTION_DOWN:            lastPosY=e.getY();            moveY=0;            break;        case MotionEvent.ACTION_MOVE:            //如果手指往下滑动            if(lastPosY
0)return true; } } lastPosY=e.getY(); if(moveY>0)return true; break; case MotionEvent.ACTION_UP: if(onScroll!=null) onScroll.eventUp((int)moveY); break; } return super.onTouchEvent(e);}

lastPosY<e.getY():代表手指向下滑动

(e.getY()-lastPosY)/2:减少每次滑动LinearLayout的距离,保证滑动的流畅性

if(moveY>0)return true:防止在滑动LinearLayout的时候同时滑动列表,从而产生滑动时的抖动。

 现在我们已经把滑动信息传递给了外界,下面我们用Activity来获取该信息:

private MyRecyclerView recyclerView;private MyPullDownLayout pullDownLayout;private LinearLayoutManager linearLayoutManager;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.act_pull_down);    init();}private void init(){    final List
list=new ArrayList<>(); //...... recyclerView=findViewById(R.id.recyclerview); recyclerView.setAdapter(new MyRecyclerViewAdapter(list)); linearLayoutManager=new LinearLayoutManager(this); recyclerView.setLayoutManager(linearLayoutManager); recyclerView.setOnScroll(new MyRecyclerView.OnScroll() { @Override public float scrollPullDown(int dy) { return 0; } @Override public void eventUp(int dy) { } });}

xml文件也给大家放一下:

那么在Activity中要怎么判断是否需要滑动LinearLayout呢?其实很容易,如果RecyclerView列表的第一项完全显示并且手指还在往下滑,那么就需要滑动LinearLayout,代码如下(Activity类):

private void init(){    final List
list=new ArrayList<>(); //...... pullDownLayout=findViewById(R.id.pulldown_layout); recyclerView=findViewById(R.id.recyclerview); recyclerView.setAdapter(new MyRecyclerViewAdapter(list)); linearLayoutManager=new LinearLayoutManager(this); recyclerView.setLayoutManager(linearLayoutManager); recyclerView.setOnScroll(new MyRecyclerView.OnScroll() { @Override public float scrollPullDown(int dy) { if(linearLayoutManager.findFirstCompletelyVisibleItemPosition()==0){ pullDownLayout.scrollY(dy); return dy; } return 0; } @Override public void eventUp(int dy) { if(linearLayoutManager.findFirstVisibleItemPosition()==0){ pullDownLayout.scrollToPage(dy); } } });}

linearLayoutManager.findFirstCompletelyVisibleItemPosition()==0:如果列表第一项完全显示

linearLayoutManager.findFirstVisibleItemPosition()==0:如果列表第一项有显示

我们再来看看pullDownLayout对象的scrollY和scrollToPage方法:

public class MyPullDownLayout extends LinearLayout {    private Scroller scroller;    public MyPullDownLayout(Context context, @Nullable AttributeSet attrs) {        super(context, attrs);        init();    }    private void init(){        scroller=new Scroller(getContext());    }    //......    @Override    public void computeScroll() {        if(scroller.computeScrollOffset()){            scrollTo(0,scroller.getCurrY());            postInvalidateDelayed(8);        }    }    private int getVerticalHeight(){        return getHeight()-getPaddingBottom()-getPaddingTop();    }    public void scrollY(int dy){        scrollTo(0,-dy);    }    public void scrollToPage(int dy){        if(dy

dy<getVerticalHeight()/6:如果总共滑动的距离小于布局高度的1/6,则自动返回到RecyclerView所在页面,否则自动滑动到抽屉页面

到这里,滑动冲突就解决了(虽然不是利用拦截的手段,但也是一种解决办法,而且效果也不错)


下面是MyRecyclerView完整代码:

public class MyRecyclerView extends RecyclerView {    private float lastPosY,moveY;   //lastPosY:手指最后滑动到的y轴位置;moveY:手指总共滑动的垂直距离    public MyRecyclerView(@NonNull Context context) {        super(context);    }    public MyRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {        super(context, attrs);    }    public MyRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);    }    @Override    public boolean onTouchEvent(MotionEvent e) {        switch (e.getAction()){            case MotionEvent.ACTION_DOWN:                lastPosY=e.getY();                moveY=0;                break;            case MotionEvent.ACTION_MOVE:                //如果手指往下滑动                if(lastPosY
0)return true; } } lastPosY=e.getY(); if(moveY>0)return true; break; case MotionEvent.ACTION_UP: if(onScroll!=null) onScroll.eventUp((int)moveY); break; } return super.onTouchEvent(e); } public interface OnScroll{ //向下滑动时回调 float scrollPullDown(int dy); //手指离开屏幕时回调 void eventUp(int dy); } private OnScroll onScroll; public void setOnScroll(OnScroll onScroll){ this.onScroll=onScroll; }}

MyRecyclerViewAdapter代码:

public class MyRecyclerViewAdapter extends RecyclerView.Adapter
{ private List
list; public MyRecyclerViewAdapter(List
list){ this.list=list; } @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list,parent,false); ViewHolder holder = new ViewHolder(view); return holder; } @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { holder.tv.setText(list.get(position)); } @Override public int getItemCount() { return list.size(); } class ViewHolder extends RecyclerView.ViewHolder{ TextView tv; public ViewHolder(@NonNull View view) { super(view); tv=view.findViewById(R.id.tv); } }}

item_list布局文件:

 

转载地址:https://blog.csdn.net/zz51233273/article/details/108355290 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:解决Android project already contains module with this name
下一篇:Android 自定义LinearLayout实现滑动下拉抽屉的功能

发表评论

最新留言

第一次来,支持一个
[***.219.124.196]2024年04月14日 06时17分41秒