玩Android项目开发7-----项目页面(使用ViewPager 2 + TabLayout实现项目页面)

在项目页面,主要包括2个模块,分别为导航页面和项目页面,因此我考虑使用TabLayout和ViewPager来实现,这也是我在项目中第一次引进TabLayout + ViewPager的结合UI框架。

1、ViewPager 2简介

在我们自定义Banner时,使用到的控件是ViewPager,在去年的谷歌大会上,推出的控件,来代替之前的ViewPager。它跟ViewPager不同的是,它支持两种滑动方式:垂直滑动和水平滑动。

public static final int ORIENTATION_HORIZONTAL = RecyclerView.HORIZONTAL;
public static final int ORIENTATION_VERTICAL = RecyclerView.VERTICAL;

我自己的理解就是水平的RecyclerView和竖直的RecyclerView,在水平滑动时,其实每个页面就相当于一个RecyclerViewItem,这样理解在后面适配器设置中就会理解了。

除了方向上不同之外,在设置适配器时,传统的ViewPager需要的是PageAdapter,我们需要自己去填充页面,然后放进Container;ViewPager2继承是RecyclerView的适配器,创建ViewHolder来绑定每个Item页面。

说一下ViewPager2ViewPager有什么不同之处;之前在ViewPagerFragment之间,通过FragmentStatePagerAdapter来联系起来,现在是用FragmentStateAdapter

vp2_main.setAdapter(new FragmentStateAdapter(this) {
            @NonNull
            @Override
            public Fragment createFragment(int position) {
                return fragments.get(position);
            }

            @Override
            public int getItemCount() {
                return fragments.size();
            }
        });

之前监听ViewPager的滑动监听,使用addPageChangeListener来做页面的滑动监听,现在修改为registerOnPageChangeCallback

 vp2_main.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
            @Override
            public void onPageSelected(int position) {
                bnb_main.selectTab(position);
            }
        });

2、TabLayout简介

<com.google.android.material.tabs.TabLayout
        android:id="@+id/tab_tour"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:tabIndicatorColor="@color/colorOrange"
        app:tabMode="scrollable"
        app:tabIndicatorGravity="bottom"
        app:tabIndicatorHeight="2dp"
        ></com.google.android.material.tabs.TabLayout>

我直接对照布局说一下属性就好了,这个使用过的应该都了解过:
tabIndicatorColor:指示器的颜色
background:TabLayout的背景
tabIndicatorHeight:设置指示器的厚度
tabMode:设置为可滑动的
tabIndicatorGravity:设置指示器的位置,这里设置为下方

3、TabLayout + ViewPager2的使用

(1)添加依赖

implementation 'androidx.viewpager2:viewpager2:1.0.0'
implementation 'com.google.android.material:material:1.2.0-alpha04'

(2)布局

 <com.google.android.material.tabs.TabLayout
        android:id="@+id/tab_tour"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:tabIndicatorColor="@color/colorOrange"
        app:tabMode="scrollable"
        app:tabIndicatorGravity="bottom"
        app:tabIndicatorHeight="2dp"
        ></com.google.android.material.tabs.TabLayout>

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/vp2_tour"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        ></androidx.viewpager2.widget.ViewPager2>

(3)在将ViewPager 2和TabLayout关联之前,先要给ViewPager2设置适配器,不然,会报错:TabLayoutMediator attached before ViewPager2 has an adapter

 @Override
    public void showTour(List<TourBean.DataBean> data) {
        List<String> titleList = new ArrayList<>();
        for (int i = 0; i < titles.length; i++) {
            titleList.add(titles[i]);
            //之前介绍过,两个模块:导航和项目
        }

        pjAdapter = new ProjectViewPagerAdapter(getContext(), titleList, data);
        vp2_project.setAdapter(pjAdapter);

        TabLayoutMediator mediator = new TabLayoutMediator(tab_title, vp2_project, new TabLayoutMediator.TabConfigurationStrategy() {
            @Override
            public void onConfigureTab(@NonNull TabLayout.Tab tab, int position) {
                //设置tab标题
                tab.setText(titleList.get(position));
                Log.e("TAG","tab position===="+position);
            }
        });
        mediator.attach();
    }

在设置适配器之后,调用TabLayoutMediator 来将TabLayout和ViewPager 2绑定:

public TabLayoutMediator(
      @NonNull TabLayout tabLayout,
      @NonNull ViewPager2 viewPager,
      @NonNull TabConfigurationStrategy tabConfigurationStrategy) {
    this(tabLayout, viewPager, true, tabConfigurationStrategy);
  }

onConfigureTab方法中,来设置Tab的名称,position是0 - 1;

接下来看看适配器源码:

public class ProjectViewPagerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private final Context context;
    private final List<String> titleList;
    //导航
    private static final int TOUR = 0;
    //项目
    private static final int PROJECT = 1;
    //当前的item
    private int currentType = TOUR;
    //导航栏数据
    private final List<TourBean.DataBean> tourList;
    //Tour的适配器
    private TourItemAdapter tourAdapter;

    public  ProjectViewPagerAdapter(Context context, List<String> titleList, List<TourBean.DataBean> tourList){
        this.context = context;
        this.titleList = titleList;
        this.tourList = tourList;
    }
    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        if (viewType == TOUR) {
            View view = LayoutInflater.from(context).inflate(R.layout.item_tour, parent, false);
            return new TourViewHolder(view);
        }else if(viewType == PROJECT){
            View view = LayoutInflater.from(context).inflate(R.layout.item_project, parent, false);
            return new ProjectViewHolder(view);
        }
        return null;
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        if(holder instanceof ProjectViewHolder){
            Log.e("TAG","position====="+position);
            ((ProjectViewHolder) holder).tv_project_title.setText(titleList.get(position));
        }else if(holder instanceof TourViewHolder){
            //绑定导航界面的数据。
        }
    }

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

    class ProjectViewHolder extends RecyclerView.ViewHolder{
        private TextView tv_project_title;
        public ProjectViewHolder(@NonNull View itemView) {
            super(itemView);
            tv_project_title = itemView.findViewById(R.id.tv_project_title);
        }
    }

    class TourViewHolder extends RecyclerView.ViewHolder{
        private TabLayout tab_tour;
        private ViewPager2 vp2_tour;
        public TourViewHolder(@NonNull View itemView) {
            super(itemView);
            tab_tour = itemView.findViewById(R.id.tab_tour);
            vp2_tour = itemView.findViewById(R.id.vp2_tour);
        }
    }

    @Override
    public int getItemViewType(int position) {
        switch (position){
            case TOUR:
                currentType = TOUR;
                break;
            case PROJECT:
                currentType = PROJECT;
                break;
        }
        return currentType;
    }
}

之前在说过,ViewPager2的适配器是继承自RecyclerViewAdapter,每个Item都是一个模块,是两个不同的类型,因此通过getItemViewType来根据position判断当前是哪个Item,如果position= 0,也就是TOUR ,那么我就去加载导航页面的布局,返回导航页面的holder,下面就会根据holder去绑定相应的数据。

比如说,我在导航界面,还是想做TabLayout + ViewPager2的UI结构,那么就在导航界面的布局中加入这个结构,同样的,也是给ViewPager2设置适配器,然后传入数据。

else if(holder instanceof TourViewHolder){
            tourAdapter = new TourItemAdapter(context,tourList);
            ((TourViewHolder) holder).vp2_tour.setAdapter(tourAdapter);

            TabLayoutMediator mediator = new TabLayoutMediator(((TourViewHolder) holder).tab_tour,
                    ((TourViewHolder) holder).vp2_tour, new TabLayoutMediator.TabConfigurationStrategy() {
                @Override
                public void onConfigureTab(@NonNull TabLayout.Tab tab, int position) {
                    tab.setText(tourList.get(position).getName());
                }
            });
            mediator.attach();

ViewPager的适配器源码先放在这吧。

public class TourItemAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private final Context context;
    private final List<TourBean.DataBean> tourList;

    public TourItemAdapter(Context context, List<TourBean.DataBean> tourList){
        this.context = context;
        this.tourList = tourList;
    }
    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view= LayoutInflater.from(context).inflate(R.layout.item_tour_adapter,parent,false);
        return new TourItemViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        if(holder instanceof TourItemViewHolder){
            List<TourBean.DataBean.ArticlesBean> articles = tourList.get(position).getArticles();
            for (int i = 0; i < articles.size(); i++) {
                TextView tv = new TextView(context);
                String title = articles.get(i).getTitle();
                tv.setText(title);
                LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                        ViewGroup.LayoutParams.WRAP_CONTENT);
                tv.setLayoutParams(params);
                ((TourItemViewHolder) holder).fl_tour.addView(tv);
            }
        }
    }

    @Override
    public int getItemCount() {
        return tourList.size();
    }

    class TourItemViewHolder extends RecyclerView.ViewHolder{
        private FlexboxLayout fl_tour;
        public TourItemViewHolder(@NonNull View itemView) {
            super(itemView);
            fl_tour = itemView.findViewById(R.id.fl_tour);
        }
    }

}

就是将每个Tab下的数据,放进FlexBoxLayout容器中(发现我独爱FlexBoxLayout)。

看下效果:
在这里插入图片描述
但是滑了一会,有出现了之前RecyclerView的数据错乱的现象,现在应该知道了,还是缓存的问题:
在这里插入图片描述
我说一下ViewPager的缓存机制,在ViewPager中,默认的缓存数是1,就是当前页面的左右两边的页面缓存,一共是3个,当我们滑动页面之后,移出缓存区的页面再次进入需要重新绑定数据,因此又出现这个错乱问题,所以自己设置一下缓存数即可。

((TourViewHolder) holder).vp2_tour.setOffscreenPageLimit(tourList.size()/2);

看一下源码就知道了

 public void setOffscreenPageLimit(@OffscreenPageLimit int limit) {
        if (limit < 1 && limit != OFFSCREEN_PAGE_LIMIT_DEFAULT) {
            throw new IllegalArgumentException(
                    "Offscreen page limit must be OFFSCREEN_PAGE_LIMIT_DEFAULT or a number > 0");
        }
        mOffscreenPageLimit = limit;
        // Trigger layout so prefetch happens through getExtraLayoutSize()
        mRecyclerView.requestLayout();
    }

这样的话问题就解决了。

我贴出来的图片一般只是看运行成功与否,后期的UI设计美化大家可以自己做。

4、TabLayout的属性使用

(1)tabMode
在上面的布局设计中,tabMode采用的是“fixed”,那么因为只有两个Tab,所以就一人一边平分;tabMode还有其他的属性,例如“auto

        app:tabMode="auto"

这样设置,Tab的位置就改变了。
在这里插入图片描述
(2)设置Tab中的文字字体

这个在属性API中没有设置Tab的字体的值,因此需要自己去动态设置。

tab_title.addOnTabSelectedListener(new TabTextSizeListener());
 private class TabTextSizeListener implements TabLayout.OnTabSelectedListener {
        @Override
        public void onTabSelected(TabLayout.Tab tab) {
            TextView textView = new TextView(getContext());
            textView.setTextSize(20);
            textView.setText(tab.getText());
            tab.setCustomView(textView);
        }

        @Override
        public void onTabUnselected(TabLayout.Tab tab) {
            //这个必须得设置,不然会一直叠加
            tab.setCustomView(null);
        }

        @Override
        public void onTabReselected(TabLayout.Tab tab) {

        }
    }

在这里插入图片描述
好了,TabLayout就先介绍到这里,后续会继续更新!

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Awesome_lay

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值