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

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

先看一下淘宝的首页。
商城购物项目开发1----仿淘宝首页设计主页面(TabLayout + ViewPager2 + Fragment)+ViewPager的事件冲突解决_第1张图片
整体的布局就是这样的,所以先从页面框架开始搭起来。

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的时候,就不需要了,不会出现这个状况。

看一下效果:
商城购物项目开发1----仿淘宝首页设计主页面(TabLayout + ViewPager2 + Fragment)+ViewPager的事件冲突解决_第2张图片
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);
        }

效果:
商城购物项目开发1----仿淘宝首页设计主页面(TabLayout + ViewPager2 + Fragment)+ViewPager的事件冲突解决_第3张图片
实现之后,因为在外层有一个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的事件分发机制,这样滑动冲突就解决了。

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