Android RecyclerView嵌套ViewPager自适应高度

引言

在Android开发中,经常会遇到需要嵌套多个可滑动的控件的场景。一个常见的需求是在RecyclerView的Item中嵌套ViewPager来展示多个页面。然而,当ViewPager的高度大于RecyclerView的Item时,会出现滑动冲突的问题。本文将介绍如何解决这个问题,并提供相应的代码示例。

问题描述

假设我们有一个RecyclerView,每个Item里面包含一个ViewPager,ViewPager里面有多个页面。当ViewPager的内容超过一屏时,就会出现滑动冲突的问题。当我们想滑动ViewPager时,RecyclerView会拦截滑动事件,导致ViewPager无法正常滑动。

解决方案

要解决上述问题,我们需要自定义一个RecyclerView,重写其onMeasure()方法,使其能够根据子控件的高度自适应高度。

自定义RecyclerView

下面是一个自定义的RecyclerView类,我们将其命名为WrapContentRecyclerView

public class WrapContentRecyclerView extends RecyclerView {
    public WrapContentRecyclerView(Context context) {
        super(context);
    }

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

    public WrapContentRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onMeasure(int widthSpec, int heightSpec) {
        int heightMeasureSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
        super.onMeasure(widthSpec, heightMeasureSpec);
    }
}

onMeasure()方法中,通过设置一个较大的高度值和MeasureSpec.AT_MOST来实现自适应高度。

使用自定义RecyclerView

在使用自定义RecyclerView时,只需要将原来的RecyclerView替换为WrapContentRecyclerView即可。

<com.example.app.WrapContentRecyclerView
    android:id="@+id/recyclerView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

具体代码示例

下面是一个简单的代码示例,演示了如何使用自定义RecyclerView嵌套ViewPager,并实现自适应高度。

public class MainActivity extends AppCompatActivity {
    private WrapContentRecyclerView mRecyclerView;
    private MyAdapter mAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mRecyclerView = findViewById(R.id.recyclerView);
        mAdapter = new MyAdapter();

        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        mRecyclerView.setAdapter(mAdapter);
    }

    private class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
        @NonNull
        @Override
        public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.item_viewpager, parent, false);
            return new ViewHolder(view);
        }

        @Override
        public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
            holder.setupViewPager();
        }

        @Override
        public int getItemCount() {
            return 10;
        }

        private class ViewHolder extends RecyclerView.ViewHolder {
            private ViewPager mViewPager;

            public ViewHolder(@NonNull View itemView) {
                super(itemView);
                mViewPager = itemView.findViewById(R.id.viewPager);
            }

            public void setupViewPager() {
                List<String> data = new ArrayList<>();
                for (int i = 0; i < 5; i++) {
                    data.add("Page " + (i + 1));
                }

                PagerAdapter adapter = new PagerAdapter(getSupportFragmentManager(), data);
                mViewPager.setAdapter(adapter);
            }
        }
    }
}

class PagerAdapter extends FragmentPagerAdapter {
    private List<String> mData;

    public PagerAdapter(FragmentManager fm, List<String> data) {
        super(fm);
        mData = data;
    }

    @Override
    public int getCount() {
        return mData.size();
    }

    @Override
    public Fragment getItem(int position) {
        return PageFragment.newInstance(mData.get(position));
    }
}

class PageFragment extends Fragment {
    private static final String ARG_TITLE = "title";

    private String mTitle;

    public static PageFragment newInstance(String title) {
        PageFragment fragment = new PageFragment();
        Bundle args = new Bundle();
        args.putString(ARG_TITLE, title);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mTitle = getArguments().getString(ARG_TITLE);
        }
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {