在开发的过程中我们需要实现一些UI效果图,如果UI界面比较简单,清晰,我们就按正常的思路写布局即可。但是如果面对比较复杂UI界面,我们还按正常的思路从上到下使用一些常用的控件,可能代码逻辑就会很复杂。另外如果后续迭代进行局部或者大范围的更改,可能会很头疼,看也得看半天。比如看下面的例子:
上面两个图是比较复杂的,如果我们用RecyclerView也能实现或者ListView ,HorizontalScrollView等一些控件直接组合实现也不是不行,但是Type类型太多会导致代码比较复杂,不利于以后的维护。今天就给大家介绍一个很容易就能实现这种复杂的UI的框架MultiType。
思路:就像ListView中可以有一种类型的ItemView,也可以有多种类型的ItemView,而MultiType也是可以存在很多个不同的Item布局。他俩的区别就是ListView中的各种类型的Item布局是直接写在ListView内部就行内部类一样,而MultiType的ItemView布局则是像插件一样注册到MutiType中,每一个ItemView(布局包括逻辑)之间相互独立的,每个ItemView只需要关心自己的布局对应着很么类型的数据,会有什么样的逻辑即可。下面就通过第一幅图来说说怎么使用:
我们将UI分成5种类型的ItemView,然后各自去实现它。
1.写一个Bean类,即是第一个ItemView中的数据。
再写一个MyBannerView0类继承ItemViewProvider (为了清晰,我将此Bean0写入对应MyBannerView0中)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="4dp"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/icon0"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@mipmap/ic_launcher" />
<TextView
android:id="@+id/tv0"
android:layout_width="wrap_content"
android:textSize="14sp"
android:layout_height="wrap_content"
android:text="分类" />
</LinearLayout>
public class MyBannerView0 extends ItemViewProvider<MyBannerView0.Bean0, MyBannerView0.ViewHolder0> {
@NonNull
@Override
protected ViewHolder0 onCreateViewHolder(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent) {
View view0 = inflater.inflate(R.layout.item_banner0, parent, false);
return new ViewHolder0(view0);
}
@Override
protected void onBindViewHolder(@NonNull ViewHolder0 holder, @NonNull Bean0 bean0) {
holder.icon0.setImageResource(bean0.ResId);//图片
holder.textView0.setText(bean0.names);//下面的APP名字
}
public class ViewHolder0 extends RecyclerView.ViewHolder {
ImageView icon0;
TextView textView0;
public ViewHolder0(@NonNull View itemView) {
super(itemView);
icon0 = itemView.findViewById(R.id.icon0);
textView0 = itemView.findViewById(R.id.tv0);
}
}
//数据源Bean
public static class Bean0 {
public int ResId;
public String names;
public Bean0(int resId, String names) {
ResId = resId;
this.names = names;
}
}
}
.仿照上面的写第二个item
<?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="160dp"
android:orientation="vertical">
<WebView
android:id="@+id/webView"
android:layout_width="match_parent"
android:layout_height="180dp">
</WebView>
</LinearLayout>
public class MyBannerView1 extends ItemViewProvider<MyBannerView1.Bean1, MyBannerView1.ViewHolder1> {
@NonNull
@Override
protected ViewHolder1 onCreateViewHolder(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent) {
View view1 = inflater.inflate(R.layout.item_banner1, parent, false);
return new ViewHolder1(view1);
}
@Override
protected void onBindViewHolder(@NonNull ViewHolder1 holder, @NonNull Bean1 bean1) {
//这个我们视为WebView控件,可以远程控制其显示
holder.webView.loadUrl(bean1.url);
}
public class ViewHolder1 extends RecyclerView.ViewHolder {
WebView webView;
public ViewHolder1(@NonNull View itemView) {
super(itemView);
webView = itemView.findViewById(R.id.webView);
WebSettings webSettings = webView.getSettings();
//支持缩放,默认为true。
webSettings.setSupportZoom(false);
//调整图片至适合webview的大小
webSettings.setUseWideViewPort(true);
// 缩放至屏幕的大小
webSettings.setLoadWithOverviewMode(true);
//设置默认编码
webSettings.setDefaultTextEncodingName("utf-8");
webSettings.setJavaScriptEnabled(true);
webView.setWebViewClient(new WebViewClient(){
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
handler.proceed();
}
});
}
}
public static class Bean1 {
public String url;
public Bean1(String url) {
this.url = url;
}
}
}
2.然后在Activity中注册这两个ItemView
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycle"
android:layout_width="match_parent"
android:layout_height="match_parent"></android.support.v7.widget.RecyclerView>
</LinearLayout>
public class MainActivity extends AppCompatActivity {
RecyclerView recyclerView;
MultiTypeAdapter multiTypeAdapter;
Items items;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.recycle);
//每一行最多可以显示12个Item
GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 12);
//根据实际情况,每一种类型的Item占据多少权重。比如第一行这种类型的最多有6个,每个占据的权重就是2
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int i) {
if (items.get(i) instanceof MyBannerView0.Bean0) {
return 12 / 6; 每个item占据2个权重
} else if (items.get(i) instanceof MyBannerView1.Bean1) {
return 12 / 1; 每个item占据12权重
} else if (items.get(i) instanceof MyEmptyBannerView.EmptyBean) {
return 12 / 1;
} else if (items.get(i) instanceof MyBannerView2.Bean2) {
return 12 / 1;
} else if (items.get(i) instanceof MyBannerView3.Bean3) {
return 12 / 1;
}
return 12;
}
});
//设置RecycleView
recyclerView.setLayoutManager(gridLayoutManager);
//类似于ArrayList<Object>
items = new Items();
//将每种类型的Item布局,注册到multiTypeAdapter 里面去
multiTypeAdapter = new MultiTypeAdapter(items);
multiTypeAdapter.register(MyBannerView0.Bean0.class, new MyBannerView0());
multiTypeAdapter.register(MyBannerView1.Bean1.class, new MyBannerView1());
multiTypeAdapter.register(ArrayList.class, new MyBannerView2());
multiTypeAdapter.register(MyBannerView3.Bean3.class, new MyBannerView3());
multiTypeAdapter.register(MyEmptyBannerView.EmptyBean.class, new MyEmptyBannerView());
recyclerView.setAdapter(multiTypeAdapter);
//请求数据,刷新数据。此时Adapter会根据item中的数据类型找到对应的布局并显示。
requestData();
}
private void requestData() {
//伪代码,模拟请求。
items.clear();
for (int i = 0; i < 6; i++) {
items.add(new MyBannerView0.Bean0(R.mipmap.ic_launcher, "测试"));
}
items.add(new MyBannerView1.Bean1("http://www.baidu.com/"));
items.add(new MyEmptyBannerView.EmptyBean(null));
items.add(new MyEmptyBannerView.EmptyBean("热门应用"));
ArrayList<MyBannerView2.Bean2> list = new ArrayList<>();
list.add(new MyBannerView2.Bean2(R.mipmap.ic_launcher, "饿了么", ""));
list.add(new MyBannerView2.Bean2(R.mipmap.ic_launcher, "腾讯视频", ""));
list.add(new MyBannerView2.Bean2(R.mipmap.ic_launcher, "美团", ""));
list.add(new MyBannerView2.Bean2(R.mipmap.ic_launcher, "腾讯QQ", ""));
list.add(new MyBannerView2.Bean2(R.mipmap.ic_launcher, "微信", ""));
list.add(new MyBannerView2.Bean2(R.mipmap.ic_launcher, "抖音", ""));
list.add(new MyBannerView2.Bean2(R.mipmap.ic_launcher, "高德地图", ""));
list.add(new MyBannerView2.Bean2(R.mipmap.ic_launcher, "百度外卖", ""));
list.add(new MyBannerView2.Bean2(R.mipmap.ic_launcher, "大众点评", ""));
items.add(list);
items.add(new MyEmptyBannerView.EmptyBean(null));
items.add(new MyEmptyBannerView.EmptyBean("热门游戏"));
ArrayList<MyBannerView2.Bean2> list1 = new ArrayList<>();
list1.add(new MyBannerView2.Bean2(R.mipmap.ic_launcher, "王者荣耀", ""));
list1.add(new MyBannerView2.Bean2(R.mipmap.ic_launcher, "皇室战争", ""));
list1.add(new MyBannerView2.Bean2(R.mipmap.ic_launcher, "炫舞", ""));
list1.add(new MyBannerView2.Bean2(R.mipmap.ic_launcher, "一道传世", ""));
list1.add(new MyBannerView2.Bean2(R.mipmap.ic_launcher, "三国群英传", ""));
list1.add(new MyBannerView2.Bean2(R.mipmap.ic_launcher, "火影忍者", ""));
list1.add(new MyBannerView2.Bean2(R.mipmap.ic_launcher, "穿越火线", ""));
list1.add(new MyBannerView2.Bean2(R.mipmap.ic_launcher, "英雄杀送", ""));
list1.add(new MyBannerView2.Bean2(R.mipmap.ic_launcher, "使命召唤", ""));
items.add(list1);
items.add(new MyEmptyBannerView.EmptyBean(null));
items.add(new MyEmptyBannerView.EmptyBean("长路漫漫,旅途相伴"));
for (int i = 0; i < 15; i++) {
items.add(new MyBannerView3.Bean3(R.mipmap.ic_launcher, "穿越火线" + i, "特别好玩的游戏" + i, ""));
}
multiTypeAdapter.notifyDataSetChanged();
}
}
很简单就实现了这个效果:虽然丑,但是很直观。实际开发的时候这些图标啊 文字啊 以及WebView的链接啊 之类的都是接口返回的,链接不对或者打不开的做个默认设置就好。
总结:
优点:最大的优点就是MultiType可以实现各种不同类型布局组合成的复杂界面,每个BannerView之间是解耦的,像插件一样,Adapter会根据item的数据进行匹配对应的BannerView。 让开发者们可以灵活的添加,修改。
重点:
1.分析UI有几种类型的布局,并写出每一个BannerView
2.将BannerView注册到MutiTypeAdapter中去
3.如果每一行显示的个数不一样的,可以使用权重的思想处理。