设计模式

设计模式实践-适配器模式,给LinearLayout插上Adap

2022-07-26  本文已影响0人  h2coder

前言

最近app首页又改版了,不得不说现在项目越来越臃肿,什么模块都想整个入口在首页,导致首页的复杂度增加。为了适应多种类型的视图,一般我们会使用RecyclerView,但是在首页的条目的类型越来越多,甚至存在1种type就只有一个条目,这样导致RecyclerView的复用根本派不上用场,毕竟复用需要多个同type的条目才能产生复用。

甚至需要在RecyclerView的item中,再嵌入一个横向滑动的RecyclerView,或者是一个ViewPager,再或者一个九宫格的RecyclerView。每次滑动条目进出屏幕,都进行onBindView,再重新刷新item里面RecyclerView的adapter和数据等,造成卡顿,在低端机尤为明显。

于是决定把嵌套层级修改一下,最外层不再是一个RecyclerView,而是使用NestScrollView包裹几个不同模块RecyclerViewRecyclerView的item如果是垂直排列的,使用LinearLayout来垂直排列,而不是再嵌套一层RecyclerView,好处是LinearLayout离开屏幕不需要重新绘制,省去了不必要的开销。

思考

如果使用LinearLayout来排列,如果需要展示多个不同类型的item,代码写起来非常麻烦,可能需要for循环不断new View或者LayoutInflate.from(context).inflate(...),代码非常不优雅。而且拓展性非常差,如果突然UI设计改版,需要插入一个新的条目在中间位置,那代码就太难看了。

解决办法

RecyclerView可以显示那么多种类型的条目,使用的是Adapter适配器模式,把数据转换为ViewHolder,ViewHolder中存放着每个条目的Item,这样RecyclerView只需要面对需要的View即可,终于View长什么样,用数据数据渲染,都和RecyclerView没有关系,RecyclerView只需要把它add进自身就可以了。

那么LinearLayout可不可以也使用上Adapter适配器模式呢,经过翻看RecyclerView的源码,发现适配器模式实现起来并不难,几百行代码就可以搞定了。

优点

使用方法和RecyclerView基本是一模一样,所以可以轻松把一些RecyclerView的封装库进行移植,让LinearLayout使用起来更加方便。

推荐移植MultiType,这个库专注于RecyclerView的多类型条目绑定,支持一对一、一对多等,代码量少,非常优雅。

移植MultiType,修改的代码量非常少,基本是2个文件即可,所以让它支持上ListViewGridView都非常轻松,甚至是本篇文章的ListLayout

我移植的是3.5.0版本的MultiType,并非最新版。

项目地址

ListLayout

源码

/**
 * 垂直列表布局,支持类似RecyclerView的Adapter、ViewHolder的写法,但没有它的条目复用和滚动能力
 * 为了和其他滚动控件协调滚动的灵活性,所以需要滚动效果时,需要包一层NestScrollView来实现
 */
public class ListLayout extends LinearLayout {
    public static final long NO_ID = -1;
    /**
     * 适配器
     */
    private Adapter mAdapter;
    /**
     * 列表数据观察者,当数据notifyChange时,重新填充子View
     */
    private final ListLayoutDataObserver mObserver = new ListLayoutDataObserver();

    public ListLayout(Context context) {
        this(context, null);
    }

    public ListLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ListLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs, defStyleAttr);
    }

    private void init(Context context, AttributeSet attrs, int defStyleAttr) {
        //子View垂直排列
        setOrientation(LinearLayout.VERTICAL);
    }

    /**
     * 设置适配器
     */
    public void setAdapter(Adapter adapter) {
        //旧的Adapter取消监听
        if (mAdapter != null) {
            mAdapter.unregisterAdapterDataObserver(mObserver);
        }
        //新的Adapter注册监听
        mAdapter = adapter;
        if (adapter != null) {
            adapter.registerAdapterDataObserver(mObserver);
        }
        //重新填充子视图
        populate();
    }

    /**
     * 获取适配器
     */
    public Adapter getAdapter() {
        return mAdapter;
    }

    /**
     * 按数据填充视图
     */
    private void populate() {
        //清除掉之前的子View
        removeAllViews();
        //开始新建子View
        int itemCount = mAdapter.getItemCount();
        for (int position = 0; position < itemCount; position++) {
            long itemId = mAdapter.getItemId(position);
            int itemViewType = mAdapter.getItemViewType(position);
            //创建ViewHolder
            ViewHolder viewHolder = mAdapter.onCreateViewHolder(this, itemViewType);
            //设置Item的布局参数
            ViewGroup.LayoutParams itemLp = viewHolder.itemView.getLayoutParams();
            if (itemLp == null) {
                itemLp = new ListLayout.LayoutParams(
                        ListLayout.LayoutParams.MATCH_PARENT,
                        ListLayout.LayoutParams.WRAP_CONTENT
                );
            }
            //设置相关属性
            viewHolder.setAdapter(mAdapter);
            viewHolder.setItemViewType(itemViewType);
            viewHolder.setAdapterPosition(position);
            viewHolder.setItemId(itemId);
            //渲染ViewHolder,内部会渲染布局
            mAdapter.onBindViewHolder(viewHolder, position);
            //添加子View
            addView(viewHolder.itemView, itemLp);
        }
    }

    /**
     * 列表适配器
     */
    public abstract static class Adapter<VH extends ViewHolder> {
        private final AdapterDataObservable mObservable = new AdapterDataObservable();

        public abstract VH onCreateViewHolder(ViewGroup parent, int itemType);

        public abstract void onBindViewHolder(VH holder, int position);

        public int getItemViewType(int position) {
            return 0;
        }

        public long getItemId(int position) {
            return NO_ID;
        }

        public abstract int getItemCount();

        /**
         * 是否有观察者
         */
        public final boolean hasObservers() {
            return mObservable.hasObservers();
        }

        /**
         * 注册列表数据观察者
         */
        public void registerAdapterDataObserver(AdapterDataObserver observer) {
            mObservable.registerObserver(observer);
        }

        /**
         * 取消注册数据观察者
         */
        public void unregisterAdapterDataObserver(AdapterDataObserver observer) {
            mObservable.unregisterObserver(observer);
        }

        /**
         * 通知数据更新
         */
        public final void notifyDataSetChanged() {
            if (hasObservers()) {
                mObservable.notifyChanged();
            }
        }
    }

    public abstract static class ViewHolder {
        long mItemId = NO_ID;

        /**
         * 适配器
         */
        private Adapter adapter;
        /**
         * 条目View
         */
        public final View itemView;
        /**
         * 条目类型
         */
        private int mItemViewType;
        /**
         * 位置
         */
        private int adapterPosition;
        /**
         * 条目Id
         */
        private long itemId;

        public ViewHolder(View itemView) {
            this.itemView = itemView;
        }

        public void setItemViewType(int itemViewType) {
            mItemViewType = itemViewType;
        }

        public int getItemViewType() {
            return mItemViewType;
        }

        public void setAdapter(Adapter adapter) {
            this.adapter = adapter;
        }

        public Adapter getAdapter() {
            return adapter;
        }

        public void setAdapterPosition(int adapterPosition) {
            this.adapterPosition = adapterPosition;
        }

        public int getAdapterPosition() {
            return adapterPosition;
        }

        public void setItemId(long itemId) {
            this.mItemId = itemId;
        }

        public long getItemId() {
            return mItemId;
        }
    }

    public abstract static class AdapterDataObserver {
        public void onChanged() {
        }
    }

    /**
     * 列表数据观察者,当数据notifyChange时,重新填充子View
     */
    private class ListLayoutDataObserver extends AdapterDataObserver {
        ListLayoutDataObserver() {
        }

        @Override
        public void onChanged() {
            super.onChanged();
            //数据更新,重新填充子View
            populate();
        }
    }

    /**
     * 适配器被观察者
     */
    private static class AdapterDataObservable extends Observable<AdapterDataObserver> {
        /**
         * 是否有观察者
         */
        public boolean hasObservers() {
            return !mObservers.isEmpty();
        }

        /**
         * 通知观察者数据改变
         */
        public void notifyChanged() {
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onChanged();
            }
        }
    }

    /**
     * 被观察者
     */
    private static class Observable<T> {
        /**
         * 观察者列表
         */
        protected final List<T> mObservers = new ArrayList<T>();

        /**
         * 注册观察者
         */
        public void registerObserver(T observer) {
            if (observer == null) {
                throw new IllegalArgumentException("observer不能为空");
            }
            synchronized (mObservers) {
                if (!mObservers.contains(observer)) {
                    mObservers.add(observer);
                }
            }
        }

        /**
         * 取消注册观察者
         */
        public void unregisterObserver(T observer) {
            if (observer == null) {
                throw new IllegalArgumentException("observer不能为空");
            }
            synchronized (mObservers) {
                mObservers.remove(observer);
            }
        }

        /**
         * 取消订阅所有观察者
         */
        public void unregisterAll() {
            synchronized (mObservers) {
                mObservers.clear();
            }
        }
    }
}
上一篇下一篇

猜你喜欢

热点阅读