Android开发之复杂布局嵌套(ScrollView+TabLayout+ViewPager+RecyclerView)导致冲突的解决办法

前言

最近在做一些项目和毕业设计,所以自从上次梳理完数据结构之后,一直想写些什么,但是又没有比较好的内容,所以博客从过完年之后就停更了很长很长很长一段时间,不过这次在做项目的时候,正好遇到一个我本以为很简单,结果折腾了好久的一个问题,其实这个问题对于做Android开发的同学来说,并不陌生,那就是滑动布局的互相嵌套。

当然并不仅限于标题中写的这种嵌套,只要是可滑动的布局,例如GridView,各种自定义的可滑动控件等等,都会遇到这个问题,如果嵌套相对简单的话,例如ScrollView嵌套RecyclerView,解决滑动冲突的办法就简单点,可以使用NestedScrollView替代ScrollView或者重写RecyclerViewonMeasure方法,都可以达到解决冲突的效果,但是一但嵌套复杂起来,像ScrollView+TabLayout+ViewPager+RecyclerView这种,情况就不一样了,那么下面分享下我这次遇到的问题,以及整个探索过程,包括我最后的解决办法。

目录

1、需要实现的效果
2、探索过程
3、解决方案

正文

1、需要实现的效果

在项目中,我需要实现的效果其实在很多APP里都可以找到,由于项目需求明确写明是仿京东发现页面,所以是需要实现一个类似京东发现页面的效果,我们来到京东的发现页面,效果如下
在这里插入图片描述
这里把图中的效果再总结一下:
1、整体布局分为顶部标题栏,顶部导航栏,下方可左右滑动的内容区,导航栏和内容区是联动的
2、整体页面任意位置往上滑动时,若标题栏在屏幕可见区域内,则标题栏会滑出屏幕,导航栏会悬浮在顶部
3、整体页面任意位置往下滑动时,若标题栏之前被滑出了屏幕,则标题栏随下滑而出现在导航栏上方(图中没有演示出来,实际是有这个效果的)
4、若内容区滑到最顶部,再往下拉时,会产生下拉刷新

2、探索过程

2.1 我最初的实现思路

首先看到这个布局,我的思路很清晰,当然也想当然的认为这样实现没有问题,因为之前也遇到过类似的布局,虽然没这个复杂,但是最后都解决了。

首先整个页面肯定是一个Activity的布局,下方的底部导航栏就暂且忽略,不是本文的重点,我们需要关注的就是中间这个页面(就是一个Fragment)的布局,由于整个页面是可滑动的,所以布局最外层肯定是一个ScrollView,布局最上面是一个标题栏,这个没啥好说的,标题栏下面是一个导航栏,导航栏的实现也不陌生了,就是一个TabLayout,导航栏下面是内容区,内容区可左右滑动,很明显,内容区可使用ViewPager实现,在内容区里,是一条条的文章数据,这个我的实现思路是使用RecyclerView,虽然你会发现有些布局项和其它不同,但是没关系,我们可以通过适配器中的viewType来控制,然后导航栏和内容区的联动效果可使用TabLayoutsetupWithViewPager方法直接实现联动效果。

上面的思路实现了整体的布局效果,但是一些小细节还没有实现:

细节一TabLayout滑到顶部时,悬浮在顶部,而标题栏直接滑出屏幕,这个效果要实现,主要是实现一个滑动监听, 我们可以监听最外层ScrollView的滑动,根据参数来判断TabLayout是否滑到了屏幕顶部,如果滑到了顶部,则在布局中移除原TabLayout,然后可以在屏幕顶部位置写一个空的顶部布局,再将TabLayout添加到这个空的布局中。反之,如果ScrollView滑动到了顶部,此时应该将TabLayout从顶部布局中移除,将TabLayout回归原位,涉及到的方法主要有addViewremoveView
细节二:整体布局任意位置下滑时,如果标题栏之前被滑出了屏幕,那么会随下滑而逐渐出现在屏幕顶部,这个效果,我最初没有去实现,因为需求中没有这个,但是我后来发现了这个细节效果,现在的思路也是和上面的一样,监听ScrollView的滑动,根据滑动参数判断上滑还是下滑,如果是下滑的话,那么直接将标题栏布局添加到顶部布局,并根据下滑的距离慢慢将标题栏布局滑出来

2.2 尝试写代码实现

首先根据上面的整体布局思路来一步步实现,至于下面的两个细节问题,先放一放,待会再来实现,ok,有了整体思路,我们不难写出如下代码:

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:id="@+id/container_normal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <RelativeLayout
            android:id="@+id/top_info"
            android:layout_width="match_parent"
            android:layout_height="60dp">

            <TextView
                android:id="@+id/tv"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:text="我是标题栏"
                android:textSize="18sp" />
        </RelativeLayout>

        <android.support.design.widget.TabLayout
            android:id="@+id/tabLayout"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:background="@android:color/white"
            app:tabSelectedTextColor="@android:color/black"
            app:tabMode="fixed"/>

        <android.support.v4.view.ViewPager
            android:id="@+id/viewPager"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>

    </LinearLayout>

</ScrollView>

现在我们尝试往这个布局中添加简单的内容,看有没有什么问题出现,为了简化,我就给ViewPager弄三个简易的Fragment,每个Fragment布局如下

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="第一个页面"
        android:textSize="20sp" />
</RelativeLayout>

对应的java代码如下

public class OneFragment extends Fragment {
   
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, 
    									@Nullable Bundle savedInstanceState) {
   
        return inflater.inflate(R.layout.fragment_one,container,false);
    }
}

我们创建三个这样的Fragment用于内容填充,接下来就是给tabLayoutviewPager设置内容,由于不是重点,我就不废话,直接放上Activity中的相关代码,如下

public class MainActivity extends AppCompatActivity {
   

    private TabLayout tabLayout;
    private ViewPager viewPager;
    private FragmentPagerAdapter mPageAdapter;
    private ArrayList<String> titleList = new ArrayList<>();
    private ArrayList<Fragment> fragmentList=new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
   
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tabLayout=findViewById(R.id.tabLayout);
        viewPager=findViewById(R.id.viewPager);

        titleList.clear();
        titleList.add("标签一");
        titleList.add("标签二");
        titleList.add("标签三");

        fragmentList.clear();
        fragmentList.add(new OneFragment());
        fragmentList.add(new TwoFragment());
        fragmentList.add(new ThreeFragment());

        mPageAdapter=new FragmentPagerAdapter(getSupportFragmentManager()) {
   
            @Override
            public Fragment getItem(int i) {
   
                return fragmentList.get(i);
            }

            @Nullable
            @Override
            public CharSequence getPageTitle(int position) {
   
                return titleList.get(position);
            }

            @Override
            public long getItemId(int position) {
   
                return position;
            }

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

        viewPager.setAdapter(mPageAdapter);
        tabLayout.setupWithViewPager(viewPager);
    }
}

最终运行出来的效果如下
在这里插入图片描述
ok,可以明显的看到,我们明明设置了ViewPager而且设置了内容,但是却发现ViewPager没有显示出来,第一个问题出现了,究其原因就是我们在ScrollView中嵌套了ViewPager,导致ViewPager的高度计算不正确,所以我们可以通过重写ViewPageronMeasure方法来重新计算高度,除此之外,还有一个简单的解决办法,就是给ScrollView设置fillViewport属性为true,这个属性的作用就是让子布局中的内容铺满全屏,ok,设置了该属性之后,我们运行看效果
在这里插入图片描述
接下来我们继续丰富其中的内容, 将Fragment中的内容更改为RecyclerView的列表,看看有没有什么问题,Fragment布局很简单,就一个RecyclerView,我们看Fragment对应的java代码,如下

public class OneFragment extends Fragment {
   

    private RecyclerView mRecyclerView;
    private RecyclerView.LayoutManager mLayoutManager;
    private RecyclerViewAdapter mAdapter;
    private View mainView;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
   
        mainView=inflater.inflate(R.layout.fragment_one,container,false);
        initView();
        return mainView;
    }

    private void initView(){
   
        mRecyclerView=mainView.findViewById(R.id.recyclerview);
        mLayoutManager=new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false);
        ArrayList<String> data=new ArrayList<>();
        for(int i=0;i<20;i++){
   
            data.add("列表项"+i);
        }
        mAdapter=new RecyclerViewAdapter(getActivity(),data);
        mRecyclerView.setLayoutManager(mLayoutManager);
        mRecyclerView.setAdapter(mAdapter);
    }
}

适配器代码如下

public class 
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值