Android開發進階 -- 通用介面卡 CommonAdapter
在Android開發中,我們經常會用到ListView 這個元件,為了將ListView 的內容展示出來,我們會去實現一個Adapter來適配,將Layout中的佈局以列表的形式展現到元件中。
比如,像 GGTalk 安卓版的查詢使用者功能,會把符合條件的使用者都列在下面:
為了達到這個效果,我們需要實現一個自定義的Adapter,而其它地方的ListView也要實現一個Adapter,這些Adapter會有很多重複的程式碼,非常繁瑣,現在我就將重複程式碼封裝到了一個通用的介面卡CommonAdapter中,這樣,在使用時只要繼承CommonAdapter就可以了,如此就避免了大段程式碼的重複,讓程式碼更簡潔易懂。我們先來看看CommonAdapter的定義。
一.CommonAdapter 實現
public abstract class CommonAdapter<T> extends BaseAdapter { private List<T> dataList; protected Context context; protected int item_layoutId=0; protected HashMap<Integer,Integer> layoutIdMap; //多種itemView時用到。 第一個int對應type型別,第二個int對應 itemlayoutId /** * 設定單個子模版VIew, * @param context * @param datas 繫結的物件集合 * @param item_layoutId 被繫結的檢視layoutID * */ public CommonAdapter(Context context, List<T> datas, int item_layoutId) { this.context=context; this.dataList=datas; this.item_layoutId=item_layoutId; } /** *設定多個子模版VIew, 並配合重寫 getItemViewType(int position)來確定是設定哪個模版 * @param context * @param datas 繫結的物件集合 * @param layoutIdMap 多種itemViewID Map 第一個int對應type型別,第二個int對應 itemlayoutId */ public CommonAdapter(Context context, List<T> datas, HashMap<Integer,Integer> layoutIdMap) { this.context=context; this.dataList=datas; this.layoutIdMap=layoutIdMap; } @Override public int getViewTypeCount() { if(this.layoutIdMap==null) { return 1; } return this.layoutIdMap.size(); } @Override public int getCount() { return this.dataList.size(); } @Override public Object getItem(int position) { return this.dataList.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { int type = getItemViewType(position); return getConvertView(position,convertView,type); } private View getConvertView(int position,View convertView,int itemViewType) { if(convertView==null) { int layoutId =0; if(this.item_layoutId!=0) { layoutId=this.item_layoutId; } if (this.layoutIdMap != null) { layoutId = this.layoutIdMap.get(itemViewType); } convertView = LayoutInflater.from(context).inflate(layoutId, null); } T t = this.dataList.get(position); this.setConvertView(convertView,t,itemViewType); return convertView; } /** * 區域性更新資料,呼叫一次getView()方法;Google推薦的做法 * * @param parent 要更新的listview * @param position 要更新的位置 */ public void onOneItemChanged(ListView parent, int position) { /**第一個可見的位置**/ int firstVisiblePosition = parent.getFirstVisiblePosition(); /**最後一個可見的位置**/ int lastVisiblePosition = parent.getLastVisiblePosition(); /**在看見範圍內才更新,不可見的滑動後自動會呼叫getView方法更新**/ if ((position >= firstVisiblePosition && position <= lastVisiblePosition)) { /**獲取指定位置view物件**/ View view = parent.getChildAt(position - firstVisiblePosition); getView(position, view, parent); } } /** * 需要去實現的對item中的view的設定操作 * * @param convertView 轉換後的試圖 * @param t Model物件 * @param itemViewType 試圖型別。對應layoutIdMap中的key */ protected abstract void setConvertView(View convertView, T t,int itemViewType); }
說明如下:
(1)泛型引數<T>就是我們要繫結資料Model物件的class型別。
(2)getViewTypeCount()方法會根據HashMap是否為空來識別是傳入的單一模版還是多模版.
(3)CommonAdapter還重寫了getCount(),getItem(int position),getView,getConvertView等BaseAdapter 的方法,這些方法的程式碼都基本每個Adapter都是一樣的,我們只需關心 setConvertView 這個抽象方法,在子類中重寫它,將具體的邏輯和資料的繫結在此即可。
二.使用CommonAdapter
- 先建一個類繼承CommonAdapter,在建構函式中呼叫父類的構造方法以初始化資料。實現 setConvertView 方法,將物件的資料繫結到模版上 即可
public class SearchFriendAdapter extends CommonAdapter<OrayUser> { /** * 設定單個子模版VIew, * @param context * @param datas 繫結的物件集合 * @param item_layoutId 被繫結的檢視layoutID * */ public SearchFriendAdapter(Context context,List<OrayUser> datas,int item_layoutId) { super(context,datas,item_layoutId); } @Override protected void setConvertView(View view, OrayUser orayUser,int itemViewType) { ...具體將物件的資料繫結到模版上 } }
- Activity中使用時new 一個Adapter,然後將adapter繫結到 ListView
adapter = new SearchFriendAdapter(this,this.userList,R.layout.friend_child); listView.setAdapter(adapter);
- 更新資料來源中的資料同步到View
//有2種方式將資料來源同步到View adapter.notifyDataSetChanged();//全部重新整理 adapter.onOneItemChanged(ListView parent, int position);//重新整理指定位置的資料
三. 讓CommonAdapter支援多模板
當然CommonAdapter還支援多模版的應用,我們只需將第一步中的Adapter實現稍稍做改動即可
public class ListViewAdapter extends CommonAdapter<ListViewItemModel> { /** *設定多個子模版VIew, 並配合重寫 getItemViewType(int position)來確定是設定哪個模版 * @param context * @param datas 繫結的物件集合 * @param layoutIdMap 多種itemViewID Map 第一個int對應type型別,第二個int對應 itemlayoutId */ public ListViewAdapter(Context context, List<ListViewItemModel> datas, HashMap<Integer,Integer> layoutIdMap) { super(context,datas,layoutIdMap); } @Override public int getItemViewType(int position) { ListViewItemModel model= (ListViewItemModel)super.getItem(position); if(model.floatRight) { return 1; } return 0; } @Override protected void setConvertView(View view, ListViewItemModel listViewItemModel,int itemViewType) { //根據itemViewType 的值來識別是哪個模版,以對應給模版繫結資料 } }
如上圖我們將建構函式的最後一個引數從單一的layoutID 換成了 模版集合的HashMap,再就是多加了 getItemViewType(int position) 方法來返回 具體Model對應的是哪一個模版型別(即hashMap 中的key值),使用時更新資料來源中的資料同步到View和上面的單一模版一樣使用。(同上2種更新方法)
HashMap<Integer,Integer> layoutMap=new HashMap<>(); layoutMap.put(0,R.layout.listview_item);//key值最好從0開始向上遞增,否則可能會找不到key的BUG layoutMap.put(1,R.layout.listview_right_item); adapter=new ListViewAdapter(this,models,layoutMap); listView.setAdapter(adapter);
四.綜合例項
最後我們還是以本文開頭的查詢使用者的例子,來更全面地說明CommonAdapter的使用。頁面截圖如下:
1.模版佈局
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/layout" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/selector_item" android:descendantFocusability="blocksDescendants" android:gravity="center_vertical" android:orientation="horizontal" android:padding="4dp"> <ImageView android:id="@+id/ct_photo" android:layout_width="@dimen/headImageSize" android:layout_height="@dimen/headImageSize" android:layout_margin="5dp" android:src="@drawable/plus" /> <ImageView android:id="@+id/ct_status" android:layout_width="11dip" android:layout_height="11dip" android:layout_marginLeft="@dimen/headImageSize" android:layout_marginTop="@dimen/headImageSize" android:src='@drawable/status_2' /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center_vertical" android:layout_alignTop="@id/ct_photo" android:layout_toRightOf="@id/ct_photo" > <TextView android:id="@+id/ct_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingLeft="5dp" android:paddingRight="5dp" android:paddingBottom="5dp" android:paddingTop="0dp" android:text="name" android:textColor="@color/dimgrey" android:textSize="16sp" android:textStyle="bold" /> <TextView android:id="@+id/ct_describe" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:layout_toRightOf="@id/ct_name" android:paddingLeft="5dp" android:paddingRight="5dp" android:paddingBottom="5dp" android:paddingTop="0dp" android:text="describe" android:textColor="@color/dimgrey" android:textSize="14sp" android:visibility="gone" /> </LinearLayout> <TextView android:id="@+id/ct_sign" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignBottom="@id/ct_photo" android:layout_toRightOf="@id/ct_photo" android:paddingLeft="5dp" android:text="sign" android:singleLine="true" android:ellipsize="start" android:textColor="@color/dimgrey" android:textSize="12sp" /> </RelativeLayout>
2.具體繼承CommonAdapter實現子類
public class SearchFriendAdapter extends CommonAdapter<OrayUser> { public SearchFriendAdapter(Context context,List<OrayUser> datas,int item_layoutId) { super(context,datas,item_layoutId); } @Override protected void setConvertView(View view, OrayUser orayUser,int itemViewType) { try{ SearchFriendAdapter.ViewHolder holder; if(view.getTag()==null) { holder = new SearchFriendAdapter.ViewHolder(view); view.setTag(holder); } else { holder = (SearchFriendAdapter.ViewHolder) view.getTag(); } HeadImgHelper.setUserHeadImg(holder.headImg,orayUser); if(orayUser.getName().equals(orayUser.getCommentName())) { holder.userName.setText(orayUser.getName()); } else { holder.userName.setText(orayUser.getCommentName()+"("+ orayUser.getName()+")"); } String catalogName= ClientGlobalCache.getInstance().getCurrentUser().getCatalogNameByFriend(orayUser.getUserID()); if(!StringHelper.isNullOrEmpty(catalogName)) { holder.describe.setText("[ "+ catalogName+" ]"); holder.describe.setVisibility(View.VISIBLE); } else { holder.describe.setText(""); holder.describe.setVisibility(View.GONE); } holder.orgName.setText(orayUser.getUserID()); }catch (Exception ex){ex.printStackTrace();} } private class ViewHolder { public ImageView headImg; public TextView userName; public TextView describe; public TextView orgName; public ViewHolder(View view) { this.headImg = (ImageView) view.findViewById(R.id.ct_photo); ImageView statusImg=(ImageView) view.findViewById(R.id.ct_status); statusImg.setVisibility(View.GONE); this.userName=(TextView) view.findViewById(R.id.ct_name); this.orgName=(TextView) view.findViewById(R.id.ct_sign); this.describe=(TextView) view.findViewById(R.id.ct_describe); } } }
3. Activity類 核心程式碼
public class SearchFriendActivity extends Activity implements TextView.OnEditorActionListener{ private DrawableEditText input; private ListView listView; private TextView search_resultStr; private List<OrayUser> userList=new ArrayList<OrayUser>() ; private SearchFriendAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_search_friend); this.initView(); } private void initView() { TextView pageTitle=(TextView)findViewById(R.id.headerText); pageTitle.setText("查詢使用者"); search_resultStr =(TextView) findViewById(R.id.search_resultStr); listView=(ListView)findViewById(R.id.listview_user); listView.setOnItemClickListener(this); adapter = new SearchFriendAdapter(this,this.userList,R.layout.friend_child); listView.setAdapter(adapter); } /** * 執行點選搜尋指令 * */ private void action_search(String idOrName) { List<OrayUser> temp_users=Manager.getInstance().doSearchUser(idOrName); search_resultStr.setText("沒有找到符合條件的使用者或群組"); this.setSearchResult(temp_users); } private void setSearchResult(List<OrayUser> temp_users) { this.userList.clear(); if(temp_users==null||temp_users.size()==0) { search_resultStr.setVisibility(View.VISIBLE); } else { for (OrayUser orayUser :temp_users) { if(orayUser.getUserID().equals(ClientGlobalCache.getInstance().getCurrentUser().getUserID())) { temp_users.remove(orayUser); break; } } this.userList.addAll(temp_users) ; search_resultStr.setVisibility(View.GONE); } this.adapter.notifyDataSetChanged(); } }
&n