鸿蒙的ListContainer多布局实现,不需要编写Provider,只需要编写条目对应的Holder即可,省下不少代码
1.多布局接口定义
需要实现多布局需要实现该接口
public interface Mult {
//返回多布局类型
int mult();
}
2.BaseProvider
public class BaseProvider<T> extends BaseItemProvider {
private List<T> data;
private Context context;
protected boolean enableMult = false;
protected ArrayList<Class<ViewHolder<T>>> holders;
protected HashMap<Class<T>,Class<ViewHolder<T>>> map;
protected int multCount = 1;
/**
* 注册holder
* @param holder
*/
public BaseProvider<T> register(Class<T> pojo,Class<ViewHolder<T>> holder){
if(holders == null){
holders = new ArrayList<>();
map = new HashMap<>();
}
holders.add(holder);
map.put(pojo,holder);
return this;
}
/**
* 是否允许多布局
* @param enableMult
* @return
*/
public BaseProvider<T> mult(boolean enableMult){
this.enableMult = enableMult;
return this;
}
public BaseProvider(Context context) {
super();
this.context = context;
data = new ArrayList<>();
}
public BaseProvider(Context context,List<T> data) {
super();
this.data = data;
this.context = context;
}
public void refreshData(List<T> data){
setData(data);
notifyDataChanged();
}
public void setData(List<T> data){
this.data = data;
}
public void more(List<T> more){
this.data.addAll(more);
notifyDataChanged();
}
@Override
public int getCount() {
return data.size();
}
@Override
public T getItem(int i) {
return data.get(i);
}
public void setMultCount(int count) {
this.multCount = count;
}
@Override
public long getItemId(int pos) {
return pos;
}
@Override
public int getItemComponentType(int position) {
T data = getItem(position);
if(data instanceof Mult){
Mult mult = (Mult)data;
Class holderClass = map.get(data.getClass());
return holderClass.hashCode()+mult.mult();
}
return position;
}
@Override
public int getComponentTypeCount() {
return multCount;
}
@Override
public Component getComponent(int pos, Component component, ComponentContainer componentContainer) {
T itemData = data.get(pos);
//单布局
Class<ViewHolder<T>> holderClass = map.get(itemData.getClass());
if(holderClass == null){
throw new RuntimeException("请先注册holder和数据类型");
}
T data = itemData;
ViewHolder<T> holder = ViewHolder.<T>get(context,component,data,pos,holderClass);
return holder.getRootComponent();
}
}
3.ViewHolder
public abstract class ViewHolder<Data> {
protected HashMap<Integer, Component> mComponents;
protected Component mRootComponent;
protected Context context;
protected int layoutId;
protected int position;
protected LayoutScatter layoutScatter;
public ViewHolder(Context context) {
super();
this.context = context;
if(layoutScatter == null){
layoutScatter = LayoutScatter.getInstance(context);
}
mComponents = new HashMap<>();
}
public void setLayout(Data data){
layoutId = getLayoutId(data,position);
this.mRootComponent = layoutScatter.parse(layoutId,null,false);
mRootComponent.setTag(this);
findComponent(data,position);
}
public static <T> ViewHolder<T> get(Context context,Component convertView,T data,int pos,Class holderClass) {
ViewHolder holder = null;
try {
if(convertView == null){
Constructor<ViewHolder<T>> declaredConstructor = holderClass.getDeclaredConstructor(Context.class);
holder = declaredConstructor.newInstance(context);
holder.position = pos;
holder.setLayout(data);
}else{
holder = (ViewHolder)convertView.getTag();
holder.position = pos;
}
}catch (Exception e){
e.printStackTrace();
}
holder.convert(data,pos);
return holder;
}
public Component getRootComponent(){
return mRootComponent;
}
public abstract void convert(Data data,int position);
/**
* 通过componentId获取控件
*
* @param viewId
* @return
*/
public <T extends Component> T getComponent(int viewId) {
Component view = mComponents.get(viewId);
if (view == null) {
view = mRootComponent.findComponentById(viewId);
mComponents.put(viewId, view);
}
return (T) view;
}
protected abstract int getLayoutId(Data data,int position);
protected abstract void findComponent(Data data,int position);
}
4. 还有我们辅助生成provider的工具类
public class ProviderCreator {
public static class Builder<T>{
private BaseProvider<T> provider;
public Builder(Context context) {
super();
provider = new BaseProvider<>(context);
}
public Builder<T> register(Class pojo, Class holder){
provider.register(pojo,holder);
return this;
}
public Builder<T> setData(List<T> data){
provider.setData(data);
return this;
}
public Builder<T> multCount(int count){
provider.setMultCount(count);
return this;
}
public BaseProvider<T> create(){
return provider;
}
}
}
接下里就可以愉快的使用啦
一.首先是单布局的使用,就是最常见的用法
List的条目对象类
public class Normal {
public Normal(String text) {
this.text = text;
}
public String text;
}
条目对应的holder !!! 一个类必须对应一个holder
public class NormalHolder extends ViewHolder<Normal> {
private Text tv;
public NormalHolder(Context context) {
super(context);
}
/**
* 具体的实现
* @param normal
* @param position
*/
@Override
public void convert(Normal normal, int position) {
tv.setText(normal.text);
}
/**
* 返回对应的布局
* @param normal
* @param position
* @return
*/
@Override
protected int getLayoutId(Normal normal, int position) {
return ResourceTable.Layout_item_normal;
}
/**
* 查找控件
* @param normal
* @param position
*/
@Override
protected void findComponent(Normal normal, int position) {
tv = getComponent(ResourceTable.Id_item_normal_tv1);
}
}
在Slice中使用
/**
* 最基础用法 单布局
* @param list
*/
public void normal(ListContainer list){
BaseProvider<Normal> provider = new ProviderCreator.Builder<Normal>(getAbility())
//注册之前创建Bean和Holder对象 !!!必须注册不然崩溃
.register(Normal.class, NormalHolder.class)
//set数据
.setData(MapUtil.<Normal>list().adds(
new Normal("1"),
new Normal("2"),
new Normal("3"),
new Normal("4"),
new Normal("5")
)).create();
//给ListContainer设置provider
list.setItemProvider(provider);
}
运行效果
二.单对象的多布局实践
我们会遇到相同的数据类型,但是显示的内容不一致的情况
直接上代码
定义数据类型News 实现Mult接口
public class News implements Mult {
public News(int style, Text title, List<String> images) {
this.style = style;
this.title = title;
this.images = images;
}
private int style; //显示类型
private Text title; //标题
private List<String> images; //图片列表
@Override
public int mult() {
return style;
}
}
定义NewsHolder,并创建两个不同的xml布局
public class NewsHolder extends ViewHolder<News> {
private Text title;
private Image image1;
private Image image2;
public NewsHolder(Context context) {
super(context);
}
@Override
public void convert(News news, int position) {
//因为2个布局标题是同一个id 所以可以直接设置 无需判断
title.setText(news.title);
if(news.mult() == 1){
Glide.with(context)
.load(news.images.get(0))
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(image1);
}else{
Glide.with(context)
.load(news.images.get(0))
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(image1);
Glide.with(context)
.load(news.images.get(1))
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(image2);
}
}
@Override
protected int getLayoutId(News news, int position) {
//根据mult返回不同的布局
if(news.mult() == 1){
//右边有图
return ResourceTable.Layout_item_news1;
}else{
//下面有图
return ResourceTable.Layout_item_news2;
}
}
@Override
protected void findComponent(News news, int position) {
//类似
if(news.mult() == 1){
}else{
}
//因为2个布局标题是同一个id 所以可以只查找一次
title = getComponent(ResourceTable.Id_item_news_tv);
image1 = getComponent(ResourceTable.Id_item_news_image);
image2 = getComponent(ResourceTable.Id_item_news_image2);
}
}
两个xml布局如下
item_news1.xml
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:height="match_content"
ohos:width="match_parent"
ohos:orientation="horizontal">
<Text
ohos:id="$+id:item_news_tv"
ohos:background_element="#ffffff"
ohos:height="100vp"
ohos:text_size="20vp"
ohos:text_color="#000000"
ohos:width="match_content"/>
<Image
ohos:id="$+id:item_news_image"
ohos:height="100vp"
ohos:width="100vp"/>
</DirectionalLayout>
item_news2.xml
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:height="match_content"
ohos:width="match_parent"
ohos:orientation="vertical">
<Text
ohos:id="$+id:item_news_tv"
ohos:background_element="#ffffff"
ohos:height="100vp"
ohos:text_size="20vp"
ohos:text_color="#000000"
ohos:width="match_parent"/>
<DirectionalLayout
ohos:orientation="horizontal"
ohos:height="match_content"
ohos:width="match_parent">
<Image
ohos:id="$+id:item_news_image"
ohos:height="100vp"
ohos:width="100vp"/>
<Image
ohos:id="$+id:item_news_image2"
ohos:height="100vp"
ohos:width="100vp"/>
</DirectionalLayout>
</DirectionalLayout>
最后一步 在slice中使用~
###
List<String> style1List = new ArrayList<>();
style1List.add("https://img03.sogoucdn.com/app/a/100520093/8379901cc65ba509-45c21ceb904429fc-e8e0ced72c7814e527ca276e0fe48673.jpg");
List<String> style2List = new ArrayList<>();
style2List.add("https://img03.sogoucdn.com/app/a/100520093/ae588be27ee085c4-fd668f66a830d70e-60b57587f934a25debc8247d3769d0ce.jpg");
style2List.add("https://img03.sogoucdn.com/app/a/100520093/ae588be27ee085c4-fd668f66a830d70e-bcb76412aab683a7d1f972c04a769065.jpg");
List<News> newsList = MapUtil.<News>list().adds(
new News(1,"第一条重大新闻",style1List),
new News(2,"第二条重大新闻",style2List),
new News(1,"第三条重大新闻",style1List),
new News(1,"第四条重大新闻",style1List),
new News(1,"第五条重大新闻",style1List),
new News(2,"第六条重大新闻",style2List),
new News(1,"第一条重大新闻",style1List),
new News(2,"第二条重大新闻",style2List),
new News(1,"第三条重大新闻",style1List),
new News(1,"第四条重大新闻",style1List),
new News(1,"第五条重大新闻",style1List),
new News(2,"第六条重大新闻",style2List),
new News(1,"第一条重大新闻",style1List),
new News(2,"第二条重大新闻",style2List),
new News(1,"第三条重大新闻",style1List),
new News(1,"第四条重大新闻",style1List),
new News(1,"第五条重大新闻",style1List),
new News(2,"第六条重大新闻",style2List)
);
BaseProvider<News> provider = new ProviderCreator.Builder<News>(getAbility())
//注册之前创建Bean和Holder对象 !!!必须注册不然崩溃
.register(News.class, NewsHolder.class)
//set数据
.setData(newsList)
.create();
//给ListContainer设置provider
list.setItemProvider(provider);
显示效果如下
3.多类型多布局的使用
如上面新闻列表中有广告的插入 而广告的数据类型一定不是News,而且广告的显示内容可能也是多种多样的,在这里我们只需要多注册一个广告类型并添加相应的广告数据即可实现,无需任何别的代码
直接上代码,在刚才的基础上新增加广告类和广告holder
广告类
public class Ad implements Mult {
public Ad(String adContent, int adStyle) {
this.adContent = adContent;
this.adStyle = adStyle;
}
public String adContent;//广告内容
public int adStyle; //显示类型
//如果没有其他返回类型,返回0即可
@Override
public int mult() {
return adStyle;
}
}
广告holder
public class AdHolder extends ViewHolder<Ad> {
private Text title;
public AdHolder(Context context) {
super(context);
}
@Override
public void convert(Ad ad, int position) {
title.setText(ad.adContent);
}
@Override
protected int getLayoutId(Ad ad, int position) {
if(ad.mult() == 1){
return ResourceTable.Layout_item_ad1;
}else{
return ResourceTable.Layout_item_ad2;
}
}
@Override
protected void findComponent(Ad ad, int position) {
title = getComponent(ResourceTable.Id_item_ad_tv);
}
}
item_ad1.xml
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:height="match_content"
ohos:width="match_parent"
ohos:orientation="horizontal">
<Text
ohos:id="$+id:item_ad_tv"
ohos:background_element="#ffffff"
ohos:height="100vp"
ohos:text_size="20vp"
ohos:text_color="#ff00ff"
ohos:width="match_content"/>
</DirectionalLayout>
item_ad2.xml
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:height="match_content"
ohos:width="match_parent"
ohos:orientation="horizontal">
<Text
ohos:id="$+id:item_ad_tv"
ohos:background_element="#ffffff"
ohos:height="100vp"
ohos:text_size="50vp"
ohos:text_color="#ff00ff"
ohos:width="match_content"/>
</DirectionalLayout>
slice中使用
List<String> style1List = new ArrayList<>();
style1List.add("https://img03.sogoucdn.com/app/a/100520093/8379901cc65ba509-45c21ceb904429fc-e8e0ced72c7814e527ca276e0fe48673.jpg");
List<String> style2List = new ArrayList<>();
style2List.add("https://img03.sogoucdn.com/app/a/100520093/ae588be27ee085c4-fd668f66a830d70e-60b57587f934a25debc8247d3769d0ce.jpg");
style2List.add("https://img03.sogoucdn.com/app/a/100520093/ae588be27ee085c4-fd668f66a830d70e-bcb76412aab683a7d1f972c04a769065.jpg");
List<Mult> newsList = MapUtil.<Mult>list().adds(
new News(1,"第一条重大新闻",style1List),
new News(2,"第二条重大新闻",style2List),
new Ad("广告1",1),
new Ad("广告2",2),
new News(1,"第三条重大新闻",style1List),
new News(1,"第四条重大新闻",style1List),
new News(1,"第五条重大新闻",style1List),
new News(2,"第六条重大新闻",style2List)
);
BaseProvider<Mult> provider = new ProviderCreator.Builder<Mult>(getAbility())
//注册之前创建Bean和Holder对象 !!!必须注册不然崩溃
.register(News.class, NewsHolder.class)
.register(Ad.class, AdHolder.class)
//set数据
.setData(newsList)
.create();
//给ListContainer设置provider
list.setItemProvider(provider);
效果如图