Android中的萬能介面卡——base-adapter-helper解析
阿新 • • 發佈:2019-01-23
在Android開發中,我們經常會用到ListView、GridView,每次編碼的時候都需要為他們寫對應的Adapter,寫多了就感覺很煩躁了,因為基本的程式設計思想都是一樣的,但是每次都要重複去寫,所以我們能不能把它們抽象成一個通用的模板,這樣就不用每次都重複寫相同的程式碼了,直接重複使用,這樣不是更好,下面我們就來介紹介紹一個開源專案base-adapter-helper。
傳統Adapter的編碼思路,主要看Adapter中的getView方法。
上面使用了一個ViewHolder用來快取對應Item中的view,並且重用移出的Item,它對應的就是convertView。這樣注意為了節省資源,提高效率。這種寫法大家都應該很熟悉了。
下面來看看base-adapter-helper是怎樣對其進行抽象封裝的。首先來看看它的類繼承圖。
下面我們分析分析getView的程式碼:
從上面可以看到它也對convertView進行了重用,當convertView為null,那麼我們需要建立一個BaseAdapterHelper,傳入的就是要顯示的位置position以及layoutId佈局檔案,這個position為前面getView中的那個position引數,layoutId為建立QuickAdapter傳入的引數,就是Item的佈局檔案。
當convertView不為null的時候,我們就直接可以複用這個convertView,首先從convertView中取出它關聯的BaseAdapterHelper物件,這樣就可以複用這個convertView對應的BaseAdapterHelper物件。
當剛開始顯示的時候,因為對應的convertView為null,所以會建立BaseAdapterHelper,核心程式碼如下:
因為在BaseAdapterHelper物件中包含有對應的Item對應的convertView和對應的position以及convertView裡面的子view集合,因此我們可以直接通過BaseAdapterHelper物件來完成各項操作。
當List向上滑動的時候,第一個Item移出List,底部就需要再顯示一個Item,這個時候getView裡面的convertView就是第一個移出的View,我們可以直接對它重用來顯示下一個Item,核心程式碼為:
也就是說我們只需要定義一個QuickAdapter重寫convert方法就可以了,在convert方法裡面我們直接使用BaseAdapterHelper物件完成對資料內容item的設定就可以了。
傳統Adapter的編碼思路,主要看Adapter中的getView方法。
public View getView(int pos, View convertView, ViewGroup parent){ ViewHolder holder; if (convertView == null) { convertView = mInflater.inflate(R.layout.list_item, null); holder = new ViewHolder(); holder.text = (TextView) convertView.findViewById(R.id.text)); holder.icon = (ImageView) convertView.findViewButId(R.id.icon)); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } holder.text.setText(DATA[pos]); holder.icon.setImageBitmap((pos & 1) == 1 ? mIcon1 : mIcon2); return convertView; } static class ViewHolder { TextView text; ImageView icon; }
上面使用了一個ViewHolder用來快取對應Item中的view,並且重用移出的Item,它對應的就是convertView。這樣注意為了節省資源,提高效率。這種寫法大家都應該很熟悉了。
下面來看看base-adapter-helper是怎樣對其進行抽象封裝的。首先來看看它的類繼承圖。
可以看到BaseQuickAdapter繼承自BaseAdapter,同樣我們重點關注它的getView函式。
@Override public View getView(int position, View convertView, ViewGroup parent) { if (getItemViewType(position) == 0) { final H helper = getAdapterHelper(position, convertView, parent); T item = getItem(position); helper.setAssociatedObject(item); convert(helper, item); return helper.getView(); } return createIndeterminateProgressView(convertView, parent); } private View createIndeterminateProgressView(View convertView, ViewGroup parent) { if (convertView == null) { FrameLayout container = new FrameLayout(context); container.setForegroundGravity(Gravity.CENTER); ProgressBar progress = new ProgressBar(context); container.addView(progress); convertView = container; } return convertView; }
下面我們分析分析getView的程式碼:
- 第3行程式碼就是獲取該postion的Item型別,上面定義了兩種型別的Item,一種是我們需要顯示的View的Item,一種是底部的載入的View的Item。當Item型別為0是就為需要顯示的Item,當Item型別為1是就為底部載入的Item,上面的第11行程式碼的createIndeterminateProgressView就是建立底部用來的載入的Item,可以看到它是一個ProgressBar。另外可以通過showIndeterminateProgress(boolean)來顯示或者隱藏這個item。
- 第4行程式碼的getAdapterHelper獲取一個BaseAdapterHelper物件,我們可以看到,在BaseQuickAdapter類中,getAdapterHelper是一個抽象函式,所以我們來看看BaseQuickAdapter的子類QuickAdapter,在這個類中它實現了getAdapterHelper方法,執行的實質是BaseAdapterHelper的靜態get方法。另外需要提到的是,QuickAdapter類的建構函式有一個引數為layoutResId,它就是傳入我們要顯示Item的佈局檔案。
static BaseAdapterHelper get(Context context, View convertView, ViewGroup parent, int layoutId, int position) {
if (convertView == null) {
return new BaseAdapterHelper(context, parent, layoutId, position);
}
// Retrieve the existing helper and update its position
BaseAdapterHelper existingHelper = (BaseAdapterHelper) convertView.getTag();
existingHelper.position = position;
return existingHelper;
}
從上面可以看到它也對convertView進行了重用,當convertView為null,那麼我們需要建立一個BaseAdapterHelper,傳入的就是要顯示的位置position以及layoutId佈局檔案,這個position為前面getView中的那個position引數,layoutId為建立QuickAdapter傳入的引數,就是Item的佈局檔案。
protected BaseAdapterHelper(Context context, ViewGroup parent, int layoutId, int position) {
this.context = context;
this.position = position;
this.views = new SparseArray<View>();
convertView = LayoutInflater.from(context) //
.inflate(layoutId, parent, false);
convertView.setTag(this);
}
在BaseAdapterHelper的建構函式裡面,定義了一個views,它其實就是傳統Adapter裡面的那個ViewHolder用來存放Item裡面的各個view。convertView為我們要顯示的Item的View,接著通過setTag函式將BaseAdapterHelper物件本身關聯到convertView上面,所以我們知道每個Item物件都關聯了一個BaseAdapterHelper物件。當convertView不為null的時候,我們就直接可以複用這個convertView,首先從convertView中取出它關聯的BaseAdapterHelper物件,這樣就可以複用這個convertView對應的BaseAdapterHelper物件。
- 接著分析最上面的程式碼,第5行程式碼getItem用來得到需要顯示的資料,這裡需要說明的是,我們應該把需要傳入的資料放在一個List中,我們可以看看QuickAdapter的建構函式
public QuickAdapter(Context context, int layoutResId, List<T> data) {
super(context, layoutResId, data);
}
可以看到我們傳入Item佈局的layoutResId和要顯示的資料data,data是List型別的。- 第6行程式碼是將我們向顯示的資料項與BaseAdapterHelper物件關聯起來。
- 第7行程式碼convert函式同樣是一個抽象函式,它需要我們進行實現,來具體設定對應的Item裡面的內容。
- 第8行程式碼是當BaseAdapterHelper物件設定為Item裡面的內容之後,然後就可以得到這個Item的View物件進行返回。
當剛開始顯示的時候,因為對應的convertView為null,所以會建立BaseAdapterHelper,核心程式碼如下:
protected BaseAdapterHelper(Context context, ViewGroup parent, int layoutId, int position) {
this.context = context;
this.position = position;
this.views = new SparseArray<View>();
convertView = LayoutInflater.from(context) //
.inflate(layoutId, parent, false);
convertView.setTag(this);
}
因為在BaseAdapterHelper物件中包含有對應的Item對應的convertView和對應的position以及convertView裡面的子view集合,因此我們可以直接通過BaseAdapterHelper物件來完成各項操作。
當List向上滑動的時候,第一個Item移出List,底部就需要再顯示一個Item,這個時候getView裡面的convertView就是第一個移出的View,我們可以直接對它重用來顯示下一個Item,核心程式碼為:
BaseAdapterHelper existingHelper = (BaseAdapterHelper) convertView.getTag();
existingHelper.position = position;
他直接得到BaseAdapterHelper物件,然後重新設定它對應的位置postion,因為BaseAdapterHelper物件中引用到了重用的convertView,這樣就可以直接使用這個view的Item了。下面來舉個簡單的例子:public class MainActivity extends AppCompatActivity {
private ListView listView;
private List<String> data = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
data.add("text1");
data.add("text2");
data.add("text3");
data.add("text4");
data.add("text5");
data.add("text6");
listView = (ListView) findViewById(R.id.listview);
QuickAdapter adapter = new QuickAdapter<String>(this, R.layout.item, data) {
@Override
protected void convert(BaseAdapterHelper helper, String item) {
helper.setText(R.id.textView, item);
}
};
listView.setAdapter(adapter);
}
}
也就是說我們只需要定義一個QuickAdapter重寫convert方法就可以了,在convert方法裡面我們直接使用BaseAdapterHelper物件完成對資料內容item的設定就可以了。
歡迎關注我的公眾號:DroidMind
精品內容,獨家釋出