This is the fourth article in our series. Project address: github.com/cachecats/L…
Android extracts Gradle dependencies from zero to a single file
Android from zero meituan (2) – Imitation Meituan drop-down refresh custom animation
Android from zero meituantuan (3) – Android multi-tab TAB slide toggle – custom View quickly achieve a highly customized package
In fact, the overall architecture and home page of the imitation Meituan open source project have long been completed. Some time ago, I was exhausted by all kinds of things at home and stopped for a period of time. I even wavered my determination to continue this project, because recently I have been torn between the depth and breadth of the technology in front of the school. One voice is to go ahead and finish the project and take Android a little deeper; Another voice is to learn the front end, expand the tech stack, and not waste too much energy on this project.
Think about it or continue to finish the project, their own project to go to the end of the mojo
Finally, the strategic policy of continuing to write projects and learning at the same time is determined
Old rules, the first figure, and then analysis of principle –> preparation of materials -> concrete realization of three steps to go step by step.
A, analysis,
Compared with ordinary apps, the home pages of platform apps like Meituan and Qunar are quite complex, and they almost want to cover the whole world
It may seem dizzying at first, but upon closer inspection, it can be abstracted into six modules:
- The top of the rotation of advertising bar, which contains a number of advertising pictures automatically unlimited rotation. Call it Banner for now (note that the English names for these modules correspond to the module names in the code).
- The following five modules, namely food, film/performance, hotel accommodation, leisure and entertainment, take-out, are temporarily called BigModule.
- Go down to two rows of small ICONS similar to GridView, KTV, peripheral tour…… Call it SmallModule temporarily.
- The four advertising pictures below the small module look like a waterfall layout without rules at first glance, but in fact they are simple regular layouts aligned with each other. I’ll call it HomeAdsView for now.
- Finally, the list RecyclerView, display nearby group purchase information.
- There is also a less obvious, pull-up refresh and pull-down load more, also considered a module.
It is these six modules after the cocooning, is it a lot of refreshing?
Implementation approach
A third-party library, Banner, with 5.2k stars, was selected for the cast bar. It was an excellent library.
BigModule is implemented by dynamically adding views to the code. The advantage is that it can quickly respond to changes. If the requirement becomes 4 ICONS in a row, it only needs to change one line of code in the Java file, without modifying the resource file.
SmallModule is a GridView of RecyclerView.
HomeAdsView is a customized View that is packaged. The advantages of high encapsulation are complete decoupling, simplifying the layout of the home page, simple configuration and convenient maintenance.
The following list with most is RecyclerView, BaseRecyclerViewAdapterHelper as auxiliary.
The drop-down refresh component uses SmartRefreshLayout
2, preparation,
The home page uses three frameworks. Add the following dependencies under app/build.gradle:
//Banner
implementation Com. Youth. "banner, the banner: 1.4.10"
//BaseRecyclerViewAdapterHelper
implementation "Com. Making. CymChad: BaseRecyclerViewAdapterHelper: 2.9.30"
//SmartRefreshLayout
implementation "Com. Scwang. Smartrefresh: SmartRefreshLayout: 1.0.4." "
Copy the code
Note: AndroidStudio 3.0 uses implementation, 3.0 uses compile. Many other libraries are also used in the project, such as Dagger, RxJava, ButterKnife, Glide, etc., will not be posted one by one, please refer to the specific use of information or see the project source github.com/cachecats/L…
Three, implementation,
The project adopts MVP architecture, and the home code is in HomeFragment and HomeFragmentPresenter under app/home directory.
The layout file is fragment_home.xml with the following layout code:
<? 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"> <! - the drop-down refresh component - > <. Com. Scwang smartrefresh. Layout. SmartRefreshLayout android: id ="@+id/smartRefreshLayout_home"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"> <! --> <com.youth.banner.Banner Android :id="@+id/home_banner"
android:layout_width="match_parent"
android:layout_height="100dp"
app:image_scale_type="center_crop"
app:scroll_time="500"/ > <! --> <LinearLayout Android :id="@+id/ll_big_module_fragment_home"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="15dp"
android:layout_marginTop="15dp"
android:orientation="horizontal"/ > <! <View Android :layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:background="@color/dividerColorF0"/ > <! - the two rows of small module layout RecyclerView GridView - > <. Android support. V7. Widget. RecyclerView android: id ="@+id/recyclerview_little_module"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"/ > <! - four advertising packaging custom View - > < com. Cachecats. At meituan. Widget. HomeAdsView android: id ="@+id/home_ads_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"/ > <! - group list - > <. Android support. V7. Widget. RecyclerView android: id ="@+id/recycler_view_shops"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</ScrollView>
</com.scwang.smartrefresh.layout.SmartRefreshLayout>
</LinearLayout>
Copy the code
Layout of the resolution
The outermost LinearLayout is wrapped, followed by a drop-down refresh component, SmartRefreshLayout, because you want to refresh the entire home page. And then there’s the ScrollView component, because it slides in one piece. Since the ScrollView can only contain one sub-view, a layer LinearLayout is wrapped inside it. Next is the detailed layout of the five modules.
1. Banner Banner
After adding the Banner dependency, add the Banner layout to the layout file and set the control height, image cropping mode, scroll time and other parameters, then initialize it in HomeFragment:
public void initBannerSetBannerStyle (bannerconfig.circle_indicator).setimageloader (new GlideImageLoader()) SetImages (presenter. GetBannerImages ()) extracted from the presenter / / picture resources. The setBannerAnimation (Transformer. The Default). IsAutoPlay (true)
.setDelayTime(3000)
.setIndicatorGravity(BannerConfig.CENTER)
.start();
}
Copy the code
HomeFragmentPresenter
/** * get the Banner image resource ** @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;
}
Copy the code
In addition, if you want to increase the experience, you can turn on auto play in the onStart method and turn off auto play in the onStop method of the life cycle.
@Override
public void onStart() { super.onStart(); // Add the banner experience banner.startautoPlay (); } @Override public voidonStop() { super.onStop(); // Add the banner experience banner.stopAutoplay (); }Copy the code
Details about how to use Banner are provided in the official documentation.
2. BigModule BigModule implementation
Use a LinearLayout as a placeholder in the home page layout and locate the module. The specific content is dynamically added in the code to facilitate later maintenance and modification. Because of the high level of encapsulation, there is more code, but it is very easy to use. Let’s start with the code:
The HomeFragment is a View layer, which should not contain specific logic according to the MVP hierarchical idea, so it only exposes a common method to add a custom View IconTitleView to a placeholder LinearLayout
/ / @override public void addViewToBigModule(IconTitleView) { llBigModule.addView(iconTitleView); }Copy the code
The specific add logic is in HomeFragmentPresenter:
Private static final Int [] bigModuleDrawables = {r.map. 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 = {"Food"."Movie/show"."Hotel accommodation"."Leisure and entertainment"."Take-away"}; /** * initializes 5 large modules under banner */ private voidinitBigModule() {
for(int i = 0; i < 5; i++) { IconTitleView iconTitleView = IconTitleView.newInstance(mContext, bigModuleDrawables[i], bigMudoleTitles[i]); // Set the width, height and weight, Make the width of each View to take up the same LinearLayout. LayoutParams lp = new LinearLayout. LayoutParams (LinearLayout. LayoutParams. WRAP_CONTENT, LinearLayout. LayoutParams. WRAP_CONTENT, 1.0 f); iconTitleView.setLayoutParams(lp); / / added to the root layout View mFragment. AddViewToBigModule (iconTitleView); // Add click event to View int finalI = I; iconTitleView.setOnClickListener((view) -> { Logger.d(bigMudoleTitles[finalI]); ToastUtils.show(bigMudoleTitles[finalI]); }); }}Copy the code
Images and corresponding text are written and placed in arrays of bigModuleDrawables and bigMudoleTitles, respectively. This module contains five ICONS, so it uses the for loop five times, each time pressing the index to retrieve the image and text resources stored in the above two arrays, passing
IconTitleView iconTitleView = IconTitleView.newInstance(mContext, bigModuleDrawables[i], bigMudoleTitles[i]);
Copy the code
Instantiate an IconTitleView object and add it to the LinearLayout:
/ / added to the root layout View mFragment. AddViewToBigModule (iconTitleView);Copy the code
Notice these lines of code:
// Set the width, height and weight, Make the width of each View to take up the same LinearLayout. LayoutParams lp = new LinearLayout. LayoutParams (LinearLayout. LayoutParams. WRAP_CONTENT, LinearLayout. LayoutParams. WRAP_CONTENT, 1.0 f); iconTitleView.setLayoutParams(lp);Copy the code
Be sure to weight each iconTitleView so that all five ICONS occupy the same width.
The custom ViewIconTitleView
The implementation of the:
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; /** * Public class IconTitleView extends LinearLayout {@bindView (r.i.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);return iconTitleView;
}
private void setImageView(int drawable) {
Glide.with(context).load(drawable).into(iv);
}
private void setTitleText(String title) { tv.setText(title); }}Copy the code
IconTitleView layout:
<? 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>
Copy the code
So this is a combination of custom views, and it’s a little bit easier, so I won’t say much more.
3. SmallModule with two lines of ICONS
RecyclerView GridView layout, directly on the code.
/** * RecyclerView/private voidinitLittleModuleRecyclerView() { GridLayoutManager gridLayoutManager = new GridLayoutManager(getActivity(), 5); / / set the LayoutManager littleModuleRecyclerView. SetLayoutManager (gridLayoutManager); / / set the divider littleModuleRecyclerView. AddItemDecoration (new HomeGridDecoration (12)); / / set animation littleModuleRecyclerView setItemAnimator (new DefaultItemAnimator ()); / / set the Adapter List < IconTitleModel > iconTitleModels = presenter. GetIconTitleModels (); LittleModuleAdapter littleModuleAdapter = new LittleModuleAdapter( R.layout.view_icon_title_small, iconTitleModels); littleModuleRecyclerView.setAdapter(littleModuleAdapter); / / set the item click event littleModuleAdapter. SetOnItemClickListener (new BaseQuickAdapter.OnItemClickListener() { @Override public void onItemClick(BaseQuickAdapter adapter, View view, int position) { ToastUtils.show(iconTitleModels.get(position).getTitle()); }}); }Copy the code
LittleModuleAdapter.java
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) {// Set image helper.setimageresource (r.i.iv_icon_title, item.geticonresource ()); // Set the title helper.settext (r.i.t.v_icon_title, item.gettitle ()); }}Copy the code
All is the basic knowledge of RecyclerView, no longer redundant.
4. HomeAdsView packaged with four ads
HomeAdsView.java
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()) {
case R.id.ads_1:
onAdsClickListener.onAds1Click();
break;
case R.id.ads_2:
onAdsClickListener.onAds2Click();
break;
case R.id.ads_3:
onAdsClickListener.onAds3Click();
break;
case R.id.ads_4:
onAdsClickListener.onAds4Click();
break; }} /** * set the AD resource ID from left to right from top to bottom ** load local image ** @param list */ public voidsetAdsResource(List<Integer> list) {
if(list == null || list.size() ! = 4) {return; } 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); } /** * set the AD resource id from left to right from top to bottom ** @param list */ public voidsetAdsUrl(List<String> list) {
if(list == null || list.size() ! = 4) {return;
}
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 setOnAdsClickListener(OnAdsClickListener onAdsClickListener) { this.onAdsClickListener = onAdsClickListener; }}Copy the code
view_home_ads.xml
<? 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>
Copy the code
Expose methods for setting image resources and URLS and provide a click-event interface. It’s easy to use:
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"); }}); }Copy the code
Because the image is written dead, only the click event callback is implemented.
5. Group purchase information list
This is also a common RecyclerView, which involves database operations, not here to post code. Note a problem, RecyclerView and ScrollView sliding will conflict, need special treatment, treatment method:
LinearLayoutManager lm = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false) {
@Override
public boolean canScrollVertically() {
return false; }}; rvShopList.setLayoutManager(lm);Copy the code
Disable vertical sliding of RecyclerView by setting the LinearLayoutManager.
6. Pull down refresh to load more
SmartRefreshLayout implementation, its official documentation is very detailed, this article focuses on the interpretation of the home page, the specific framework is not said.
The above is the process of analysis and implementation of meituan home page layout, the first four modules are more detailed, involving the encapsulation of custom View. In fact, do not package direct writing is also ok, but in order to later maintenance is not scolded, or spend more energy to package it. Group purchase information list and drop-down refresh is mainly common RecyclerView usage and framework integration, this kind of article is more, do not understand can refer to the relevant information.
Source code address: github.com/cachecats/L… Welcome to download, welcome to star, welcome to like ~