商城购物项目开发1----仿淘宝首页设计主页面(TabLayout + ViewPager2 + Fragment)+ViewPager的事件冲突解决

从这节开始,主要是对这个商城项目的主页面做设计和开发,根据淘宝的页面结构做参考,设计一款商城购物APP。

先看一下淘宝的首页。
在这里插入图片描述
整体的布局就是这样的,所以先从页面框架开始搭起来。

1、页面整体布局

<androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="30dp"
        app:navigationIcon="@drawable/ic_scan">
        <androidx.appcompat.widget.SearchView
            android:id="@+id/sv_main"
            android:layout_width="200dp"
            android:layout_height="30dp"
            app:queryHint="输入要查询的商品"
            app:iconifiedByDefault="false"
            android:background="@drawable/search_bg"></androidx.appcompat.widget.SearchView>
    </androidx.appcompat.widget.Toolbar>

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tab_main"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/toolbar"
        app:tabIndicatorHeight="3dp"
        app:tabIndicatorGravity="bottom"
        app:tabIndicatorColor="@color/colorRed"
        app:tabMode="scrollable"></com.google.android.material.tabs.TabLayout>
    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/vp2_tab"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@id/tab_main"></androidx.viewpager2.widget.ViewPager2>


</RelativeLayout>

整体的布局还是使用TabLayout + ViewPager2实现,关于这两个控件的具体使用,在之前的项目中已经说过很多了,尤其是ViewPager2,跟之前的ViewPager还是有很多不同的地方。

经过网络请求之后,得到每个Tab的标题,以及每个标题的id,在后续的页面实现中要用到,最终通过ViewPager2和TabLayout的联动之后:

        //获取的Tab数据集合
        data = tabBean.getData();
        Log.e("TAG","data=="+tabBean.getMessage());
		//ViewPager2中嵌套Fragment
        vp2_tab.setAdapter(new FragmentStateAdapter(this) {
            @NonNull
            @Override
            public Fragment createFragment(int position) {
                return fragmentList.get(position);
            }

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

        TabLayoutMediator mediator = new TabLayoutMediator(tab_main, vp2_tab, new TabLayoutMediator.TabConfigurationStrategy() {
            @Override
            public void onConfigureTab(@NonNull TabLayout.Tab tab, int position) {
                tab.setText(data.get(position).getTitle());
            }
        });
        mediator.attach();

其中注意一点:如果使用的是ViewPager,那么需要在ViewPager设置适配器的时候,从写setPageTitle()方法,不然,TabLayout不会显示标题,但是在使用ViewPager2的时候,就不需要了,不会出现这个状况。

看一下效果:
在这里插入图片描述
2、Fragment与Fragment之间的数据传递

在TabLayout的第一个页面是推荐页面,推荐页面的数据需要根据该Tab标题的对应id获取,这就涉及到数据的传递,在首页是一个Fragment页面,推荐页面也是一个Fragment页面,在Fragment之间的数据传递,在之前的章节中有涉及到,可以去《Android模块开发-----用户信息模块(Fragment和Activity之间数据的传递)》,这里使用setArguments方法进行数据传递。

   tab_main.addOnTabSelectedListener(new MyOnTabSelectionListener());
    }

    private class MyOnTabSelectionListener implements TabLayout.OnTabSelectedListener {
        @Override
        public void onTabSelected(TabLayout.Tab tab) {
            Log.e("TAG","position=="+tab.getPosition());
            int position = tab.getPosition();
//            Log.e("TAG","fragment==="+fragmentList.get(tab.getPosition()));
            Bundle bundle = new Bundle();
            bundle.putInt("id",data.get(position).getId());
            fragmentList.get(position).setArguments(bundle);
        }

当点击Tab时,会切换Fragment页面,所以需要获取当前Tab的位置,将数据保存到Bundle中,获取集合中该位置的Fragment,调用setArguments(Bundle bundle)发送数据。

在对应的Fragment页面,调用getArguments得到对应的Bundle数据,获取id值;但也存在特殊情况,在之前玩Android项目公众号模块中,因为TabLayout是默认选中第一个Tab,上述设置方式在第一个Tab不起作用,因此需要得到默认的选中位置,发送数据。

        int curPosition = tab_main.getSelectedTabPosition();
        Log.e("TAG","selectedTabPosition==="+curPosition);
        int id = data.get(curPosition).getId();
        Bundle bundle = new Bundle();
        bundle.putInt("id",id);
        fragmentList.get(curPosition).setArguments(bundle);

3、推荐界面

因为数据源比较多,因此抽取出来几个作为轮播图在首页显示,在之前的章节中介绍过使用ViewPager自定义轮播图,因此在这里就不多说,直接上核心代码了。

推荐界面使用RecyclerView,RecyclerView实现多种布局方式,因此轮播图作为其中一种:

if(holder instanceof BannerViewHolder){
            //banner adapter
            for (int i = 0; i < 5; i++) {
                ImageView imageView = new ImageView(context);
                String pict_url = data.get(i).getPict_url();
                Glide.with(context).load("http:"+pict_url).centerCrop()
                        .into(imageView);
                LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                        150);
                imageView.setLayoutParams(params);
                imageViewList.add(imageView);
            }
            bannerAdapter = new BannerAdapter2(context,imageViewList);
            ((BannerViewHolder) holder).vp_banner.setAdapter(bannerAdapter);
            Log.e("TAG","position == "+position);
        }

效果:
在这里插入图片描述
实现之后,因为在外层有一个ViewPager与定义轮播图的ViewPager产生了滑动冲突,导致轮播图在滑动时,事件(DOWN事件)被外层ViewPager消费了,导致在轮播图处的点击事件无法响应,因此需要处理滑动冲突。

滑动冲突的处理,分为两种:一种是外部处理,通常是在父容器处理,判断是否对事件拦截,在onInterceptTouchEvent方法中处理;另一种是内部处理,在子容器中判断父容器能否拦截该事件,在dispatchTouchEvent方法中处理。滑动冲突设计到事件分发机制,之后拿出时间再写一篇关于事件分发的博客,这里就把事件冲突的处理先贴出来吧,逻辑也挺简单的。

public class BannerView extends ViewPager {

    public BannerView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }
    private float x1;
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()){
            case MotionEvent.ACTION_DOWN:
                x1 = ev.getX();
                //不允许父类消费事件
                getParent().requestDisallowInterceptTouchEvent(true);
                break;
            case MotionEvent.ACTION_MOVE:
                //得到当前的位置下标
                int currentItem = getCurrentItem();
                float x2 = ev.getX();
                //如果当前是第一页,还是向左滑动(手指的方向)-----滑动到第二页,那么父类不能拦截
                if(currentItem == 0){
                    if((x1-x2)>10){
                        getParent().requestDisallowInterceptTouchEvent(true);
                    }else{
                        //第一页向右滑动,那就交给父类处理
                        getParent().requestDisallowInterceptTouchEvent(false);
                    }
                }else if(currentItem == (getAdapter().getCount() - 1)){
                    //如果是最后一页,还是向左滑动--交给父容器处理事件
                    if((x1-x2) > 10){
                        getParent().requestDisallowInterceptTouchEvent(false);
                    }else{
                        getParent().requestDisallowInterceptTouchEvent(true);
                    }
                }else{
                    //其他页面,都是给ViewPager消费
                    getParent().requestDisallowInterceptTouchEvent(true);
                }
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        return super.dispatchTouchEvent(ev);
    }

}

这样使用自定义的View代替ViewPager,因为本来就继承了ViewPager,只是重写了ViewPager的事件分发机制,这样滑动冲突就解决了。

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
实现 ViewPager2+TabLayout+Fragment 实现页面切换,需要以下步骤: 1. 在 XML 布局文件中定义 ViewPager2 和 TabLayout,并将它们嵌套在一个父布局中。 ```xml <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <com.google.android.material.tabs.TabLayout android:id="@+id/tab_layout" android:layout_width="match_parent" android:layout_height="wrap_content" app:tabMode="scrollable" app:tabGravity="center"/> <androidx.viewpager2.widget.ViewPager2 android:id="@+id/view_pager" android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintTop_toBottomOf="@id/tab_layout" app:layout_constraintBottom_toBottomOf="parent"/> </androidx.constraintlayout.widget.ConstraintLayout> ``` 2. 创建 Fragment,并实现 ViewPager2 的适配器。 ```kotlin class MyFragmentAdapter(fragmentActivity: FragmentActivity) : FragmentStateAdapter(fragmentActivity) { private val fragmentList = listOf( FirstFragment(), SecondFragment(), ThirdFragment() ) override fun getItemCount() = fragmentList.size override fun createFragment(position: Int) = fragmentList[position] } ``` 3. 在 Activity 或 Fragment 中,初始化 ViewPager2 和 TabLayout,并将适配器设置给 ViewPager2。 ```kotlin val viewPager: ViewPager2 = findViewById(R.id.view_pager) val tabLayout: TabLayout = findViewById(R.id.tab_layout) val adapter = MyFragmentAdapter(this) viewPager.adapter = adapter TabLayoutMediator(tabLayout, viewPager) { tab, position -> tab.text = "Tab ${position + 1}" }.attach() ``` 这样就可以实现 ViewPager2+TabLayout+Fragment 实现页面切换了。注意,TabLayoutMediator 是用来关联 TabLayoutViewPager2 的,它的第一个参数是 TabLayout,第二个参数是 ViewPager2,第三个参数是一个回调函数,用来设置 TabLayout 的标签文本。在最后一行调用 attach() 方法即可完成关联。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Awesome_lay

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

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

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

打赏作者

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

抵扣说明:

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

余额充值