豎直滑動的RecyclerView巢狀豎直滑動的RecyclerView並解決滑動衝突
阿新 • • 發佈:2018-12-30
入職國美三個月了,進入大公司最大的感觸就是人遠比你自己想象的要強大!這一路走來實屬不易,但給我帶來的更多是成長。這裡是人生的起點,也可以說是夢開始的地方,加油吧!
專案中要求兩個紅框區域列表滑動時吸頂,頭像部分超過四行可上下滑動。
問題①兩個區域吸頂實現:這個比較好實現,我是在頁面根佈局RelativeLayout中寫了兩個跟吸頂佈局一樣的View,然後通過監聽RecyclerView滑動距離控制RelativeLayout中兩個佈局的顯示和隱藏。程式碼如下:
productRV.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); int[] peopleRegionLoc = new int[2]; peopleRegion.getLocationOnScreen(peopleRegionLoc); int peopleRegionLocX = peopleRegionLoc[0]; int peopleRegionLocY = peopleRegionLoc[1]; if (isStickTuan == false && peopleRegionLocY <= tuanLy.getHeight() + topTitleView.getHeight() + StatusBarUtil.getStatusBarHeight(HomeZutuanActivity.this)) { isStickTuan = true; tuanLyStick.setVisibility(View.VISIBLE); } else if (isStickTuan && peopleRegionLocY > tuanLy.getHeight() + topTitleView.getHeight() + StatusBarUtil.getStatusBarHeight(HomeZutuanActivity.this)) { tuanLyStick.setVisibility(View.INVISIBLE); isStickTuan = false; } int[] grayRegionLoc = new int[2]; grayRegion.getLocationOnScreen(grayRegionLoc); int grayRegionLocX = grayRegionLoc[0]; int grayRegionLocY = grayRegionLoc[1]; if (isStickTab == false && grayRegionLocY <= tuanLy.getHeight() + topTitleView.getHeight() + StatusBarUtil.getStatusBarHeight(HomeZutuanActivity.this) - ScreenUtils.dip2px(HomeZutuanActivity.this, 10)) { isStickTab = true; tabLyStick.setVisibility(View.VISIBLE); } else if (isStickTab && grayRegionLocY > tuanLy.getHeight() + topTitleView.getHeight() + StatusBarUtil.getStatusBarHeight(HomeZutuanActivity.this) - ScreenUtils.dip2px(HomeZutuanActivity.this, 10)) { tabLyStick.setVisibility(View.INVISIBLE); isStickTab = false; } } });
問題②:RecyclerView巢狀的RecyclerView也要實現可上下滑動。首先我寫了最基本的巢狀程式碼,發現內層RecyclerView根本無法滑動,設定setFocusable也是然並卵。當時第一想法就是先去網上找找有沒有類似效果,經過一番搜尋以失敗而告終。沒有辦法,只能硬著頭皮自己解決滑動衝突。隱隱約約記得之前看過一篇ScrollView巢狀ScrollView的文章(https://mp.weixin.qq.com/s/2-LVR0vTDDLoHQAt7Y4CCw),再次品味突然恍然大悟,自己心裡默默地 ao 了一聲,原來也不過如此嘛。
解決滑動衝突原理其實就是:當我們想讓內層滑動時在onTouchEvent中設定getParent().requestDisallowInterceptTouchEvent(true);這樣控制父View不攔截滑動事件,進而讓內層獲得焦點滑動。當內層RecyclerView滑動到底部了我們要讓外層滑動,設定etParent().requestDisallowInterceptTouchEvent(false);並返回false 不消耗事件。具體程式碼如下:
public class NestRecyclerView extends RecyclerView { private int lastVisibleItemPosition; private int firstVisibleItemPosition; private float mLastY = 0;// 記錄上次Y位置 private boolean isTopToBottom = false; private boolean isBottomToTop = false; public NestRecyclerView(Context context) { this(context, null); } public NestRecyclerView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public NestRecyclerView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } /*@Override public boolean onInterceptTouchEvent(MotionEvent ev) { int action = ev.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: getParent().requestDisallowInterceptTouchEvent(true); break; case MotionEvent.ACTION_MOVE: break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: getParent().requestDisallowInterceptTouchEvent(false); break; } return super.onInterceptTouchEvent(ev); }*/ @Override public boolean onTouchEvent(MotionEvent event) { int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: mLastY = event.getY(); //不允許父View攔截事件 getParent().requestDisallowInterceptTouchEvent(true); break; case MotionEvent.ACTION_MOVE: float nowY = event.getY(); isIntercept(nowY); if (isBottomToTop||isTopToBottom){ getParent().requestDisallowInterceptTouchEvent(false); return false; }else{ getParent().requestDisallowInterceptTouchEvent(true); } mLastY = nowY; break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: getParent().requestDisallowInterceptTouchEvent(false); break; } return super.onTouchEvent(event); } private void isIntercept(float nowY){ isTopToBottom = false; isBottomToTop = false; RecyclerView.LayoutManager layoutManager = getLayoutManager(); if (layoutManager instanceof GridLayoutManager) { //得到當前介面,最後一個子檢視對應的position lastVisibleItemPosition = ((GridLayoutManager) layoutManager) .findLastVisibleItemPosition(); //得到當前介面,第一個子檢視的position firstVisibleItemPosition = ((GridLayoutManager) layoutManager) .findFirstVisibleItemPosition(); } //得到當前介面可見資料的大小 int visibleItemCount = layoutManager.getChildCount(); //得到RecyclerView對應所有資料的大小 int totalItemCount = layoutManager.getItemCount(); LogUtils.d("nestScrolling","onScrollStateChanged"); if (visibleItemCount>0) { if (lastVisibleItemPosition == totalItemCount - 1) { //最後檢視對應的position等於總數-1時,說明上一次滑動結束時,觸底了 LogUtils.d("nestScrolling", "觸底了"); if (NestRecyclerView.this.canScrollVertically(-1) && nowY < mLastY) { // 不能向上滑動 LogUtils.d("nestScrolling", "不能向上滑動"); isBottomToTop = true; } else { LogUtils.d("nestScrolling", "向下滑動"); } } else if (firstVisibleItemPosition == 0) { //第一個檢視的position等於0,說明上一次滑動結束時,觸頂了 LogUtils.d("nestScrolling", "觸頂了"); if (NestRecyclerView.this.canScrollVertically(1) && nowY > mLastY) { // 不能向下滑動 LogUtils.d("nestScrolling", "不能向下滑動"); isTopToBottom = true; } else { LogUtils.d("nestScrolling", "向上滑動"); } } } } }
這個是我重寫的RecyclerView解決了巢狀的滑動衝突,喜歡的小夥伴可以直接拿去用。