鸿蒙ListContainer多布局实践

鸿蒙的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);
    }
运行效果
image.png

二.单对象的多布局实践

我们会遇到相同的数据类型,但是显示的内容不一致的情况

直接上代码

定义数据类型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);

显示效果如下


image.png

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);

效果如图


image.png

接下来是对于控件事件的处理

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容