鴻蒙的ListContainer多布局實(shí)現(xiàn),不需要編寫Provider,只需要編寫條目對(duì)應(yīng)的Holder即可,省下不少代碼
1.多布局接口定義
需要實(shí)現(xiàn)多布局需要實(shí)現(xiàn)該接口
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;
/**
* 注冊(cè)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("請(qǐng)先注冊(cè)holder和數(shù)據(jù)類型");
}
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的條目對(duì)象類
public class Normal {
public Normal(String text) {
this.text = text;
}
public String text;
}
條目對(duì)應(yīng)的holder !!! 一個(gè)類必須對(duì)應(yīng)一個(gè)holder
public class NormalHolder extends ViewHolder<Normal> {
private Text tv;
public NormalHolder(Context context) {
super(context);
}
/**
* 具體的實(shí)現(xiàn)
* @param normal
* @param position
*/
@Override
public void convert(Normal normal, int position) {
tv.setText(normal.text);
}
/**
* 返回對(duì)應(yīng)的布局
* @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中使用
/**
* 最基礎(chǔ)用法 單布局
* @param list
*/
public void normal(ListContainer list){
BaseProvider<Normal> provider = new ProviderCreator.Builder<Normal>(getAbility())
//注冊(cè)之前創(chuàng)建Bean和Holder對(duì)象 !!!必須注冊(cè)不然崩潰
.register(Normal.class, NormalHolder.class)
//set數(shù)據(jù)
.setData(MapUtil.<Normal>list().adds(
new Normal("1"),
new Normal("2"),
new Normal("3"),
new Normal("4"),
new Normal("5")
)).create();
//給ListContainer設(shè)置provider
list.setItemProvider(provider);
}
運(yùn)行效果
image.png
二.單對(duì)象的多布局實(shí)踐
我們會(huì)遇到相同的數(shù)據(jù)類型,但是顯示的內(nèi)容不一致的情況
直接上代碼
定義數(shù)據(jù)類型News 實(shí)現(xiàn)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; //標(biāo)題
private List<String> images; //圖片列表
@Override
public int mult() {
return style;
}
}
定義NewsHolder,并創(chuàng)建兩個(gè)不同的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) {
//因?yàn)?個(gè)布局標(biāo)題是同一個(gè)id 所以可以直接設(shè)置 無需判斷
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) {
//根據(jù)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{
}
//因?yàn)?個(gè)布局標(biāo)題是同一個(gè)id 所以可以只查找一次
title = getComponent(ResourceTable.Id_item_news_tv);
image1 = getComponent(ResourceTable.Id_item_news_image);
image2 = getComponent(ResourceTable.Id_item_news_image2);
}
}
兩個(gè)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())
//注冊(cè)之前創(chuàng)建Bean和Holder對(duì)象 !!!必須注冊(cè)不然崩潰
.register(News.class, NewsHolder.class)
//set數(shù)據(jù)
.setData(newsList)
.create();
//給ListContainer設(shè)置provider
list.setItemProvider(provider);
顯示效果如下
image.png
3.多類型多布局的使用
如上面新聞列表中有廣告的插入 而廣告的數(shù)據(jù)類型一定不是News,而且廣告的顯示內(nèi)容可能也是多種多樣的,在這里我們只需要多注冊(cè)一個(gè)廣告類型并添加相應(yīng)的廣告數(shù)據(jù)即可實(shí)現(xiàn),無需任何別的代碼
直接上代碼,在剛才的基礎(chǔ)上新增加廣告類和廣告holder
廣告類
public class Ad implements Mult {
public Ad(String adContent, int adStyle) {
this.adContent = adContent;
this.adStyle = adStyle;
}
public String adContent;//廣告內(nèi)容
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())
//注冊(cè)之前創(chuàng)建Bean和Holder對(duì)象 !!!必須注冊(cè)不然崩潰
.register(News.class, NewsHolder.class)
.register(Ad.class, AdHolder.class)
//set數(shù)據(jù)
.setData(newsList)
.create();
//給ListContainer設(shè)置provider
list.setItemProvider(provider);
效果如圖
image.png