• 谈谈-BaseAdapter的封装


    RecyclerView虽然因其灵活性、高效性等特点而备受好评,但也不是一定得用它把ListView给替代掉。在某些场景中,ListView还是相对更适合的。比如数据量不大,不频繁更新,并且需要简单地设置一下divider或header、footer的时候,相对于RecyclerView的繁琐,ListView在实现上则表现得更方便和简洁。

    过去的封装

    在使用ListView的过程中,为了复用ListView中的convertView以及优化getView()时的findViewById操作,我们通常会引入一个ViewHolder类,来持有itemView的子view。但是不便的是,我们需要为每一种显示不同数据的ListView都重写其BaseAdapter的getView(),于是就有了对其的一种封装: 
    创建一个通常的ViewHolder,里面用SparseArray<View>来缓存,如下:

    public class ViewHolder {
        private final View itemView;
        private SparseArray<View> mHolderViews;
    
        public ViewHolder(View view) {
            itemView = view;
            view.setTag(this);
            mHolderViews = new SparseArray<>();
        }
    
        public void hold(int... resIds) {
            for (int id : resIds) {
                mHolderViews.put(id, itemView.findViewById(id));
            }
        }
    
        public <V> V get(int id) {
            return (V) mHolderViews.get(id);
        }
    }

    再通过重写BaseAdapter的getView方法,作如下封装:

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        final ViewHolder holder;
        if (convertView == null) {
            holder = createHolder(position, parent);
            convertView = holder.itemView;
        } else {
            holder = (ViewHolder) convertView.getTag();
        }
        bindData(position, holder, getItem(position));
        return convertView;
    }
    
    public abstract ViewHolder createHolder(int position, ViewGroup parent);
    
    public abstract void bindData(int position, ViewHolder holder, T data);

    这样使用的时候只需要继承并实现两个抽象方法即可。

    这样写法,在后来我觉得还是有些别扭的地方。一是使用的时候还是需要去继承我们封装过的Adapter;二是在bindData()方法中,需要对每一个我们要设置的控件调用holder.get(id)方法,才能开始赋值或进行其他的设置,当我们的item的控件较多时,这些调用会使bindData()显得臃肿而不够纯粹,于是我又对其进行了另一种封装。

    另一种优雅封装

    这里我主要是通过接口解决重写Adapter的问题,然后再借鉴RecyclerView.ViewHolder,封装结果如下:

    假设我们重写BaseAdapter的类为BaseListAdapter,那么首先定义它的一个静态内部类:

        public static abstract class ViewHolder {
            public final View itemView;
    
            public ViewHolder(View itemView) {
                this.itemView = itemView;
                itemView.setTag(this);
            }
        }

    然后我们定义一个接口,用于创建ViewHolder以及绑定数据,如下:

    package com.githang.android.snippet.demo.adapter.adapter;
    
    import android.view.ViewGroup;
    
    public interface ViewCreator<T, H extends BaseListAdapter.ViewHolder> {
    
        H createHolder(int position, ViewGroup parent);
    
        /**
         * 设置列表里的视图内容
         *
         * @param position 在列表中的位置
         * @param holder   该位置对应的视图
         */
        void bindData(int position, H holder, T data);
    }

    这里定义了两个泛型 ,一个是T,表示我们Adapter里的数据对象,另一个是H,为我们的ViewHolder的子类,表示我们最终创建出来的ViewHolder。

    接下来,修改我们的BaseListAdapter,如下:

    public class BaseListAdapter<T, H extends BaseListAdapter.ViewHolder> extends BaseAdapter {
        private final List<T> mData;
        private final ViewCreator<T, H> mViewCreator;
    
        public BaseListAdapter(ViewCreator<T, H > creator) {
            this(new ArrayList<T>(), creator);
        }
    
        public BaseListAdapter(List<T> data, ViewCreator<T, H> creator) {
            mData = data == null ? new ArrayList<T>() : data;
            mViewCreator = creator;
        }
    
        @Override
        public int getCount() {
            return mData.size();
        }
    
        @Override
        public T getItem(int position) {
            return mData.get(position);
        }
    
        @Override
        public long getItemId(int position) {
            return position;
        }
    
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            final H holder;
            if (convertView == null) {
                holder = mViewCreator.createHolder(position, parent);
                convertView = holder.itemView;
            } else {
                holder = (H) convertView.getTag();
            }
            mViewCreator.bindData(position, holder, getItem(position));
            return convertView;
        }
    
        public void update(List<T> data) {
            mData.clear();
            addData(data);
        }
    
        public void addData(List<T> data) {
            if (data != null) {
                mData.addAll(data);
            }
            notifyDataSetChanged();
            }
    
        public static abstract class ViewHolder {
            public final View itemView;
    
            public ViewHolder(View itemView) {
                this.itemView = itemView;
                itemView.setTag(this);
            }
        }
    }

    在这个BaseListAdapter里,同样定义了两个泛型,与我们的ViewCreator相同。我们创建ViewHolder及绑定数据的操作,通过调用该接口来执行(见getView方法),而该接口实例则通过构造方法传入。 
    这样,我们在使用的时候就可以先像使用RecyclerView一样,定义一个实现我们ViewHolder的子类,然后使我们的Fragment或Activity实现这个ViewCreator接口就可以了。 
    最后,我们还可以继承ViewHolder,封装一个通用的ViewHolder,代码如下:

        public static class DefaultViewHolder extends ViewHolder {
            private SparseArray<View> mHolderViews;
    
            public DefaultViewHolder(View view) {
                super(view);
                mHolderViews = new SparseArray<>();
            }
    
            public void hold(int... resIds) {
                for (int id : resIds) {
                    mHolderViews.put(id, itemView.findViewById(id));
                }
            }
    
            public <V> V get(int id) {
                return (V) mHolderViews.get(id);
            }
        }

    接下来是使用示例:

    public class SampleFragment extends Fragment implements ViewCreator<User,SampleFragment.UserViewHolder> {
    
        private BaseListAdapter<User, UserViewHolder> mAdapter = new BaseListAdapter<User, UserViewHolder>(this);
    
        @Override
        public UserViewHolder createHolder(int position, ViewGroup parent) {
            return new UserViewHolder(LayoutInflater.from(getActivity()).inflate(R.layout.item_user, parent, false));
        }
    
        @Override
        public void bindData(int position, UserViewHolder holder, User user) {
            holder.name.setText(user.name);
            holder.email.setText(user.email);
        }
    
        static class UserViewHolder extends BaseListAdapter.ViewHolder {
            public final TextView name;
            public final TextView email;
    
            public UserViewHolder(View itemView) {
                super(itemView);
                name = (TextView) itemView.findViewById(R.id.name);
                email = (TextView) itemView.findViewById(R.id.email);
            }
        }
    }

    全部的封装代码加上注释等也不过一百来行,但是可以看到,在这样的封装下,我们的代码已经变得很清晰。

    本文的代码见:http://download.csdn.net/detail/maosidiaoxian/9700700
    更详细的封装及相关代码见我的开源项目:https://github.com/msdx/AndroidSnippet/tree/master/library/src/main/java/com/githang/android/snippet/adapter

    本文转载自:http://blog.csdn.net/maosidiaoxian/article/details/53444262

  • 相关阅读:
    [component]button skin–按钮组件外观
    as3 图片平滑方法
    SOLVED: Right Click in AS3
    一个超高效的不规则物体碰撞检测的类
    flex中的css应用
    控制时间间隔
    用flash cs3美化flex3之skin开发
    AS3实现RPG游戏鼠标8方向操作
    log4net basic example write to file
    观察者模式(C#实现 + eventhandler)
  • 原文地址:https://www.cnblogs.com/zly1022/p/7755076.html
Copyright © 2020-2023  润新知