使用Vlayout打造淘宝首页

先上效果图

     淘宝首页是从上到下是各种不同的样式,最上面是搜索,其次是一个轮播图,再下来是10个圆角的菜单,等等,我们可以采用一个recyclerView实现,但是实现起来的复杂程度是比较高的,如果使用阿里开源的VLayout控件,实现起来则简单多了,Vlayout就适用于这种多种item的布局。

官方文档

    详细的介绍可以参考官网文档,中文版:https://github.com/alibaba/vlayout/blob/master/README-ch.md

代码实现

    先来看主页面的xml布局,为了简单把搜索栏用图片代替,下面是一个RecyclerView

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	xmlns:tools="http://schemas.android.com/tools"
	android:layout_width="match_parent"
	android:layout_height="match_parent"
	android:orientation="vertical"
	tools:context=".MainActivity">

	<ImageView
		android:layout_width="match_parent"
		android:layout_height="48dp"
		android:scaleType="centerCrop"
		android:src="@mipmap/img_title_bar" />

	<androidx.recyclerview.widget.RecyclerView
		android:id="@+id/rv"
		android:layout_width="match_parent"
		android:layout_height="match_parent" />

</LinearLayout>

完了我们对recyclerView先进行初始化,设置LayoutManager,平时我们使用的系统自带的,这里我们使用VirtualLayoutManager,并设置回收复用池大小,(如果一屏内相同类型的 View 个数比较多,需要设置一个合适的大小,防止来回滚动时重新创建 View)。

        recyclerView = findViewById(R.id.rv);
        VirtualLayoutManager virtualLayoutManager = new VirtualLayoutManager(this);
        recyclerView.setLayoutManager(virtualLayoutManager);
        RecyclerView.RecycledViewPool viewPool = new RecyclerView.RecycledViewPool();
        viewPool.setMaxRecycledViews(0, 10);
        recyclerView.setRecycledViewPool(viewPool);

    完了就到了加载数据的地方,正常的adaper是继承recyclerView的adapter,使用VLayout的话就要继承BaseDelegeteAdapter,可以像平常一样写继承自DelegateAdapter.Adapter的Adapter, 只比之前的Adapter需要多重载onCreateLayoutHelper方法。 其他的和默认Adapter一样。


public class BaseDelegeteAdapter extends DelegateAdapter.Adapter<BaseViewHolder> {

    private LayoutHelper mLayoutHelper;
    private int mCount = -1;
    private int mLayoutId = -1;
    private int mViewTypeItem = -1;

    public BaseDelegeteAdapter(LayoutHelper layoutHelper, int layoutId, int count) {
        this.mLayoutHelper = layoutHelper;
        this.mCount = count;
        this.mLayoutId = layoutId;
    }

    @Override
    public LayoutHelper onCreateLayoutHelper() {
        return mLayoutHelper;
    }

    @NonNull
    @Override
    public BaseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return new BaseViewHolder(LayoutInflater.from(parent.getContext()).inflate(mLayoutId, parent, false));
    }

    @Override
    public void onBindViewHolder(@NonNull BaseViewHolder holder, int position) {

    }

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

ViewHolder的写法还和之前的一样


public class BaseViewHolder  extends RecyclerView.ViewHolder {
    private SparseArray<View> views;

    public BaseViewHolder(@NonNull View itemView) {
        super(itemView);
        this.views = new SparseArray<>();
    }

    public BaseViewHolder setTextColor(@IdRes int viewId, @ColorInt int textColor) {
        TextView view = getView(viewId);
        view.setTextColor(textColor);
        return this;
    }

    public BaseViewHolder setImageResource(@IdRes int viewId, @DrawableRes int imageResId) {
        ImageView view = getView(viewId);
        view.setImageResource(imageResId);
        return this;
    }

    public BaseViewHolder setText(@IdRes int viewId, CharSequence value) {
        TextView view = getView(viewId);
        view.setText(value);
        return this;
    }

    public <T extends View> T getView(@IdRes int viewId) {
        View view = views.get(viewId);
        if (view == null) {
            view = itemView.findViewById(viewId);
            views.put(viewId, view);
        }
        return (T) view;
    }
}

    到这里跟之前其实都差不多,都是一些初始化的工作,接下来就加载数据了。先是第一个是轮播图,这里使用开源框架 implementation 'com.youth.banner:banner:1.4.10',我们先来创建Banner轮播图的adapter,因为其只有一个元素,所以我们使用LinearLayoutHelper: 线性布局就可以

    private BaseDelegeteAdapter getBannerAdapter() {
        return new BaseDelegeteAdapter(new LinearLayoutHelper(), R.layout.vlayout_banner, 1) {
            @Override
            public void onBindViewHolder(@NonNull BaseViewHolder holder, int position) {
                ArrayList<String> arrayList = new ArrayList<>();
                arrayList.add("https://img.alicdn.com/tps/TB1EMhjIpXXXXaPXVXXXXXXXXXX.jpg");
                arrayList.add("https://img.alicdn.com/simba/img/TB17OFBloD1gK0jSZFGSuvd3FXa.jpg");
                arrayList.add("https://img.alicdn.com/tfs/TB1HSHMlKH2gK0jSZJnXXaT1FXa-520-280.jpg");
                arrayList.add("https://img.alicdn.com/simba/img/TB181dplkL0gK0jSZFxSutWHVXa.jpg");
                arrayList.add("https://img.alicdn.com/simba/img/TB1rWhyleH2gK0jSZFESuwqMpXa.jpg");
                arrayList.add("https://img.alicdn.com/tfs/TB15xIZk7T2gK0jSZFkXXcIQFXa-520-280.png");
                // 绑定数据
                Banner mBanner = holder.getView(R.id.banner);
                //设置banner样式
                mBanner.setBannerStyle(BannerConfig.CIRCLE_INDICATOR);
                //设置图片加载器
                mBanner.setImageLoader(new ImageLoader() {
                    @Override
                    public void displayImage(Context context, Object path, ImageView imageView) {
                        Glide.with(context)
                                .load(path)
                                .into(imageView);
                    }
                });
                //设置图片集合
                mBanner.setImages(arrayList);
                //设置banner动画效果
                mBanner.setBannerAnimation(Transformer.DepthPage);
                //设置标题集合(当banner样式有显示title时)
                //        mBanner.setBannerTitles(titles);
                //设置自动轮播,默认为true
                mBanner.isAutoPlay(true);
                //设置轮播时间
                mBanner.setDelayTime(3000);
                //设置指示器位置(当banner模式中有指示器时)
                mBanner.setIndicatorGravity(BannerConfig.CENTER);
                //banner设置方法全部调用完毕时最后调用
                mBanner.start();
            }
        };
    }

    接下来是10个按钮菜单,这里是上面5个,下面5个,我们采用GridLayoutHelper: Grid布局, 支持横向的colspan

    private BaseDelegeteAdapter getMenuAdapter() {
        GridLayoutHelper gridLayoutHelper = new GridLayoutHelper(5);
        gridLayoutHelper.setPadding(0, 16, 0, 0);
        gridLayoutHelper.setVGap(10);
        gridLayoutHelper.setHGap(0); 控制子元素之间的水平间距
        return new BaseDelegeteAdapter(gridLayoutHelper, R.layout.vlayout_menu, 10) {
            @Override
            public void onBindViewHolder(@NonNull BaseViewHolder holder, final int position) {
                holder.setText(R.id.tv_menu_title_home, ITEM_NAMES[position] + "");
                holder.setImageResource(R.id.iv_menu_home, IMG_URLS[position]);
                holder.getView(R.id.ll_menu_home).setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        Toast.makeText(getApplicationContext(), ITEM_NAMES[position], Toast.LENGTH_SHORT).show();
                    }
                });
            }
        };
    }

在接下来是两个竖直拍的新闻推送,滚动的文字我们也使用开源框架MarqueeView,我们使用LinearLayoutHelper: 线性布局

 

    private BaseDelegeteAdapter getNewsAdapter() {
        return new BaseDelegeteAdapter(new LinearLayoutHelper(), R.layout.vlayout_news, 1) {
            @Override
            public void onBindViewHolder(@NonNull BaseViewHolder holder, int i) {
                MarqueeView marqueeView1 = holder.getView(R.id.marqueeView1);
                MarqueeView marqueeView2 = holder.getView(R.id.marqueeView2);

                List<String> info1 = new ArrayList<>();
                info1.add("天猫超市最近发大活动啦,快来抢");
                info1.add("没有最便宜,只有更便宜!");

                List<String> info2 = new ArrayList<>();
                info2.add("这个是用来搞笑的,不要在意这写小细节!");
                info2.add("啦啦啦啦,我就是来搞笑的!");

                marqueeView1.startWithList(info1);
                marqueeView2.startWithList(info2);
                // 在代码里设置自己的动画
                marqueeView1.startWithList(info1, R.anim.anim_bottom_in, R.anim.anim_top_out);
                marqueeView2.startWithList(info2, R.anim.anim_bottom_in, R.anim.anim_top_out);

                marqueeView1.setOnItemClickListener(new MarqueeView.OnItemClickListener() {
                    @Override
                    public void onItemClick(int position, TextView textView) {
                        Toast.makeText(getApplicationContext(), textView.getText().toString(), Toast.LENGTH_SHORT).show();
                    }
                });
                marqueeView2.setOnItemClickListener(new MarqueeView.OnItemClickListener() {
                    @Override
                    public void onItemClick(int position, TextView textView) {
                        Toast.makeText(getApplicationContext(), textView.getText().toString(), Toast.LENGTH_SHORT).show();
                    }
                });
            }
        };
    }

以此类推,后面的Adapter就不写了,得到各种adapter之后我们需要创建一个DelegateAdapter,并将各种adapter添加到DelegateAdapter,最后给recycler设置adapter设置DelegateAdapter就ok了

        BaseDelegeteAdapter bannerAdapter = getBannerAdapter();
        BaseDelegeteAdapter menuAdapter = getMenuAdapter();
        BaseDelegeteAdapter newsAdapter = getNewsAdapter();

        DelegateAdapter delegateAdapter = new DelegateAdapter(virtualLayoutManager, true);
        delegateAdapter.addAdapter(bannerAdapter);
        delegateAdapter.addAdapter(menuAdapter);
        delegateAdapter.addAdapter(newsAdapter);

        for (int i = 0; i < ITEM_URL.length; i++) {
            final int finalI = i;
            BaseDelegeteAdapter titleAdapter = getTitleAdapter(ITEM_URL[finalI]);
            GridLayoutHelper gridHelper = new GridLayoutHelper(2);
            BaseDelegeteAdapter gridAdapter = getGridAdapter(gridHelper);
            delegateAdapter.addAdapter(titleAdapter);
            delegateAdapter.addAdapter(gridAdapter);
        }

        recyclerView.setAdapter(delegateAdapter);

VLayout的使用就是这么简单,实现复杂的recyclerview布局,下面有MainActivity完整代码,文末附上Demo地址


public class MainActivity extends AppCompatActivity {

    private RecyclerView recyclerView;
    //应用
    String[] ITEM_NAMES = {"天猫", "聚划算", "天猫国际", "外卖", "天猫超市", "充值中心", "飞猪旅行", "领金币", "拍卖", "分类"};
    int[] IMG_URLS = {R.mipmap.ic_tian_mao, R.mipmap.ic_ju_hua_suan, R.mipmap.ic_tian_mao_guoji, R.mipmap.ic_waimai, R.mipmap.ic_chaoshi, R.mipmap.ic_voucher_center, R.mipmap.ic_travel, R.mipmap.ic_tao_gold, R.mipmap.ic_auction, R.mipmap.ic_classify};

    //    高颜值商品位
    int[] ITEM_URL = {R.mipmap.item1, R.mipmap.item2, R.mipmap.item3, R.mipmap.item4, R.mipmap.item5};
    int[] GRID_URL = {R.mipmap.flashsale1, R.mipmap.flashsale2, R.mipmap.flashsale3, R.mipmap.flashsale4};

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

    private void initView() {
        recyclerView = findViewById(R.id.rv);
        VirtualLayoutManager virtualLayoutManager = new VirtualLayoutManager(this);
        recyclerView.setLayoutManager(virtualLayoutManager);
        RecyclerView.RecycledViewPool viewPool = new RecyclerView.RecycledViewPool();
        viewPool.setMaxRecycledViews(0, 10);
        recyclerView.setRecycledViewPool(viewPool);

        BaseDelegeteAdapter bannerAdapter = getBannerAdapter();
        BaseDelegeteAdapter menuAdapter = getMenuAdapter();
        BaseDelegeteAdapter newsAdapter = getNewsAdapter();

        DelegateAdapter delegateAdapter = new DelegateAdapter(virtualLayoutManager, true);
        delegateAdapter.addAdapter(bannerAdapter);
        delegateAdapter.addAdapter(menuAdapter);
        delegateAdapter.addAdapter(newsAdapter);

        for (int i = 0; i < ITEM_URL.length; i++) {
            final int finalI = i;
            BaseDelegeteAdapter titleAdapter = getTitleAdapter(ITEM_URL[finalI]);
            GridLayoutHelper gridHelper = new GridLayoutHelper(2);
            BaseDelegeteAdapter gridAdapter = getGridAdapter(gridHelper);
            delegateAdapter.addAdapter(titleAdapter);
            delegateAdapter.addAdapter(gridAdapter);
        }

        recyclerView.setAdapter(delegateAdapter);
    }

    private BaseDelegeteAdapter getGridAdapter(final GridLayoutHelper gridHelper) {
        return new BaseDelegeteAdapter(gridHelper, R.layout.vlayout_grid, 4) {

            @Override
            public void onBindViewHolder(@NonNull BaseViewHolder holder, final int position) {
                int item = GRID_URL[position];
                ImageView iv = holder.getView(R.id.iv);
                Glide.with(getApplicationContext()).load(item).into(iv);

                iv.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        Toast.makeText(getApplicationContext(), "item" + position, Toast.LENGTH_SHORT).show();
                    }
                });
            }
        };
    }

    private BaseDelegeteAdapter getTitleAdapter(final int imageResId) {
        return new BaseDelegeteAdapter(new LinearLayoutHelper(), R.layout.vlayout_title, 1) {
            @Override
            public void onBindViewHolder(BaseViewHolder holder, int position) {
                super.onBindViewHolder(holder, position);
                holder.setImageResource(R.id.iv, imageResId);
            }
        };
    }


    private BaseDelegeteAdapter getBannerAdapter() {
        return new BaseDelegeteAdapter(new LinearLayoutHelper(), R.layout.vlayout_banner, 1) {
            @Override
            public void onBindViewHolder(@NonNull BaseViewHolder holder, int position) {
                ArrayList<String> arrayList = new ArrayList<>();
                arrayList.add("https://img.alicdn.com/tps/TB1EMhjIpXXXXaPXVXXXXXXXXXX.jpg");
                arrayList.add("https://img.alicdn.com/simba/img/TB17OFBloD1gK0jSZFGSuvd3FXa.jpg");
                arrayList.add("https://img.alicdn.com/tfs/TB1HSHMlKH2gK0jSZJnXXaT1FXa-520-280.jpg");
                arrayList.add("https://img.alicdn.com/simba/img/TB181dplkL0gK0jSZFxSutWHVXa.jpg");
                arrayList.add("https://img.alicdn.com/simba/img/TB1rWhyleH2gK0jSZFESuwqMpXa.jpg");
                arrayList.add("https://img.alicdn.com/tfs/TB15xIZk7T2gK0jSZFkXXcIQFXa-520-280.png");
                // 绑定数据
                Banner mBanner = holder.getView(R.id.banner);
                //设置banner样式
                mBanner.setBannerStyle(BannerConfig.CIRCLE_INDICATOR);
                //设置图片加载器
                mBanner.setImageLoader(new ImageLoader() {
                    @Override
                    public void displayImage(Context context, Object path, ImageView imageView) {
                        Glide.with(context)
                                .load(path)
                                .into(imageView);
                    }
                });
                //设置图片集合
                mBanner.setImages(arrayList);
                //设置banner动画效果
                mBanner.setBannerAnimation(Transformer.DepthPage);
                //设置标题集合(当banner样式有显示title时)
                //        mBanner.setBannerTitles(titles);
                //设置自动轮播,默认为true
                mBanner.isAutoPlay(true);
                //设置轮播时间
                mBanner.setDelayTime(3000);
                //设置指示器位置(当banner模式中有指示器时)
                mBanner.setIndicatorGravity(BannerConfig.CENTER);
                //banner设置方法全部调用完毕时最后调用
                mBanner.start();
            }
        };
    }

    private BaseDelegeteAdapter getMenuAdapter() {
        GridLayoutHelper gridLayoutHelper = new GridLayoutHelper(5);
        gridLayoutHelper.setPadding(0, 16, 0, 0);
        gridLayoutHelper.setVGap(10);
        gridLayoutHelper.setHGap(0); 控制子元素之间的水平间距
        return new BaseDelegeteAdapter(gridLayoutHelper, R.layout.vlayout_menu, 10) {
            @Override
            public void onBindViewHolder(@NonNull BaseViewHolder holder, final int position) {
                holder.setText(R.id.tv_menu_title_home, ITEM_NAMES[position] + "");
                holder.setImageResource(R.id.iv_menu_home, IMG_URLS[position]);
                holder.getView(R.id.ll_menu_home).setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        Toast.makeText(getApplicationContext(), ITEM_NAMES[position], Toast.LENGTH_SHORT).show();
                    }
                });
            }
        };
    }

    private BaseDelegeteAdapter getNewsAdapter() {
        return new BaseDelegeteAdapter(new LinearLayoutHelper(), R.layout.vlayout_news, 1) {
            @Override
            public void onBindViewHolder(@NonNull BaseViewHolder holder, int i) {
                MarqueeView marqueeView1 = holder.getView(R.id.marqueeView1);
                MarqueeView marqueeView2 = holder.getView(R.id.marqueeView2);

                List<String> info1 = new ArrayList<>();
                info1.add("天猫超市最近发大活动啦,快来抢");
                info1.add("没有最便宜,只有更便宜!");

                List<String> info2 = new ArrayList<>();
                info2.add("这个是用来搞笑的,不要在意这写小细节!");
                info2.add("啦啦啦啦,我就是来搞笑的!");

                marqueeView1.startWithList(info1);
                marqueeView2.startWithList(info2);
                // 在代码里设置自己的动画
                marqueeView1.startWithList(info1, R.anim.anim_bottom_in, R.anim.anim_top_out);
                marqueeView2.startWithList(info2, R.anim.anim_bottom_in, R.anim.anim_top_out);

                marqueeView1.setOnItemClickListener(new MarqueeView.OnItemClickListener() {
                    @Override
                    public void onItemClick(int position, TextView textView) {
                        Toast.makeText(getApplicationContext(), textView.getText().toString(), Toast.LENGTH_SHORT).show();
                    }
                });
                marqueeView2.setOnItemClickListener(new MarqueeView.OnItemClickListener() {
                    @Override
                    public void onItemClick(int position, TextView textView) {
                        Toast.makeText(getApplicationContext(), textView.getText().toString(), Toast.LENGTH_SHORT).show();
                    }
                });
            }
        };
    }
}

 

Demo地址:https://github.com/987570437/VLayoutDemo

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值