編輯:關於Android編程
在Android開發中ListView是最為常用的控件之一,基本每個應用都會涉及到它,要使用ListView列表展示,就不可避免地涉及到另外一個東西——Adapter,我們都知道,Adapter是連接數據和列表界面的一個橋梁,一般項目中一個listview就會有一個Adapter與之對應,然後就是一堆方法的重寫,包括getCount,getItem,getView等等,遇到自定義布局時還需重寫getView方法,重寫getView的時候邏輯不復雜還好,遇到代碼邏輯復雜的時候adapter簡直臃腫,並且還需要寫很多次重復的代碼,比如判斷convertView是否為空,findViewById無數次停不下來。
activity_main.xml:
MainActivity:
public class MainActivity extends Activity { private ListView listview; private MyAdapter adapter; private Listdata; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); listview = (ListView)this.findViewById(R.id.listview); getData(); adapter = new MyAdapter(this.getApplicationContext(), data); listview.setAdapter(adapter); } public void getData(){ data = new ArrayList (); for(int i=0; i<20; i++){ data.add("數據"+i); } } }
自定義適配器 MyAdapter:
public class MyAdapter extends BaseAdapter{ private Context mContext; private Listlist; public MyAdapter(Context context, List list){ this.mContext = context; this.list = list; } @Override public int getCount() { // TODO Auto-generated method stub return list.size(); } @Override public Object getItem(int position) { // TODO Auto-generated method stub return list.get(position); } @Override public long getItemId(int position) { // TODO Auto-generated method stub return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub ViewHolder viewholder = null; if(convertView == null){ convertView = View.inflate(mContext, R.layout.item_listview, null); viewholder = new ViewHolder(); viewholder.titleTv = (TextView)convertView.findViewById(R.id.titleTv); convertView.setTag(viewholder); } else{ viewholder = (ViewHolder)convertView.getTag(); } viewholder.titleTv.setText(list.get(position)); return convertView; } public static class ViewHolder{ TextView titleTv; } }
每個列表項的布局文件 item_listview.xml:
通過以上代碼就可以完成一個簡單的列表界面和數據展示:
可以看到,我們每次調用getView時候,都會新建一個ViewHolder,然後判斷convertView是否為空,以及用
viewholder存儲我們每個列表項的子控件,再通過setTag和getTag來復用Viewholder,這一部分邏輯是每次getView
都會調用的,所以首先能夠想到將ViewHolder對象的邏輯給封裝起來,封裝ViewHolder首先要考慮以下幾點:
1.首先這個封裝來肯定要有一個ViewHolder的構造方法,另外,從上面可以看出每次getView都需要初始化convertView,那麼我們可以將convertView的初始化搬到ViewHold的構造方法中來進行,既然convertView要在ViewHolder的構造方法中初始化,那麼必定還需要inflate所需要的參數,以及每一個Item的下標,即context、layoutId、ViewGroup、position:
public class CommonViewHolder { public View mConvertView; public CommonViewHolder(Context context, int position, int layoutId, ViewGroup parent){ mConvertView = View.inflate(context, layoutId, null); mConvertView.setTag(this); } }
2.注意到以前的方式每次都需要判斷convertView是否為null,是則new一個新的convertView和ViewHolder實例並且setTag,否則采用getTag重用之前的ViewHolder:
public static CommonViewHolder get(Context context, View convertView, int position, int layoutId, ViewGroup parent){ if(convertView == null){ return new CommonViewHolder(context, position, layoutId, parent); } else{ return (CommonViewHolder)convertView.getTag(); } }
3.不同場景下列表項的元素是不確定的,數量和類型都不一致,既然是打造通用Adapter,那肯定要兼容多種情況,數量上我們可以想到使用Map來存儲我們的子控件,類型上可以使用Java的泛型來構造,如下:
private HashMapmap = new HashMap (); public T getView(int viewId){ View view = map.get(viewId); //如果view為空,則findId找到,並放進map中 if(view == null){ view = mConvertView.findViewById(viewId); map.put(viewId, view); } //如果view不會空,則直接返回 return (T)view; }
4.當然,以前我們getView方法最後返回的是一個convertView,所以還可以提供一個getConvertView的方法返回每一行對應的convertView:
public View getConvertView(){ return mConvertView; }
至此,完整的通用ViewHolder已打造完畢,完整代碼如下:
public class CommonViewHolder { public HashMapmap; public View mConvertView; public CommonViewHolder(Context context, int position, int layoutId, ViewGroup parent){ map = new HashMap (); mConvertView = View.inflate(context, layoutId, null); mConvertView.setTag(this); } public static CommonViewHolder get(Context context, View convertView, int position, int layoutId, ViewGroup parent){ if(convertView == null){ return new CommonViewHolder(context, position, layoutId, parent); } else{ return (CommonViewHolder)convertView.getTag(); } } public T getView(int viewId){ View view = map.get(viewId); if(view == null){ view = mConvertView.findViewById(viewId); map.put(viewId, view); } return (T)view; } public View getConvertView(){ return mConvertView; } }
如今,我們Adapter中的getView也就變成了這個樣子:
public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub CommonViewHolder holder = CommonViewHolder.get(mContext, convertView, position, R.layout.item_listview, parent); TextView titleTv = holder.getView(R.id.titleTv); titleTv.setText(list.get(position)); return holder.getConvertView(); }
它的代碼運行流程如下:
1.首先進入get方法獲取一個ViewHolder實例,如果convertView為空,則進入到構造方法,new一個用來存放這一行的map集合,inflate一個新的View,並且給它setTag,如果convertView不為空,則直接通過getTag獲得ViewHolder實例
2.接著調用holder.getView,傳入控件ID,如果在該map中還未有過,則通過findViewById找到控件,並存放進該行的View集合中,如果已經存在,則可以進行View的復用,即直接map.get(viewId);
3.最後調用getConvertView,獲得我們已經處理好的convertView實例
上面我們對ViewHolder進行了封裝,讓adapter的getView方法大大簡化,接下來開始封裝我們的Adapter
封裝Adapter成為公共類,我們需要注意以下問題:
平時我們寫Adapter的時候數據類型總是不一樣的,比如一會兒是一個User列表,一會兒是一個Car列表,傳進來的數據源的類型一般是不一樣的,那如何做到不管傳進來什麼類型都能使用呢?是的沒錯,又是通過泛型來解決:
public abstract class CommonAdapterextends BaseAdapter{ public Context mContext; public List list; public CommonAdapter(Context context, List list){ this.mContext = context; this.list = list; } @Override public int getCount() { // TODO Auto-generated method stub return list.size(); } @Override public Object getItem(int position) { // TODO Auto-generated method stub return list.get(position); } @Override public long getItemId(int position) { // TODO Auto-generated method stub return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub CommonViewHolder holder = CommonViewHolder.get(mContext, convertView, position, R.layout.item_listview, parent); TextView titleTv = holder.getView(R.id.titleTv); titleTv.setText(list.get(position)); return holder.getConvertView(); } }
可以看到,我們將Adapter的數據類型代替為泛型的形式,並且我們定義的CommonAdapter為一個抽象類,這樣做的原因是在基類先將getCount、getItem等方法給實現了,然後以後的具體Adapter類就只需要繼承該CommonAdapter,但是其實開發中我們的getView是每次的操作都是各有所異的,不可能定死,而且仔細看你會發現getView中的生成holder實例的代碼和返回convertView實例的代碼是千篇一律,區別只在於傳進來的item的布局id以及控件的生成不一樣罷了,所以可以將這部分不一樣的提取出來放在一個抽象方法中,留給子類去實現,將
MainActivity中:CommonAdapter修改如下:
public abstract class CommonAdapterextends BaseAdapter{ public Context mContext; public List list; public int layoutId; public CommonAdapter(Context context, List list, int layoutId){ this.mContext = context; this.list = list; this.layoutId = layoutId; } @Override public int getCount() { // TODO Auto-generated method stub return list.size(); } @Override public Object getItem(int position) { // TODO Auto-generated method stub return list.get(position); } @Override public long getItemId(int position) { // TODO Auto-generated method stub return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub CommonViewHolder holder = CommonViewHolder.get(mContext, convertView, position, layoutId, parent); convert(holder, list.get(position), position); return holder.getConvertView(); } //這個就是留給具體Adapter實現的方法 public abstract void convert(CommonViewHolder viewHolder, T data, int position); }
至此,我們的通用Adapter打造完畢,接下來我們來看看實踐效果:
例子1
先定義一個用於純文本顯示的ListView的Adapter類:
public class TextListViewAdapter extends CommonAdapter{ public TextListViewAdapter(Context context, List list) { super(context, list); // TODO Auto-generated constructor stub } public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub CommonViewHolder holder = CommonViewHolder.get(mContext, convertView, position, R.layout.item_listview, parent); TextView titleTv = holder.getView(R.id.titleTv); titleTv.setText(list.get(position)); return holder.getConvertView(); } }
MainActivity中:
public class MainActivity extends Activity { private ListView listview; private CommonAdapter adapter; private Listdata; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); listview = (ListView)this.findViewById(R.id.listview); initData(); adapter = new TextListViewAdapter(this.getApplicationContext(), data); listview.setAdapter(adapter); } public void initData(){ data = new ArrayList (); for(int i=0; i<20; i++){ data.add("數據"+i); } } }
可以看到,Adapter的代碼比以前省去了好多,運行後效果:
例子2
我們再試試多控件的情況,將item_listview布局文件更改如下:
MainActivity中:
public class MainActivity extends Activity { private ListView listview; private CommonAdapter adapter; private Listdata; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); listview = (ListView)this.findViewById(R.id.listview); initData(); adapter = new TextImgAdapter(this.getApplicationContext(), data, R.layout.item_listview); listview.setAdapter(adapter); } public void initData(){ data = new ArrayList (); for(int i=0; i<20; i++){ ItemBean bean = new ItemBean(R.drawable.ic_launcher, "標題"+i, "詳細內容"+i); data.add(bean); } } }
可以看到,做了些更改,數據類型更改為了我們自定義的bean,bean中有三個屬性,分別每個ListViewItem中的頭像、標題、內容
ItemBean類:
public class ItemBean { private int imgid; private String title; private String detail; public ItemBean() { super(); // TODO Auto-generated constructor stub } public ItemBean(int imgid, String title, String detail) { super(); this.imgid = imgid; this.title = title; this.detail = detail; } public int getImgid() { return imgid; } public void setImgid(int imgid) { this.imgid = imgid; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getDetail() { return detail; } public void setDetail(String detail) { this.detail = detail; } }
最後再看看我們的Adapter,由於現在的item布局多了一個ImageView以及一個TextView,所以我們的Adapter相應得變成如下:
public class TextImgAdapter extends CommonAdapter{ public TextImgAdapter(Context context, List list, int layoutId) { super(context, list, layoutId); // TODO Auto-generated constructor stub } @Override public void convert(CommonViewHolder viewHolder, ItemBean data, int position) { // TODO Auto-generated method stub ImageView item_iv = viewHolder.getView(R.id.item_iv); TextView titleTv = viewHolder.getView(R.id.titleTv); TextView detailTv = viewHolder.getView(R.id.detailTv); item_iv.setBackgroundResource(R.drawable.ic_launcher); titleTv.setText(data.getTitle()); detailTv.setText(data.getDetail()); } }
只是多了幾行控件的生成以及設置值,清晰了很多有木有~~以後有再多的元素,依然只需先生成對應的實例,然後set值,一目了然。
運行結果:
成功實現我們的效果,媽媽再也不用擔心我寫Adapter寫到廢寢忘食.........
有不少朋友為自己的手機鎖屏後設置了開鎖密碼,但如果忘記了手機解鎖圖案設置,不管是因為太過復雜,還是別的什麼原因,用上手機才是最重要的,下載吧小編就來分享一個
1,HttpUrlConnection一個UrlConnection常用來通過網絡發送和獲取數據,數據可以使任何類型任何長度,HttpUrlConnecti
馬上到雙十一,紅包來襲,時間又是充裕,搶紅包的時候意外發現了百度的福袋界面還不錯,想想還要專門寫一篇博文來完成其界面。當然啦,這其實就是解鎖界面的進化版本。不過其包含的知
1,需要權限 2,下載 RootTools.jar包。3,兩個關鍵方法。主要是獲取shell,並執行命令行。方法如下: private