he is coding

ScrollView嵌套ViewPager嵌套RecyclerView

| Comments

需求背景

一图胜千言

点击展开按钮可以展开

  1. 首先这个页面可以竖向滑动
  2. tab栏固定位置,只有tab栏底下的部分滑动
  3. 点击上面的tab,底下的条目会有对应的切换,横向滑动下面条目,tab也会更改对应的选中状态
  4. 点击展开按钮/收起按钮,条目数量会有对应的变化
  5. tab在切换时,底部的横向RecyclerView不动

实现

最终我选择的方案是 最外层LinearLayout,里面依次为播放器TabLayoutScrollView ScrollView里面竖向包含ViewPager和底部的横向RecyclerView ViewPager里面的每个子view都是RecyclerView

遇到的问题

  1. ViewPager的高度出不来
  2. ScrollView和ViewPager里面的RecyclerView滑动冲突

分析解决

ViewPager的高度出不来

对于这个问题,可以重写ViewPager的onMeasure方法,重新定义测量子View的规则。 还有个问题,每个tab展开收缩的状态都不一样,一次在切tab时,要重新测量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
public class CustomViewPager extends ViewPager {

    private int current;
    private int height = 0;
    /**
     * 保存position与对于的View
     */
    private SparseArray<View> mChildrenViews = new SparseArray<View>();

    public CustomViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
        addOnPageChangeListener(new OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            }

            @Override
            public void onPageSelected(int position) {
                resetHeight(position);
            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        if (mChildrenViews.size() > current) {
            View child = mChildrenViews.get(current);
            child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
            height = child.getMeasuredHeight();
        }

        heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    public void resetHeight(int current) {
        this.current = current;
        if (mChildrenViews.size() > current) {

            FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) getLayoutParams();
            if (layoutParams == null) {
                layoutParams = new FrameLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, height);
            } else {
                layoutParams.height = height;
            }
            setLayoutParams(layoutParams);
        }
    }

    /**
     * 在RecyclerView初始化之后调用这个方法
     * 
     * 保存position和View的对应关系
     */
    public void setObjectForPosition(View view, int position) {
        mChildrenViews.put(position, view);
    }
}

ScrollView和ViewPager里面的RecyclerView滑动冲突

让ScrollView拦截所有的竖向滑动事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class CustomScrollView extends ScrollView {

    private int downY;
    private int mTouchSlop;

    public CustomScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                downY = (int) ev.getRawY();
                break;
            case MotionEvent.ACTION_MOVE:
                int moveY = (int) ev.getRawY();
                // 判断是否滑动,若滑动就拦截事件
                if (Math.abs(moveY - downY) > mTouchSlop) {
                    return true;
                }
                break;
            default:
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }
}

Comments