鸿蒙ListContainer多布局实践

鸿蒙的ListContainer多布局实现,不需要编写Provider,只需要编写条目对应的Holder即可,省下不少代码

1.多布局接口定义

需要实现多布局需要实现该接口
public interface Mult {
    //返回多布局类型
    int mult();
}

2.BaseProvider

public class BaseProvider extends BaseItemProvider {

    private List data;
    private Context context;
    protected boolean enableMult = false;
    protected ArrayList>> holders;
    protected HashMap,Class>> map;
    protected int multCount = 1;
    /**
     * 注册holder
     * @param holder
     */
    public BaseProvider register(Class pojo,Class> holder){
        if(holders == null){
            holders = new ArrayList<>();
            map = new HashMap<>();
        }
        holders.add(holder);
        map.put(pojo,holder);
        return this;
    }

    /**
     * 是否允许多布局
     * @param enableMult
     * @return
     */
    public BaseProvider mult(boolean enableMult){
        this.enableMult = enableMult;
        return this;
    }

    public BaseProvider(Context context) {
        super();
        this.context = context;
        data = new ArrayList<>();
    }

    public BaseProvider(Context context,List data) {
        super();
        this.data = data;
        this.context = context;
    }

    public void refreshData(List data){
        setData(data);
        notifyDataChanged();
    }

    public void setData(List data){
        this.data = data;
    }

    public void more(List 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> holderClass = map.get(itemData.getClass());
        if(holderClass == null){
            throw new RuntimeException("请先注册holder和数据类型");
        }
        T data = itemData;
        ViewHolder holder = ViewHolder.get(context,component,data,pos,holderClass);
        return holder.getRootComponent();
    }


}

3.ViewHolder

public abstract class ViewHolder {

    protected HashMap 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  ViewHolder get(Context context,Component convertView,T data,int pos,Class holderClass)  {
        ViewHolder holder = null;
        try {
            if(convertView == null){
                Constructor> 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 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{
        private BaseProvider provider;

        public Builder(Context context) {
            super();
            provider = new BaseProvider<>(context);
        }

        public Builder register(Class pojo, Class holder){
            provider.register(pojo,holder);
            return this;
        }

        public Builder setData(List data){
            provider.setData(data);
            return this;
        }

        public Builder multCount(int count){
            provider.setMultCount(count);
            return this;
        }

        public BaseProvider create(){
            return provider;
        }
    }
}

接下里就可以愉快的使用啦

一.首先是单布局的使用,就是最常见的用法

List的条目对象类

public class Normal {
    public Normal(String text) {
        this.text = text;
    }

    public String text;
}
条目对应的holder !!! 一个类必须对应一个holder
public class NormalHolder extends ViewHolder {

    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 provider = new ProviderCreator.Builder(getAbility())
                //注册之前创建Bean和Holder对象 !!!必须注册不然崩溃
                .register(Normal.class, NormalHolder.class)
                //set数据
                .setData(MapUtil.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 images) {
        this.style = style;
        this.title = title;
        this.images = images;
    }

    private int style; //显示类型
    private Text title; //标题
    private List images; //图片列表

    @Override
    public int mult() {
        return style;
    }
}

定义NewsHolder,并创建两个不同的xml布局
public class NewsHolder extends ViewHolder {

    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


    
    

item_news2.xml


    
  
      
      
  

最后一步 在slice中使用~

###
  List style1List = new ArrayList<>();
        style1List.add("https://img03.sogoucdn.com/app/a/100520093/8379901cc65ba509-45c21ceb904429fc-e8e0ced72c7814e527ca276e0fe48673.jpg");
        List 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 newsList =  MapUtil.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 provider = new ProviderCreator.Builder(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 {

    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



    


item_ad2.xml



    


slice中使用

        List style1List = new ArrayList<>();
        style1List.add("https://img03.sogoucdn.com/app/a/100520093/8379901cc65ba509-45c21ceb904429fc-e8e0ced72c7814e527ca276e0fe48673.jpg");
        List 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 newsList =  MapUtil.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 provider = new ProviderCreator.Builder(getAbility())
                //注册之前创建Bean和Holder对象 !!!必须注册不然崩溃
                .register(News.class, NewsHolder.class)
                .register(Ad.class, AdHolder.class)
                //set数据
                .setData(newsList)
                .create();
        //给ListContainer设置provider
        list.setItemProvider(provider);

效果如图


image.png

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

你可能感兴趣的:(鸿蒙ListContainer多布局实践)