Android布局中NestedScrollView嵌套viewpager不显示内容的解决方案(含TabLayout + viewpager 的场景)

一、概念分析

NestedScrollView里面只能有一个ViewGroup,也就是只允许有一个子节点(允许一个父子节点包含多个子子节点)的存在。

1、如果你的NestedScrollView包含了多个子节点就会报错,一般来说,都会在所有子节点的最外层嵌套一个LinearLayout或者其他你业务场景的布局。

子节点:通俗的讲,一个button是一个子节点,一个TextView也是一个子节点,以此类推,每个控件都可以理解为一个字节点

2、viewpager为什么不能显示内容,无论你是这样设置宽高

android:layout_width="match_parent"
android:layout_height="wrap_content"

还是这样设置宽高,都无法显示

android:layout_width="match_parent"
android:layout_height="match_parent"

但是你给高度设置具体的数值时“android:layout_height=“xxxdp””,才可以显示内容,但是如果你的ViewPager里面的Fragment 拥有一个RecyclerView的时候,固定的数值明显不能从根本上解决问题。

原因:

NestedScrollView 计算高度先于 ViewPager 渲染呈现前,所以 ViewPager 的高度才会一直是0,除非你自己给了固定高度

问题解决方案一:

在NestedScrollView 控件属性中加入
android:fillViewport=“true”

局限是:低于Android 9 以下版本开发的时候

问题解决方案二:

给ViewPager 重写一遍测量,重新给测量高度;
在每次测完后将测量得到的高度保存下来,
下次ViewPager 再切换回来就可以直接设置高度

步骤一:创建自定义的 AutoHeightViewPager 类,继承ViewPager 覆盖重写 计算高度 的onMeasure 方法,并梳理计算和保存原先的高度值等等…

public class AutoHeightViewPager extends ViewPager {
    
    

    private int mHeight = 0;

    /** 已经获取到的高度下标 : 当前的高度 */
    private int mCurPosition = 0;

    /** 当前显示下标 */
    private int mPosition = 0;

    /** 按下标存储View历史高度 */
    private HashMap<Integer, Integer> mChildrenViews = new LinkedHashMap<Integer, Integer>();
    /** 记录页面是否存储了高度 */
    private HashMap<Integer, Boolean> indexList = new LinkedHashMap<Integer, Boolean>();

    /** 做自适应高度,必须先进行初始化标记 */
    public void initIndexList(int size) {
    
    
        mHeight = 0;
        mCurPosition = 0;
        mPosition = 0;
        for (int i = 0; i < size; i++) {
    
    
            /** 初始化高度存储状态 */
            indexList.put(i, false);
        }
    }

    public AutoHeightViewPager(@NonNull Context context) {
    
    
        super(context);
    }

    public AutoHeightViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {
    
    
        super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    
    
        int height = 0;
        if (indexList.size() > 0) {
    
    
            if (indexList.get(mPosition)) {
    
    
                height = mChildrenViews.get(mPosition);
            } else {
    
    
                for (int i = 0; i < getChildCount(); i++) {
    
    
                    View child = getChildAt(i);
                    child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
                    int h = child.getMeasuredHeight();
                    if (h > height) {
    
    
                        height = h;
                    }
                }
                mHeight = height;
            }
        }

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

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    /**
     * 在viewpager 切换的时候进行更新高度
     */
    public void updateHeight(int current) {
    
    
        this.mPosition = current;
        if (indexList.size() > 0) {
    
    
            saveIndexData();
            if (indexList.get(current)) {
    
    
                int height = 0;
                if (mChildrenViews.get(current) != null) {
    
    
                    height = mChildrenViews.get(current);
                }
                LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) getLayoutParams();
                if (layoutParams == null) {
    
    
                    layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, height);
                } else {
    
    
                    layoutParams.height = height;
                }
                setLayoutParams(layoutParams);
            }
            this.mCurPosition = current;
        }
    }

    /**
     * 保存已经测绘好的高度
     */
    private void saveIndexData() {
    
    
        if (!indexList.get(mCurPosition)) {
    
    
            /** 没保存高度时,保存 */
            indexList.put(mCurPosition, true);
            mChildrenViews.put(mCurPosition, mHeight);
        }
    }


}

步骤二:在xml中引用自定义的控件

 <xxx.xxx.xxx.AutoHeightViewPager
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

xxx.xxx.xxx是你的自定义控件路径,也就是你AutoHeightViewPager在哪个包下的路径

注意:AutoHeightViewPager类中一定要有

 public AutoHeightViewPager(Context context, AttributeSet attrs) {
    
    
        super(context, attrs);
    }

不然会报 :

Android.View.InflateException: Binary XML File Line #异常

步骤三:在你的Activity / Fragment 中引用

		 /** 初始化记录是否保存高度的下标 , 注:在重新刷新加载页面的时候,需要对下标进行重新初始化 */
		viewpager.initIndexList(fragments.size());
		 /** 添加viewpager 切换监听 */
		viewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
    
    
            @Override
            public void onPageScrolled(int i, float v, int i1) {
    
    

            }

            @Override
            public void onPageSelected(int i) {
    
    
            	//在每次切换的时候更新高度
                viewpager.updateHeight(i);
            }

            @Override
            public void onPageScrollStateChanged(int i) {
    
    

            }
        });
		
fragments.size() 代表你fragments的数量大小,也可以传入具体的int数值

猜你喜欢

转载自blog.csdn.net/weixin_44720673/article/details/111477948#comments_22593755