本文共 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(lastPosY0)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 Listlist=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 Listlist=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(lastPosY0)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 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!