zoukankan      html  css  js  c++  java
  • 谈谈-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

  • 相关阅读:
    又玩起了“数独”
    WebService应用:音乐站图片上传
    大家都来DIY自己的Blog啦
    CSS导圆角,不过这个代码没有怎么看懂,与一般的HTML是不同
    网站PR值
    CommunityServer2.0何去何从?
    网络最经典命令行
    炎热八月,小心"落雪"
    Topology activation failed. Each partition must have at least one index component from the previous topology in the new topology, in the same host.
    SharePoint 2013服务器场设计的一些链接
  • 原文地址:https://www.cnblogs.com/zly1022/p/7755076.html
Copyright © 2011-2022 走看看