前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android从零撸美团(四) - 美团首页布局解析及实现 - Banner+自定义View+SmartRefreshLayout下拉刷新上拉加载更多

Android从零撸美团(四) - 美团首页布局解析及实现 - Banner+自定义View+SmartRefreshLayout下拉刷新上拉加载更多

作者头像
solocoder
发布2022-04-06 13:03:47
1.3K0
发布2022-04-06 13:03:47
举报
文章被收录于专栏:大前端客栈

这是【从零撸美团】系列文章第四篇。 项目地址:https://github.com/cachecats/LikeMeiTuan

仿美团开源项目整体架构和首页其实早就完成了,前段时间家里各种事情搞得心力交瘁,停更了一段时间。甚至一度动摇继续这个项目的决心,因为最近在学前端,在技术的深度和广度之间一直纠结摇摆不定。一个声音是继续完成这个项目,把安卓玩的更深入一些;另一个声音是赶紧学前端吧,抓紧拓展技术栈,不要在这个项目上浪费太多精力。 思来想去还是继续完成项目吧,自己开的项目跪着也要走完 〒▽〒 最后确定了继续写项目和学前端同时进行的战略方针~

老规矩,先上图,再 分析原理 --> 准备材料 --> 具体实现 三步走一步步的搞定。

在这里插入图片描述
在这里插入图片描述

一、分析

相比于普通的应用,美团、去哪儿这样的平台性 App 的首页还是相当复杂的,简直想把全世界都包进去~

在这里插入图片描述
在这里插入图片描述

刚开始看可能觉得眼花缭乱,但仔细观察,可以把它抽象成六个模块:

  1. 最上面的轮播广告条,里面包含若干个广告图片自动无限轮播。暂时称之为 Banner(注意这几个模块起的英文名对应着代码中的模块名)。
  2. 轮播条下面的美食、电影/演出、酒店住宿、休闲娱乐、外卖等五个大模块入口,暂时称之为大模块 BigModule。
  3. 再往下类似 GridView 的两排小图标,KTV、周边游……暂时称之为小模块 SmallModule。
  4. 小模块下面四张广告图片,乍一看是没有规则的瀑布布局,其实是互相对齐的简单规则布局。暂时称之为 HomeAdsView。
  5. 最后就是列表 RecyclerView 了,显示附近团购信息。
  6. 还有一个不太明显的,上拉刷新下拉加载更多,也算一个模块吧。

抽丝剥茧后就是这六个模块啦,是不是一下清爽很多?

实现思路

轮播条选用了第三方的库:Banner, 有 5.2k 颗 star,非常优秀的库。

大模块 BigModule 采用代码中动态添加 View 的方式实现,好处在于能快速响应变化,假如需求变成一行放4个图标,只需要在 java 文件中改一句代码就好,不用修改资源文件。

两行小模块 SmallModule 是 RecyclerView 实现的 GridView。

四张广告图片 HomeAdsView 是封装的自定义 View,高度封装优点是完全解耦,简化了主页的布局,使用配置简单,后期维护方便。

最下面的列表用的是 RecyclerView,BaseRecyclerViewAdapterHelper 作为辅助。

下拉刷新组件用的是 SmartRefreshLayout

二、准备

主页中用到了三个框架,在 app/build.gradle 下添加如下依赖:

代码语言:javascript
复制
//Banner
implementation "com.youth.banner:banner:1.4.10"
//BaseRecyclerViewAdapterHelper
implementation "com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.30"
//SmartRefreshLayout
implementation "com.scwang.smartrefresh:SmartRefreshLayout:1.0.4"

注:AndroidStudio 3.0 以上用 implementation,3.0以下用 compile。 项目中还用到了很多其他库,如 Dagger、RxJava、ButterKnife、Glide 等,就不一一贴出来了,具体的使用方式请自行查阅资料或看本项目源码 https://github.com/cachecats/LikeMeiTuan

三、实现

项目采用 MVP 架构,主页代码在 app/home 目录下的 HomeFragmentHomeFragmentPresenter 中。

布局文件是 fragment_home.xml,布局代码如下:

代码语言:javascript
复制
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white"
    android:orientation="vertical">

&lt;!--下拉刷新组件--&gt;
&lt;com.scwang.smartrefresh.layout.SmartRefreshLayout
    android:id=<span class="hljs-string">"@+id/smartRefreshLayout_home"</span>
    android:layout_width=<span class="hljs-string">"match_parent"</span>
    android:layout_height=<span class="hljs-string">"match_parent"</span>&gt;

    &lt;ScrollView
        android:layout_width=<span class="hljs-string">"match_parent"</span>
        android:layout_height=<span class="hljs-string">"match_parent"</span>&gt;

        &lt;LinearLayout
            android:layout_width=<span class="hljs-string">"match_parent"</span>
            android:layout_height=<span class="hljs-string">"match_parent"</span>
            android:orientation=<span class="hljs-string">"vertical"</span>&gt;

            &lt;!--Banner轮播条--&gt;
            &lt;com.youth.banner.Banner
                android:id=<span class="hljs-string">"@+id/home_banner"</span>
                android:layout_width=<span class="hljs-string">"match_parent"</span>
                android:layout_height=<span class="hljs-string">"100dp"</span>
                app:image_scale_type=<span class="hljs-string">"center_crop"</span>
                app:scroll_time=<span class="hljs-string">"500"</span> /&gt;

            &lt;!--5个大模块布局--&gt;
            &lt;LinearLayout
                android:id=<span class="hljs-string">"@+id/ll_big_module_fragment_home"</span>
                android:layout_width=<span class="hljs-string">"match_parent"</span>
                android:layout_height=<span class="hljs-string">"wrap_content"</span>
                android:layout_marginBottom=<span class="hljs-string">"15dp"</span>
                android:layout_marginTop=<span class="hljs-string">"15dp"</span>
                android:orientation=<span class="hljs-string">"horizontal"</span> /&gt;

            &lt;!--分割线--&gt;
            &lt;View
                android:layout_width=<span class="hljs-string">"match_parent"</span>
                android:layout_height=<span class="hljs-string">"1dp"</span>
                android:layout_marginLeft=<span class="hljs-string">"20dp"</span>
                android:layout_marginRight=<span class="hljs-string">"20dp"</span>
                android:background=<span class="hljs-string">"@color/dividerColorF0"</span> /&gt;

            &lt;!--两行小模块布局 RecyclerView实现的GridView --&gt;
            &lt;android.support.v7.widget.RecyclerView
                android:id=<span class="hljs-string">"@+id/recyclerview_little_module"</span>
                android:layout_width=<span class="hljs-string">"match_parent"</span>
                android:layout_height=<span class="hljs-string">"wrap_content"</span>
                android:layout_marginBottom=<span class="hljs-string">"10dp"</span> /&gt;

            &lt;!--四个广告封装的自定义View--&gt;
            &lt;com.cachecats.meituan.widget.HomeAdsView
                android:id=<span class="hljs-string">"@+id/home_ads_view"</span>
                android:layout_width=<span class="hljs-string">"match_parent"</span>
                android:layout_height=<span class="hljs-string">"wrap_content"</span> /&gt;

            &lt;!--团购列表--&gt;
            &lt;android.support.v7.widget.RecyclerView
                android:id=<span class="hljs-string">"@+id/recycler_view_shops"</span>
                android:layout_width=<span class="hljs-string">"match_parent"</span>
                android:layout_height=<span class="hljs-string">"wrap_content"</span> /&gt;

        &lt;/LinearLayout&gt;
    &lt;/ScrollView&gt;
&lt;/com.scwang.smartrefresh.layout.SmartRefreshLayout&gt;

</LinearLayout>
布局解析

最外层用 LinearLayout 包裹,接下来是下拉刷新组件 SmartRefreshLayout,因为要实现整个主页的刷新。然后是滚动组件 ScrollView,因为要整体滑动。由于 ScrollView 里只能包含一个子 View,所以在里面又包了层 LinearLayout 。接下来就是五个分模块的具体布局啦。

1. Banner轮播条

添加Banner依赖后,在布局文件中添加 Banner布局,并设置控件高度、图片裁剪模式、滚动时间等参数,然后在 HomeFragment 中初始化:

代码语言:javascript
复制
public void initBanner() {
        //设置banner的各种属性
        banner.setBannerStyle(BannerConfig.CIRCLE_INDICATOR)
                .setImageLoader(new GlideImageLoader())
                .setImages(presenter.getBannerImages()) //从Presenter中取出图片资源
                .setBannerAnimation(Transformer.Default)
                .isAutoPlay(true)
                .setDelayTime(3000)
                .setIndicatorGravity(BannerConfig.CENTER)
                .start();
    }

HomeFragmentPresenter

代码语言:javascript
复制
   /**
     * 获取Banner的图片资源
     *
     * @return
     */
    @Override
    public List<Integer> getBannerImages() {
        List<Integer> mBannerImages = new ArrayList<>();
        mBannerImages.add(R.mipmap.banner1);
        mBannerImages.add(R.mipmap.banner2);
        mBannerImages.add(R.mipmap.banner3);
        mBannerImages.add(R.mipmap.banner4);
        mBannerImages.add(R.mipmap.banner5);
        mBannerImages.add(R.mipmap.banner6);
        return mBannerImages;
    }

另外如果想增加体验的话,可以在生命周期的 onStart 方法中开启自动播放,在 onStop 方法中关闭自动播放。

代码语言:javascript
复制
    @Override
    public void onStart() {
        super.onStart();
        //增加banner的体验
        banner.startAutoPlay();
    }

@Override
public void <span class="hljs-function"><span class="hljs-title">onStop</span></span>() {
    super.onStop();
    //增加banner的体验
    banner.stopAutoPlay();
}

Banner 的官方文档中有详细使用方法。

2. 大模块 BigModule 实现

在主页布局中用一个 LinearLayout 作为占位,并确定这个模块的位置。具体的内容在代码中动态添加,方便后期维护修改。 因为做了高度的封装,所以代码多些,但用起来很方便。 先上代码吧:

HomeFragment 是 View 层,按 MVP 分层思想,不应包含具体的逻辑,所以只向外暴露一个共有方法,用于添加自定义 View IconTitleView 到 占位的 LinearLayout

代码语言:javascript
复制
    /**
     * 往根布局上添加View
     */
    @Override
    public void addViewToBigModule(IconTitleView iconTitleView) {
        llBigModule.addView(iconTitleView);
    }

具体的添加逻辑在 HomeFragmentPresenter 中:

代码语言:javascript
复制
    //大模块的图片数组
    private static final int[] bigModuleDrawables = {
            R.mipmap.homepage_icon_light_food_b,
            R.mipmap.homepage_icon_light_movie_b,
            R.mipmap.homepage_icon_light_hotel_b,
            R.mipmap.homepage_icon_light_amusement_b,
            R.mipmap.homepage_icon_light_takeout_b,
    };

//大模块的标题数组
private static final String[] bigMudoleTitles = {
        <span class="hljs-string">"美食"</span>, <span class="hljs-string">"电影/演出"</span>, <span class="hljs-string">"酒店住宿"</span>, <span class="hljs-string">"休闲娱乐"</span>, <span class="hljs-string">"外卖"</span>
};

/**
 * 初始化banner下面的5个大模块
 */
private void <span class="hljs-function"><span class="hljs-title">initBigModule</span></span>() {
    <span class="hljs-keyword">for</span> (int i = 0; i &lt; 5; i++) {
        IconTitleView iconTitleView = IconTitleView.newInstance(mContext, bigModuleDrawables[i], bigMudoleTitles[i]);
        // 设置宽高和权重weight,使每个View占用相同的宽度
        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.WRAP_CONTENT,
                LinearLayout.LayoutParams.WRAP_CONTENT, 1.0f);
        iconTitleView.setLayoutParams(lp);

        // 往根布局上添加View
        mFragment.addViewToBigModule(iconTitleView);

        //给View添加点击事件
        int finalI = i;
        iconTitleView.setOnClickListener((view) -&gt; {
            Logger.d(bigMudoleTitles[finalI]);
            ToastUtils.show(bigMudoleTitles[finalI]);
        });
    }
}

图片和对应的文字都是写好的,分别放在 bigModuleDrawablesbigMudoleTitles 数组中。 这个模块放了五个图标,所以用了 for 循环五次,每次按下标取出上面两个数组中存入的图片和文字资源,通过

代码语言:javascript
复制
IconTitleView iconTitleView = IconTitleView.newInstance(mContext, bigModuleDrawables[i], bigMudoleTitles[i]);

实例化一个 IconTitleView 对象,并添加到 LinearLayout上

代码语言:javascript
复制
// 往根布局上添加View
mFragment.addViewToBigModule(iconTitleView);

注意这几行代码:

代码语言:javascript
复制
// 设置宽高和权重weight,使每个View占用相同的宽度
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
        LinearLayout.LayoutParams.WRAP_CONTENT,
        LinearLayout.LayoutParams.WRAP_CONTENT, 1.0f);
iconTitleView.setLayoutParams(lp);

一定要给每个 iconTitleView 设置权重,这样才会让5个图标占用相同的宽度。

自定义 View IconTitleView 的实现:
代码语言:javascript
复制
package com.cachecats.meituan.widget;

import android.content.Context;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.cachecats.meituan.R;
import butterknife.BindView;
import butterknife.ButterKnife;
/**

上图片下标题的简单分模块布局自定义View

 */
public class IconTitleView extends LinearLayout {
@BindView(R.id.iv_icon_title)
ImageView iv;
@BindView(R.id.tv_icon_title)
TextView tv;

private Context context;

public IconTitleView(Context context) {
    this(context, null, 0);
    this.context = context;
}

public IconTitleView(Context context, @Nullable AttributeSet attrs) {
    this(context, attrs, 0);
}

public IconTitleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);

    View view = View.inflate(context, R.layout.view_icon_title, this);
    ButterKnife.bind(view);

}

public static IconTitleView newInstance(Context context, int imageResource, String title) {
    IconTitleView iconTitleView = new IconTitleView(context);
    iconTitleView.setImageView(imageResource);
    iconTitleView.setTitleText(title);
    <span class="hljs-built_in">return</span> iconTitleView;
}

private void <span class="hljs-built_in">set</span>ImageView(int drawable) {
    Glide.with(context).load(drawable).into(iv);
}

private void <span class="hljs-built_in">set</span>TitleText(String title) {
    tv.setText(title);
}

}

IconTitleView的布局:

代码语言:javascript
复制
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:gravity="center"
    >

    <ImageView
        android:id="@+id/iv_icon_title"
        android:layout_width="50dp"
        android:layout_height="50dp"
        />

    <TextView
        android:id="@+id/tv_icon_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@color/gray666"
        android:textSize="12sp"
        />

</LinearLayout>

这个是组合自定义View,比较简单,就不多说啦。

3. 两行图标的小模块 SmallModule

RecyclerView 实现的 GridView 布局,直接上代码吧。

代码语言:javascript
复制
    /**
     * 初始化小模块的RecyclerView
     */
    private void initLittleModuleRecyclerView() {

    GridLayoutManager gridLayoutManager = new GridLayoutManager(getActivity(), 5);
    //设置LayoutManager
    littleModuleRecyclerView.setLayoutManager(gridLayoutManager);
    //设置分割器
    littleModuleRecyclerView.addItemDecoration(new HomeGridDecoration(12));
    //设置动画
    littleModuleRecyclerView.setItemAnimator(new DefaultItemAnimator());

    //设置Adapter
    List&lt;IconTitleModel&gt; iconTitleModels = presenter.getIconTitleModels();
    LittleModuleAdapter littleModuleAdapter = new LittleModuleAdapter(
            R.layout.view_icon_title_small, iconTitleModels);

    littleModuleRecyclerView.setAdapter(littleModuleAdapter);
    //设置item点击事件
    littleModuleAdapter.setOnItemClickListener(new BaseQuickAdapter.<span class="hljs-function"><span class="hljs-title">OnItemClickListener</span></span>() {
        @Override
        public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
            ToastUtils.show(iconTitleModels.get(position).getTitle());
        }
    });

}

LittleModuleAdapter.java

代码语言:javascript
复制
public class LittleModuleAdapter extends BaseQuickAdapter<IconTitleModel, BaseViewHolder> {

    private List<IconTitleModel> list;

    public LittleModuleAdapter(int layoutResId, @Nullable List<IconTitleModel> data) {
        super(layoutResId, data);
        list = data;
    }

    @Override
    protected void convert(BaseViewHolder helper, IconTitleModel item) {
        //设置图片
        helper.setImageResource(R.id.iv_icon_title, item.getIconResource());
        //设置标题
        helper.setText(R.id.tv_icon_title, item.getTitle());
    }
}

都是 RecyclerView 的基本知识,就不再赘述了。

4. 四个广告封装的 HomeAdsView

HomeAdsView.java

代码语言:javascript
复制
public class HomeAdsView extends LinearLayout {

@BindView(R.id.ads_1)
ImageView ads1;
@BindView(R.id.ads_2)
ImageView ads2;
@BindView(R.id.ads_3)
ImageView ads3;
@BindView(R.id.ads_4)
ImageView ads4;

public HomeAdsView(Context context) {
    this(context, null, 0);
}

public HomeAdsView(Context context, @Nullable AttributeSet attrs) {
    this(context, attrs, 0);
}

public HomeAdsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    View view = View.inflate(context, R.layout.view_home_ads, this);
    ButterKnife.bind(view);
}

@OnClick({R.id.ads_1, R.id.ads_2, R.id.ads_3, R.id.ads_4})
public void onViewClicked(View view) {
    switch (view.getId()) {
        <span class="hljs-keyword">case</span> R.id.ads_1:
            onAdsClickListener.onAds1Click();
            <span class="hljs-built_in">break</span>;
        <span class="hljs-keyword">case</span> R.id.ads_2:
            onAdsClickListener.onAds2Click();
            <span class="hljs-built_in">break</span>;
        <span class="hljs-keyword">case</span> R.id.ads_3:
            onAdsClickListener.onAds3Click();
            <span class="hljs-built_in">break</span>;
        <span class="hljs-keyword">case</span> R.id.ads_4:
            onAdsClickListener.onAds4Click();
            <span class="hljs-built_in">break</span>;
    }
}

/**
 * 设置广告的资源id,从左到右从上到下依次排列
 * 加载本地图片
 *
 * @param list
 */
public void <span class="hljs-built_in">set</span>AdsResource(List&lt;Integer&gt; list) {
    <span class="hljs-keyword">if</span> (list == null || list.size() != 4) {
        <span class="hljs-built_in">return</span>;
    }
    Glide.with(this).load(list.get(0)).into(ads1);
    Glide.with(this).load(list.get(1)).into(ads2);
    Glide.with(this).load(list.get(2)).into(ads3);
    Glide.with(this).load(list.get(3)).into(ads4);
}

/**
 * 设置广告的资源id,从左到右从上到下依次排列
 * 加载网络图片
 *
 * @param list
 */
public void <span class="hljs-built_in">set</span>AdsUrl(List&lt;String&gt; list) {
    <span class="hljs-keyword">if</span> (list == null || list.size() != 4) {
        <span class="hljs-built_in">return</span>;
    }
    Glide.with(this).load(list.get(0)).into(ads1);
    Glide.with(this).load(list.get(1)).into(ads2);
    Glide.with(this).load(list.get(2)).into(ads3);
    Glide.with(this).load(list.get(3)).into(ads4);
}

private OnAdsClickListener onAdsClickListener;

public interface OnAdsClickListener {
    void onAds1Click();

    void onAds2Click();

    void onAds3Click();

    void onAds4Click();
}

public void <span class="hljs-built_in">set</span>OnAdsClickListener(OnAdsClickListener onAdsClickListener) {
    this.onAdsClickListener = onAdsClickListener;
}

}

view_home_ads.xml

代码语言:javascript
复制
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">

    <ImageView
        android:id="@+id/ads_1"
        android:layout_width="120dp"
        android:layout_height="240dp"
        android:src="@mipmap/ads_1"
        android:layout_margin="2dp"
        android:scaleType="fitStart"
        />

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="240dp"
        android:layout_weight="1"
        android:orientation="vertical">

        <ImageView
            android:id="@+id/ads_2"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:src="@mipmap/ads_2"
            android:layout_margin="2dp"
            android:scaleType="fitStart"
            />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1">

            <ImageView
                android:id="@+id/ads_3"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:src="@mipmap/ads_3"
                android:layout_margin="2dp"
                android:scaleType="fitStart"
                />

            <ImageView
                android:id="@+id/ads_4"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:src="@mipmap/ads_4"
                android:layout_margin="2dp"
                android:scaleType="fitStart"
                />

        </LinearLayout>
    </LinearLayout>
</LinearLayout>

向外暴露设置图片资源和Url地址的方法,并提供点击事件接口。用起来很简单:

代码语言:javascript
复制
private void initAds() {
        homeAdsView.setOnAdsClickListener(new HomeAdsView.OnAdsClickListener() {
            @Override
            public void onAds1Click() {
                ToastUtils.show("Ads1");
            }

            @Override
            public void onAds2Click() {
                ToastUtils.show("Ads2");
            }

            @Override
            public void onAds3Click() {
                ToastUtils.show("Ads3");
            }

            @Override
            public void onAds4Click() {
                ToastUtils.show("Ads4");
            }
        });
    }

因为图片是写死的,这里只实现了点击事件回调。

5.团购信息列表

这个也是个普通的 RecyclerView,里面牵扯到数据库操作,就不在这里贴代码啦。 注意个问题,RecyclerViewScrollView 滑动会有冲突,需要特殊处理下,处理方法:

代码语言:javascript
复制
LinearLayoutManager lm = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false) {
            @Override
            public boolean canScrollVertically() {
                return false;
            }
        };
 rvShopList.setLayoutManager(lm);

通过设置 LinearLayoutManager 禁止RecyclerView 垂直方向上滑动。

6.下拉刷新加载更多

SmartRefreshLayout 实现的,它的官方文档写的很详细,本文重点在于解读主页,具体框架使用就不多说啦。

以上就是对美团首页布局分析及实现的过程,前四个模块说的比较详细,牵扯到自定义View的封装。其实不封装直接写也行,但为了后期维护起来不被人骂,还是多花点精力封装下吧。 团购信息列表和下拉刷新主要是普通的 RecyclerView 用法和框架整合,这类文章比较多,不明白的可以自行查阅相关资料。

源码地址:https://github.com/cachecats/LikeMeiTuan

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018/11/03 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、分析
    • 实现思路
    • 二、准备
    • 三、实现
      • 布局解析
        • 1. Banner轮播条
          • 2. 大模块 BigModule 实现
            • 自定义 View IconTitleView 的实现:
          • 3. 两行图标的小模块 SmallModule
            • 4. 四个广告封装的 HomeAdsView
              • 5.团购信息列表
                • 6.下拉刷新加载更多
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档