Android 快速開發系列 打造萬能的ListView GridView 適配器,androidgridview

來源:互聯網
上載者:User

Android 快速開發系列 打造萬能的ListView GridView 適配器,androidgridview

轉載請標明出處:http://blog.csdn.net/lmj623565791/article/details/38902805 ,本文出自【張鴻洋的部落格】

1、概述

相信做Android開發的寫得最多的就是ListView,GridView的適配器吧,記得以前開發一同事開發項目,一個項目下來基本就一直在寫ListView的Adapter都快吐了~~~對於Adapter一般都繼承BaseAdapter複寫幾個方法,getView裡面使用ViewHolder模式,其實大部分的代碼基本都是類似的。

本篇部落格為快速開發系列的第一篇,將一步一步帶您封裝出一個通用的Adapter。

2、常見的例子

首先看一個最常見的案例,大家一目十行的掃一眼

1、布局檔案

主布局檔案:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent" >    <ListView        android:id="@+id/id_lv_main"        android:layout_width="fill_parent"        android:layout_height="fill_parent" /></RelativeLayout>

Item的布局檔案:

<?xml version="1.0" encoding="utf-8"?><TextView xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/id_tv_title"    android:layout_width="match_parent"    android:layout_height="50dp"    android:background="#aa111111"    android:gravity="center_vertical"    android:paddingLeft="15dp"    android:textColor="#ffffff"    android:text="hello"    android:textSize="20sp"    android:textStyle="bold" ></TextView>
2、Adapter

package com.example.zhy_baseadapterhelper;import java.util.List;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.TextView;public class MyAdapter extends BaseAdapter{private LayoutInflater mInflater;private Context mContext;private List<String> mDatas;public MyAdapter(Context context, List<String> mDatas){mInflater = LayoutInflater.from(context);this.mContext = context;this.mDatas = mDatas;}@Overridepublic int getCount(){return mDatas.size();}@Overridepublic Object getItem(int position){return mDatas.get(position);}@Overridepublic long getItemId(int position){return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent){ViewHolder viewHolder = null;if (convertView == null){convertView = mInflater.inflate(R.layout.item_single_str, parent,false);viewHolder = new ViewHolder();viewHolder.mTextView = (TextView) convertView.findViewById(R.id.id_tv_title);convertView.setTag(viewHolder);} else{viewHolder = (ViewHolder) convertView.getTag();}viewHolder.mTextView.setText(mDatas.get(position));return convertView;}private final class ViewHolder{TextView mTextView;}}

3、Activity

package com.example.zhy_baseadapterhelper;import java.util.ArrayList;import java.util.Arrays;import java.util.List;import android.app.Activity;import android.os.Bundle;import android.widget.ListView;public class MainActivity extends Activity{private ListView mListView;private List<String> mDatas = new ArrayList<String>(Arrays.asList("Hello","World", "Welcome"));private MyAdapter mAdapter;@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mListView = (ListView) findViewById(R.id.id_lv_main);mListView.setAdapter(mAdapter = new MyAdapter(this, mDatas));}}

上面這個例子大家應該都寫了無數遍了,MyAdapter整合BaseAdapter,然後getView裡面使用ViewHolder模式;一般情況下,我們的寫法是這樣的:對於不同布局的ListView,我們會有一個對應的Adapter,在Adapter中又會有一個ViewHolder類來提高效率。

這樣出現ListView就會出現與之對於的Adapter類、ViewHolder類;那麼有沒有辦法減少我們的編碼呢?

下面首先拿ViewHolder開刀~

3、通用的ViewHolder

首先分析下ViewHolder的作用,通過convertView.setTag與convertView進行綁定,然後當convertView複用時,直接從與之對於的ViewHolder(getTag)中拿到convertView布局中的控制項,省去了findViewById的時間~

也就是說,實際上們每個convertView會綁定一個ViewHolder對象,這個viewHolder主要用於幫convertView儲存布局中的控制項。

那麼我們只要寫出一個通用的ViewHolder,然後對於任意的convertView,提供一個對象讓其setTag即可;

既然是通用,那麼我們這個ViewHolder就不可能含有各種控制項的成員變數了,因為每個Item的布局是不同的,最好的方式是什麼呢?

提供一個容器,專門存每個Item布局中的所有控制項,而且還要能夠尋找出來;既然需要尋找,那麼ListView肯定是不行了,需要一個索引值對進行儲存,鍵為控制項的Id,值為控制項的引用,相信大家立刻就能想到Map;但是我們不用Map,因為有更好的替代類,就是我們android提供的SparseArray這個類,和Map類似,但是比Map效率,不過鍵只能為Integer.

下面看我們的ViewHolder類:

package com.example.zhy_baseadapterhelper;import android.content.Context;import android.util.Log;import android.util.SparseArray;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;public class ViewHolder{private final SparseArray<View> mViews;private View mConvertView;private ViewHolder(Context context, ViewGroup parent, int layoutId,int position){this.mViews = new SparseArray<View>();mConvertView = LayoutInflater.from(context).inflate(layoutId, parent,false);//setTagmConvertView.setTag(this);}/** * 拿到一個ViewHolder對象 * @param context * @param convertView * @param parent * @param layoutId * @param position * @return */public static ViewHolder get(Context context, View convertView,ViewGroup parent, int layoutId, int position){if (convertView == null){return new ViewHolder(context, parent, layoutId, position);}return (ViewHolder) convertView.getTag();}/** * 通過控制項的Id擷取對於的控制項,如果沒有則加入views * @param viewId * @return */public <T extends View> T getView(int viewId){View view = mViews.get(viewId);if (view == null){view = mConvertView.findViewById(viewId);mViews.put(viewId, view);}return (T) view;}public View getConvertView(){return mConvertView;}}


與傳統的ViewHolder不同,我們使用了一個SparseArray<View>用於儲存與之對於的convertView的所有的控制項,當需要拿這些控制項時,通過getView(id)進行擷取;

下面看使用該ViewHolder的MyAdapter;

@Overridepublic View getView(int position, View convertView, ViewGroup parent){//執行個體化一個viewHolderViewHolder viewHolder = ViewHolder.get(mContext, convertView, parent,R.layout.item_single_str, position);//通過getView擷取控制項TextView tv = viewHolder.getView(R.id.id_tv_title);//使用tv.setText(mDatas.get(position));return viewHolder.getConvertView();}

只看getView,其他方法都一樣;首先調用ViewHolder的get方法,如果convertView為null,new一個ViewHolder執行個體,通過使用mInflater.inflate載入布局,然後new一個SparseArray用於儲存View,最後setTag(this);

如果存在那麼直接getTag

最後通過getView(id)擷取控制項,如果存在則直接返回,否則調用findViewById,返回儲存,返回。

好了,一個通用的ViewHolder寫好了,以後一個項目幾十個Adapter一個ViewHolder直接hold住全場~~大家可以省點時間鬥個小地主了~~

4、打造通用的Adapter

有了通用的ViewHolder大家肯定不能滿足,怎麼也得省出dota的時間,人在塔在~~

下面看如何打造一個通過的Adapter,我們叫做CommonAdapter

繼續分析,Adapter一般需要保持一個List對象,儲存一個Bean的集合,不同的ListView,Bean肯定是不同的,這個CommonAdapter肯定需要支援泛型,內部維持一個List<T>,就解決我們的問題了;

於是我們初步打造我們的CommonAdapter

package com.example.zhy_baseadapterhelper;import java.util.List;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.TextView;public abstract class CommonAdapter<T> extends BaseAdapter{protected LayoutInflater mInflater;protected Context mContext;protected List<T> mDatas;public CommonAdapter(Context context, List<T> mDatas){mInflater = LayoutInflater.from(context);this.mContext = context;this.mDatas = mDatas;}@Overridepublic int getCount(){return mDatas.size();}@Overridepublic Object getItem(int position){return mDatas.get(position);}@Overridepublic long getItemId(int position){return position;}}
我們的CommonAdapter依然是一個抽象類別,除了getView以外我們把其他的代碼都實現了,這樣的話,在使用我們的Adapter只要實現一個getView,然後getView裡面再使用我們打造的通過的ViewHolder是不是感覺還不錯~

現在我們的MyAdapter是這樣的:

package com.example.zhy_baseadapterhelper;import java.util.List;import android.content.Context;import android.view.View;import android.view.ViewGroup;import android.widget.TextView;public class MyAdapter<T> extends CommonAdapter<T>{public MyAdapter(Context context, List<T> mDatas){super(context, mDatas);}@Overridepublic View getView(int position, View convertView, ViewGroup parent){ViewHolder viewHolder = ViewHolder.get(mContext, convertView, parent,R.layout.item_single_str, position);TextView mTitle = viewHolder.getView(R.id.id_tv_title);mTitle.setText((String) mDatas.get(position));return viewHolder.getConvertView();}}

所有的代碼加起來也就10行左右,是不是神清氣爽~~稍等,我先去dota一把~

但是我們是否就這樣滿足了呢?顯然還可以簡化。

5、進一步鑄造

注意我們的getView裡面的代碼,雖然只有4行,但是我覺得所有的Adapter的

第一行(ViewHolder viewHolder = getViewHolder(position, convertView,parent);)和

最後一行:return viewHolder.getConvertView();一定是一樣的。

那麼我們可以這樣做:我們把第一行和最後一行寫死,把中間變化的部分抽取出來,這不就是OO的設計原則嘛。現在CommonAdapter是這樣的:

package com.example.zhy_baseadapterhelper;import java.util.List;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;public abstract class n<T> extends BaseAdapter{protected LayoutInflater mInflater;protected Context mContext;protected List<T> mDatas;protected final int mItemLayoutId;public CommonAdapter(Context context, List<T> mDatas, int itemLayoutId){this.mContext = context;this.mInflater = LayoutInflater.from(mContext);this.mDatas = mDatas;this.mItemLayoutId = itemLayoutId;}@Overridepublic int getCount(){return mDatas.size();}@Overridepublic T getItem(int position){return mDatas.get(position);}@Overridepublic long getItemId(int position){return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent){final ViewHolder viewHolder = getViewHolder(position, convertView,parent);convert(viewHolder, getItem(position));return viewHolder.getConvertView();}public abstract void convert(ViewHolder helper, T item);private ViewHolder getViewHolder(int position, View convertView,ViewGroup parent){return ViewHolder.get(mContext, convertView, parent, mItemLayoutId,position);}}

對外公布了一個convert方法,並且還把viewHolder和本Item對於的Bean對象給傳出去,現在convert方法裡面需要幹嘛呢?

通過ViewHolder把View找到,通過Item設定值;

現在我覺得代碼簡化到這樣,我已經不需要單獨寫一個Adapter了,直接MainActivity匿名內部類走起~

@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mListView = (ListView) findViewById(R.id.id_lv_main);//設定適配器mListView.setAdapter(mAdapter = new CommonAdapter<String>(getApplicationContext(), mDatas, R.layout.item_single_str){@Overridepublic void convert(ViewHolder c, String item){TextView view = viewHolder.getView(R.id.id_tv_title);view.setText(item);}});}

可以看到效果咋樣,不錯吧。你覺得還能簡化嗎?我覺得還能改善。

6、Adapter最後的封魔

我們現在在convertView裡面需要這樣:

@Override
public void convert(ViewHolder viewHolder, String item)
{
TextView view = viewHolder.getView(R.id.id_tv_title);
view.setText(item);
}

我們細想一下,其實布局裡面的View常用也就那麼幾種:ImageView,TextView,Button,CheckBox等等;

那麼我覺得ViewHolder還可以封裝一些常用的方法,比如setText(id,String);setImageResource(viewId, resId);setImageBitmap(viewId, bitmap);

那麼現在ViewHolder是:

package com.example.zhy_baseadapterhelper;import android.content.Context;import android.graphics.Bitmap;import android.util.SparseArray;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.ImageView;import android.widget.TextView;import com.example.zhy_baseadapterhelper.ImageLoader.Type;public class ViewHolder{private final SparseArray<View> mViews;private int mPosition;private View mConvertView;private ViewHolder(Context context, ViewGroup parent, int layoutId,int position){this.mPosition = position;this.mViews = new SparseArray<View>();mConvertView = LayoutInflater.from(context).inflate(layoutId, parent,false);// setTagmConvertView.setTag(this);}/** * 拿到一個ViewHolder對象 *  * @param context * @param convertView * @param parent * @param layoutId * @param position * @return */public static ViewHolder get(Context context, View convertView,ViewGroup parent, int layoutId, int position){if (convertView == null){return new ViewHolder(context, parent, layoutId, position);}return (ViewHolder) convertView.getTag();}public View getConvertView(){return mConvertView;}/** * 通過控制項的Id擷取對於的控制項,如果沒有則加入views *  * @param viewId * @return */public <T extends View> T getView(int viewId){View view = mViews.get(viewId);if (view == null){view = mConvertView.findViewById(viewId);mViews.put(viewId, view);}return (T) view;}/** * 為TextView設定字串 *  * @param viewId * @param text * @return */public ViewHolder setText(int viewId, String text){TextView view = getView(viewId);view.setText(text);return this;}/** * 為ImageView設定圖片 *  * @param viewId * @param drawableId * @return */public ViewHolder setImageResource(int viewId, int drawableId){ImageView view = getView(viewId);view.setImageResource(drawableId);return this;}/** * 為ImageView設定圖片 *  * @param viewId * @param drawableId * @return */public ViewHolder setImageBitmap(int viewId, Bitmap bm){ImageView view = getView(viewId);view.setImageBitmap(bm);return this;}/** * 為ImageView設定圖片 *  * @param viewId * @param drawableId * @return */public ViewHolder setImageByUrl(int viewId, String url){ImageLoader.getInstance(3, Type.LIFO).loadImage(url,(ImageView) getView(viewId));return this;}public int getPosition(){return mPosition;}}

現在的MainActivity只需要這麼寫:

mAdapter = new CommonAdapter<String>(getApplicationContext(),R.layout.item_single_str, mDatas){@Overrideprotected void convert(ViewHolder viewHolder, String item){viewHolder.setText(R.id.id_tv_title, item);}};

convertView裡面只要一行代碼了~~~

好了,到此我們的通用的Adapter已經一步一步鑄造完畢~咋樣,以後寫項目省下來的時間是不是可以陪我切磋dota了(ps:11暱稱:血魔哥404)~~

註:關於ViewHolder裡面的setText,setImageResource這類的方法,大家可以在使用的過程中不斷的完善,今天發現這個控制項可以這麼設定值,好,放進去;時間長了,基本就完善了。還有那個ImageLoader是我另一篇部落格裡的,大家可以使用UIL,Volley或者自己寫個圖片載入器;

7、實踐

說了這麼多,還是得拿出來讓我們的實踐檢驗檢驗,順便來幾張套圖,俗話說,沒圖沒正相。

1、我們的執行個體代碼的圖是這樣的:

關於Adapter和ViewHolder的代碼是這樣的:

// 設定適配器mListView.setAdapter(mAdapter = new CommonAdapter<String>(getApplicationContext(), mDatas, R.layout.item_single_str){@Overridepublic void convert(ViewHolder helper, String item){helper.setText(R.id.id_tv_title,item);}});

哎喲,我是不是只要貼一行;

2、來個複雜點的布局

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="wrap_content"    android:background="#ffffff"    android:orientation="vertical"    android:padding="10dp" >    <TextView        android:id="@+id/tv_title"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:singleLine="true"        android:text="紅色錢包"        android:textSize="16sp"        android:textColor="#444444" >    </TextView>    <TextView        android:id="@+id/tv_describe"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_below="@id/tv_title"        android:layout_marginTop="10dp"        android:maxLines="2"        android:minLines="1"        android:text="周三早上丟失了紅色錢包,在食堂二樓"        android:textColor="#898989"        android:textSize="16sp" >    </TextView>    <TextView        android:id="@+id/tv_time"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_below="@id/tv_describe"        android:layout_marginTop="10dp"        android:text="20130240122"        android:textColor="#898989"        android:textSize="12sp" >    </TextView>    <TextView        android:id="@+id/tv_phone"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignParentRight="true"        android:layout_below="@id/tv_describe"        android:layout_marginTop="10dp"        android:background="#5cbe6c"        android:drawableLeft="@drawable/icon_photo"        android:drawablePadding="5dp"        android:paddingBottom="3dp"        android:paddingLeft="5dp"        android:paddingRight="5dp"        android:paddingTop="3dp"        android:text="138024249542"        android:textColor="#ffffff"        android:textSize="12sp" >    </TextView></RelativeLayout>

是這樣的:


布局是不是挺複雜的了~~

但是代碼是這樣的:

// 設定適配器mListView.setAdapter(mAdapter = new CommonAdapter<Bean>(getApplicationContext(), mDatas, R.layout.item_list){@Overridepublic void convert(ViewHolder helper, Bean item){helper.setText(R.id.tv_title, item.getTitle());helper.setText(R.id.tv_describe, item.getDesc());helper.setText(R.id.tv_phone, item.getPhone());helper.setText(R.id.tv_time, item.getTime());//helper.getView(R.id.tv_title).setOnClickListener(l)}});

從一個字串的布局到這樣的布局,Adapter加ViewHolder的改變就這麼多,加起來3行左右代碼~~~


到此,Android 快速開發系列 打造萬能的ListView GridView 適配器結束;


最後給大家推薦一個gitHub項目:https://github.com/JoanZapata/base-adapter-helper ,這個項目所做的,和我上面寫的基本一致。

還有上面的布局檔案來自網路,感謝Bmob的提供~

好了,我要去快樂的玩耍了~~










用Android開發,顯示資料在listview中,對於適配器的問題,

在繼承的Baseadpter類中的getview方法裡面做個判斷就可以了
 
android 開發怎把一個listview 放在gridview上面,listview的內容不被gridview 蓋住?

非常有創意的想法。gridview要建立一個適配器,針對你的問題肯定是自訂的adatper,然後在getview項裡面做每個gridview item的布局
,這個布局就是一個xml,這xml包含了一個listview。listview外層加上一個layout,listview本身有下拉功能,所以這個layout高度跟gridview
子項的高度同高!
 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.