使用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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章