美文网首页设计模式
设计模式实践-适配器模式,给LinearLayout插上Adap

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

作者: h2coder | 来源:发表于2022-07-26 20:51 被阅读0次

    前言

    最近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的源码,发现适配器模式实现起来并不难,几百行代码就可以搞定了。

    • 自定义一个ListLayout,继承于LinearLayout,在构造方法中,设置为垂直排列
    • 设置适配器Adapter,清除自身所有子View
    • 调用adapter.onCreateViewHolder(),获取每个条目的ViewHolder,创建条目View
    • 调用adapter.onBindViewHolder(),以条目数据,渲染条目内容
    • 把条目View添加到自身

    优点

    使用方法和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();
                }
            }
        }
    }
    

    相关文章

      网友评论

        本文标题:设计模式实践-适配器模式,给LinearLayout插上Adap

        本文链接:https://www.haomeiwen.com/subject/utqairtx.html