關(guān)于android中ListView的Adapter如何設(shè)計(jì)能通用的一些看法

我們都知道,在安卓中使用ListView顯示多條數(shù)據(jù)的時(shí)候,必須要用一個(gè)適配器作為Data和View的橋梁,這種設(shè)計(jì)非常好, 能很簡(jiǎn)單就把ui和data分離開(kāi)來(lái),為ui的復(fù)用和維護(hù)代碼提供方便。
但是每次寫(xiě)一個(gè)適配器,都要實(shí)現(xiàn)一大堆的重復(fù)邏輯,下面是一個(gè)常規(guī)的實(shí)現(xiàn):
foo_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
xmlns:android="http://schemas.android.com/apk/res/android"    
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/activity_horizontal_margin"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical">   
 <TextView
android:id="@+id/txtTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="今天好天氣" />
<TextView
android:id="@+id/txtContent"
android:layout_marginTop="10dp"
tools:text="我是一名android開(kāi)發(fā)者"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>

適配器:

public class FooNormalAdapter extends BaseAdapter {

    private List<FooBean> datas = new ArrayList<>();
    private Context _context;

    public FooNormalAdapter(Context context) {
        this._context = context;
    }

    public Context getContext() {
        return _context;
    }

    public void setDataSource(List<FooBean> fooBeens) {
        setDataSource(fooBeens,true);
    }

    public void setDataSource(List<FooBean> fooBeens,boolean isClear) {
        if(isClear) this.datas.clear();
        this.datas.addAll(fooBeens);
        notifyDataSetChanged();
    }

    @Override
    public int getCount() {
        return datas.size();
    }

    @Override
    public Object getItem(int position) {
        return datas.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        FooViewHolder viewHolder = null;
        if (convertView == null) {
            convertView = View.inflate(getContext(), R.layout.foo_item, null);
            convertView.setTag(new FooViewHolder(convertView));
        } else {
            viewHolder = (FooViewHolder) convertView.getTag();
        }
        FooBean fb = (FooBean) getItem(position);
        viewHolder.txtTitle.setText(fb.getTitle());
        viewHolder.txtContent.setText(fb.getContent());
        return convertView;
    }

    public static class FooViewHolder {
        private TextView txtTitle;
        private TextView txtContent;

        public FooViewHolder(View convertView) {
            this.txtTitle = (TextView) convertView.findViewById(R.id.txtTitle);
            this.txtContent = (TextView) convertView.findViewById(R.id.txtContent);
        }
    }
}

嗯,看起來(lái)沒(méi)有什么問(wèn)題,但是如果有十個(gè)adapter,就要寫(xiě)十次這種無(wú)意義的代碼,我們不能干體力活啊~怎么辦?
先分析一下adapter需要哪些元素:
1.首先要inflateView就必須用到Context.
2.需要一個(gè)數(shù)組來(lái)存儲(chǔ)用于顯示的數(shù)據(jù)源
3.需要一個(gè)viewholder來(lái)優(yōu)化程序性能
4.可能有不同的viewType
分析完這個(gè),代碼隨之而來(lái):

public abstract class LBaseAdapter<E, V extends LBaseAdapter.BaseViewHolder> extends BaseAdapter {

    private Context context;
    private List<E> dataSource = new ArrayList<>(); //初始化一個(gè)防止getCount()空指針

    public LBaseAdapter(Context context) {
        this.context = context;
    }

    public Context getContext() {
        return context;
    }

    //替換原有數(shù)據(jù)源
    public void setDataSource(List<E> dataSource) {
        setDataSource(dataSource,true);
    }

    //如果isClear==true,則替換原有數(shù)據(jù)源,否則加到數(shù)據(jù)源后面
    public void setDataSource(List<E> dataSource, boolean isClear) {
        if (isClear) this.dataSource.clear();
        this.dataSource = dataSource;
        notifyDataSetChanged();
    }

    //只加一個(gè)數(shù)據(jù)
    public void addData(E data) {
        this.dataSource.add(data);
        notifyDataSetChanged();
    }

    //通過(guò)下標(biāo)移除一條數(shù)據(jù)
    public void removeData(int position) {
        this.dataSource.remove(position);
        notifyDataSetChanged();
    }

    //通過(guò)對(duì)象移除一條數(shù)據(jù)
    public void removeData(E data) {
        this.dataSource.remove(data);
        notifyDataSetChanged();
    }


    @Override
    public int getCount() {
        return this.dataSource.size();
    }


    @Override
    public E getItem(int position) {
        return this.dataSource.get(position);
    }


    @Override
    public long getItemId(int position) {
        return position;
    }


    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        V viewHolder = null;
        if (convertView == null) {
            viewHolder = createViewHolder(position, parent);
            if (viewHolder == null || viewHolder.getRootView() == null) {
                throw new NullPointerException("createViewHolder不能返回null或view為null的實(shí)例");
            }
            convertView = viewHolder.getRootView();
            convertView.setTag(viewHolder);
        }else{
            viewHolder = (V) convertView.getTag();
        }
        //給當(dāng)前復(fù)用的holder一個(gè)正確的position
        viewHolder.setPosition(position);
        bindViewHolder(viewHolder,position,getItem(position));
        return viewHolder.getRootView();
    }

    protected abstract V createViewHolder(int position, ViewGroup parent);

    protected abstract void bindViewHolder(V holder,int position, E data);

    public static class BaseViewHolder {
        private View rootView;
        private SparseArray<View> viewCache = new SparseArray<>();
        private int position = -1;

        public View getRootView() {
            return rootView;
        }

        void setPosition(int position) {
            this.position = position;
        }

        public int getPosition() {
            return position;
        }

        public BaseViewHolder(View rootView) {
            this.rootView = rootView;
        }

        public <R> R getView(@IdRes int viewID) {
            View cachedView = viewCache.get(viewID);
            if(null == cachedView) {
                cachedView = rootView.findViewById(viewID);
                viewCache.put(viewID, cachedView);
            }
            return (R) cachedView;
        }
    }
}

1.我加一個(gè)構(gòu)造函數(shù),強(qiáng)制傳context,并提供getContext()方法
2.加一個(gè)泛型E,允許子類(lèi)提供隨意實(shí)體類(lèi)型
3.加一個(gè)BaseViewHolder,并提供一些常用方法
4.重寫(xiě)getView(),在父類(lèi)里面把復(fù)用邏輯搞定,并提供兩個(gè)抽象方法用于讓子類(lèi)提供viewholder,和綁定具體數(shù)據(jù),嗯,我是仿著RecyclerView來(lái)的。:)
ok,這個(gè)通用的父類(lèi)怎么使用呢?
代碼說(shuō)話(huà):

public class FooSuperAdapter extends LBaseAdapter<FooBean, LBaseAdapter.BaseViewHolder> {

    public FooSuperAdapter(Context context) {
        super(context);
    }

    @Override
    protected BaseViewHolder createViewHolder(int position, ViewGroup parent) {
        return new BaseViewHolder(View.inflate(getContext(), R.layout.foo_item,null));
    }

    @Override
    protected void bindViewHolder(BaseViewHolder holder, int position, FooBean data) {
        TextView txtTitle = holder.getView(R.id.txtTitle);
        TextView txtContent = holder.getView(R.id.txtContent);

        txtTitle.setText(data.getTitle());
        txtContent.setText(data.getContent());
    }
}

之前的一大堆東西,現(xiàn)在都不用關(guān)心了, 只管設(shè)置itemview,和綁定數(shù)據(jù)就好了,是不是好看多了呢?
通用adapter用于了泛型,如果不了解可以留言,我將出一個(gè)泛型的專(zhuān)題來(lái)討論希望我的博客能幫到你 :)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容