1. 程式人生 > >豎直滑動的RecyclerView巢狀豎直滑動的RecyclerView並解決滑動衝突

豎直滑動的RecyclerView巢狀豎直滑動的RecyclerView並解決滑動衝突

入職國美三個月了,進入大公司最大的感觸就是人遠比你自己想象的要強大!這一路走來實屬不易,但給我帶來的更多是成長。這裡是人生的起點,也可以說是夢開始的地方,加油吧!

專案中要求兩個紅框區域列表滑動時吸頂,頭像部分超過四行可上下滑動。

問題①兩個區域吸頂實現:這個比較好實現,我是在頁面根佈局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解決了巢狀的滑動衝突,喜歡的小夥伴可以直接拿去用。