日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android之BaseAdapter—convertView回收机制与动态控件响应

發(fā)布時間:2023/12/4 Android 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android之BaseAdapter—convertView回收机制与动态控件响应 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言:對于listView的BaseAdapter的派生,難度比較大。最難理解的莫過于getView(int position, View convertView, ViewGroup parent)這個函數(shù)是如何產(chǎn)生每條記錄的,有些博客中利用holderView,有些博客卻沒有用,種種方法之間有什么異同,今天我們就來揭開這個繪制ITEM機制的面紗。

本篇借助《PullToRefresh使用詳解(二)---重寫B(tài)aseAdapter實現(xiàn)復雜XML下拉刷新》的例子。所以本篇對于代碼的講解就比較粗略,如果有讀者對于如何重寫B(tài)aseAdapter不太熟悉的話,請先移步看看這篇文章,然后再回來這里,相信會有不一樣的收獲。

一、ConvertView回收機制

工作原理:

1、ListView 針對List中每個item,要求 adapter “給我一個視圖” (getView)。
2、一個新的視圖被返回并顯示

如果我們有上億個項目要顯示怎么辦?為每個項目創(chuàng)建一個新視圖?NO!這不可能!
實際上Android為你緩存了視圖。Android中有個叫做Recycler的構(gòu)件,下圖是他的工作原理:


如果你有10億個項目(item),其中只有可見的項目存在內(nèi)存中,其他的在Recycler中。
ListView先請求一個type1視圖(getView)然后請求其他可見的項目。convertView在getView中是空(null)的。
當item1滾出屏幕,并且一個新的項目從屏幕低端上來時,ListView再請求一個type1視圖。convertView此時不是空值了,它的值是item1。你只需設(shè)定新的數(shù)據(jù)然后返回convertView,不必重新創(chuàng)建一個視圖。?? 以上摘自《ListView中g(shù)etView的原理+如何在ListView中放置多個item》

也就是說:

1、android的listView在初始化的時候,如上面這個列表,整個屏幕只能放下7個item,那么listView在初始化時,就會只創(chuàng)建7個view,對于這些view也就是參數(shù)中的convertView。
2、那問題來了,當繼續(xù)網(wǎng)上滑動,item1消失了,而item8出來了。那系統(tǒng)還是為item8重新創(chuàng)建一個新的convertView嗎?另一個問題,item1的convertView去哪了?(銷毀回收資源,還是重新利用?)如果你是系統(tǒng)設(shè)計者,你會怎么做?大家想想,如果為每個要顯示的item都創(chuàng)建新convertView是不是太浪費了,況且對于item1的convertView已經(jīng)沒用了,我們何不把它拿來給item8用。對!系統(tǒng)就是這樣做的!這就是convertView的回收機制。就是將那些不再被用的ITEM的convertView重新給即將顯示的ITEM使用的機制!

二、例子

先給大家看一下單個ITEM的布局圖片,對于具體布局代碼,看源碼吧。


對于JAVA源碼,我們先看這種方式寫的convertView的生成方法。

package com.example.try_pulltorefresh_map; /*** 完成了從TXT文本中提取,并向下刷新* blog:http://blog.csdn.net/harvic880925* @author harvic* @date 2014-5-8* */ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.HashMap; import org.json.JSONArray;import com.handmark.pulltorefresh.library.PullToRefreshBase; import com.handmark.pulltorefresh.library.PullToRefreshListView; import com.handmark.pulltorefresh.library.PullToRefreshBase.Mode; import com.handmark.pulltorefresh.library.PullToRefreshBase.OnRefreshListener;import android.os.Bundle; import android.app.ListActivity; import android.content.Context; import android.graphics.Color; import android.text.format.DateUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.TextView;public class MainActivity extends ListActivity {private ArrayList<HashMap<String, Object>> listItem = new ArrayList<HashMap<String, Object>>();private PullToRefreshListView mPullRefreshListView;MyAdapter adapter=null;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mPullRefreshListView = (PullToRefreshListView) findViewById(R.id.pull_refresh_list);//設(shè)定下拉監(jiān)聽函數(shù)mPullRefreshListView.setOnRefreshListener(new OnRefreshListener<ListView>() {@Overridepublic void onRefresh(PullToRefreshBase<ListView> refreshView) {String label = DateUtils.formatDateTime(getApplicationContext(), System.currentTimeMillis(),DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_ALL);// Update the LastUpdatedLabelrefreshView.getLoadingLayoutProxy().setLastUpdatedLabel(label);// Do work to refresh the list here.}});mPullRefreshListView.setMode(Mode.PULL_FROM_END);//設(shè)置底部下拉刷新模式listItem=getData();//獲取LIST數(shù)據(jù)adapter = new MyAdapter(this);//設(shè)置適配器ListView actualListView = mPullRefreshListView.getRefreshableView();actualListView.setAdapter(adapter); }private ArrayList<HashMap<String, Object>> getData() {ArrayList<HashMap<String, Object>> list = new ArrayList<HashMap<String, Object>>();HashMap<String, Object> map = new HashMap<String, Object>();InputStream inputStream;try {inputStream=this.getAssets().open("my_home_friends.txt");String json=readTextFile(inputStream);JSONArray array = new JSONArray(json);for (int i = 0; i < array.length(); i++) {map = new HashMap<String, Object>();map.put("name", array.getJSONObject(i).getString("name"));map.put("info", array.getJSONObject(i).getString("info"));map.put("img",array.getJSONObject(i).getString("photo"));list.add(map);}return list; } catch (Exception e) {// TODO: handle exceptione.printStackTrace();}return list; }public final class ViewHolder{public TextView name;public TextView info;public TextView attentntion;} public class MyAdapter extends BaseAdapter{private LayoutInflater mInflater;public MyAdapter(Context context){this.mInflater = LayoutInflater.from(context);}@Overridepublic int getCount() {// TODO Auto-generated method stubreturn listItem.size();}@Overridepublic Object getItem(int arg0) {// TODO Auto-generated method stubreturn null;}@Overridepublic long getItemId(int arg0) {// TODO Auto-generated method stubreturn 0;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {System.out.println("position:"+position+" convertView:"+convertView);ViewHolder holder = null;holder=new ViewHolder(); convertView = mInflater.inflate(R.layout.item, null);holder.name = (TextView)convertView.findViewById(R.id.name);holder.info = (TextView)convertView.findViewById(R.id.info);holder.attentntion=(TextView)convertView.findViewById(R.id.attention);holder.name.setText((String)listItem.get(position).get("name"));holder.info.setText((String)listItem.get(position).get("info"));final TextView attention=holder.attentntion;holder.attentntion.setOnClickListener(new View.OnClickListener() { @Overridepublic void onClick(View v) {// TODO Auto-generated method stubattention.setTextColor(Color.RED);}});convertView.setTag(holder); return convertView;}}工具類/*** * @param inputStream* @return*/public String readTextFile(InputStream inputStream) {String readedStr = "";BufferedReader br;try {br = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));String tmp;while ((tmp = br.readLine()) != null) {readedStr += tmp;}br.close();inputStream.close();} catch (UnsupportedEncodingException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return readedStr;}}
以上代碼凡是懂如何派生自BaseAdapter的應該都可以看懂,這里就不再多講,只看核心代碼,摘錄如下:

@Override public View getView(int position, View convertView, ViewGroup parent) {System.out.println("position:"+position+" convertView:"+convertView);ViewHolder holder = null;holder=new ViewHolder(); convertView = mInflater.inflate(R.layout.item, null);holder.name = (TextView)convertView.findViewById(R.id.name);holder.info = (TextView)convertView.findViewById(R.id.info);holder.attentntion=(TextView)convertView.findViewById(R.id.attention);holder.name.setText((String)listItem.get(position).get("name"));holder.info.setText((String)listItem.get(position).get("info"));final TextView attention=holder.attentntion;holder.attentntion.setOnClickListener(new View.OnClickListener() { @Overridepublic void onClick(View v) {// TODO Auto-generated method stubattention.setTextColor(Color.RED);}}); return convertView; }

這里利用system.out.println對convertView進行捕捉,運行如果如下:

手機初始化是這樣子的:



根據(jù)上面我們講的理論,在初始化時,整個屏幕能放下三個ITEM,所以會創(chuàng)建三個全新的convertView。當我往下拉一個ITEM,出現(xiàn)第四個ITEM的時候,就會回收第一個ITEM的convertView給第四個。捕捉結(jié)果如下:


清楚的看到,前四個convertView為NULL,當?shù)谖鍌€ITEM出現(xiàn)時,此時由于第一個ITEM肯定已經(jīng)滾出屏幕,所以將其重新傳給即將出現(xiàn)的item5使用。我們上面說的第四個ITEM出現(xiàn)的時候就應該不再創(chuàng)建新convertView了,我想android開發(fā)者在考慮多創(chuàng)建一個ITEM的目的在于更安全吧。
回到上面的代碼,好像看著代碼沒有任何問題,我在里面寫了個clickListener,當點擊“關(guān)注”的時候,字體會變紅,試一下。
??????????? 點擊“關(guān)注”???????????????????? 下拉后再拉回來


問題出現(xiàn)了:當拉回來的時候,“關(guān)注”不再紅了!!!!!!為什么????
問題出在代碼上:

holder=new ViewHolder(); convertView = mInflater.inflate(R.layout.item, null); holder.name = (TextView)convertView.findViewById(R.id.name); holder.info = (TextView)convertView.findViewById(R.id.info); holder.attentntion=(TextView)convertView.findViewById(R.id.attention); 每次運行g(shù)etView獲取當前ITEM時,都會重新new 一個viewHolder與R.layout.item綁定,也就是說,每次都會產(chǎn)生一個新布局賦值給convertView讓其顯示。而我們上面講了,android會將回收過來的convertView返回給即將顯示的getView使用,以節(jié)約資源。而我們這里卻沒有領(lǐng)情,每次都重新創(chuàng)建一個布局賦給convertView,由于每次都創(chuàng)建一個新布局,所以當ITEM1被重新拉回來顯示的時候,由于是重新創(chuàng)建的布局,當然是初始狀態(tài)。“關(guān)注”當然也就是黑色的了。

改進

@Override public View getView(int position, View convertView, ViewGroup parent) {System.out.println("position:"+position+" convertView:"+convertView);ViewHolder holder = null;if (convertView == null) {holder=new ViewHolder(); convertView = mInflater.inflate(R.layout.item, null);holder.name = (TextView)convertView.findViewById(R.id.name);holder.info = (TextView)convertView.findViewById(R.id.info);holder.attentntion=(TextView)convertView.findViewById(R.id.attention);convertView.setTag(holder); }else {holder = (ViewHolder)convertView.getTag();}holder.name.setText((String)listItem.get(position).get("name"));holder.info.setText((String)listItem.get(position).get("info"));final TextView attention=holder.attentntion;holder.attentntion.setOnClickListener(new View.OnClickListener() { @Overridepublic void onClick(View v) {// TODO Auto-generated method stubattention.setTextColor(Color.RED);}});return convertView; }
不同的部分在這:

ViewHolder holder = null;if (convertView == null) {holder=new ViewHolder(); convertView = mInflater.inflate(R.layout.item, null); holder.name = (TextView)convertView.findViewById(R.id.name); holder.info = (TextView)convertView.findViewById(R.id.info); holder.attentntion=(TextView)convertView.findViewById(R.id.attention); convertView.setTag(holder); }else {holder = (ViewHolder)convertView.getTag(); }
當convertView為空,即初始化創(chuàng)建時,我們就將生成的布局利用setTag()保存在convertView中,當convertView利用回收機制回收過來讓我們再次使用時,我們通過getTag()將保存的布局取出來,重新將布局里的各個控件重新賦值就可以了。這里就利用了android-listView的回收機制。
再看"關(guān)注"的點擊事件運行的怎樣:


看第二張圖,當拉下去再拉回來的時候,一切正常,但當我們再往下拉(第三張圖),問題又出現(xiàn)了,明明沒有點P-5,為什么關(guān)注反而是紅色的!!!!!!

這是因為,P-5用的是P-1回收來的convertView!!!而P-1的convertView的布局里“關(guān)注”是紅色的。所以只要回收機制在,我們就沒有辦法改變從P-1回收來的convertView里的圖片布局,除非人為的將其重置!

理解了這個問題以后,我們想想解決辦法。
首先申請一個arrayList ?attentionArr變量,保存用戶點擊“關(guān)注”的ITEM的position,然后在繪制當前ITEM時,根據(jù)這個position是否在attentionArr里來判斷是不是將“關(guān)注”重新變紅。

代碼如下:


@Override public View getView(final int position, View convertView, ViewGroup parent) {System.out.println("position:"+position+" convertView:"+convertView);ViewHolder holder = null;if (convertView == null) {holder=new ViewHolder(); convertView = mInflater.inflate(R.layout.item, null);holder.name = (TextView)convertView.findViewById(R.id.name);holder.info = (TextView)convertView.findViewById(R.id.info);holder.attentntion=(TextView)convertView.findViewById(R.id.attention);convertView.setTag(holder); }else {holder = (ViewHolder)convertView.getTag();}holder.name.setText((String)listItem.get(position).get("name"));holder.info.setText((String)listItem.get(position).get("info"));final TextView attention=holder.attentntion;//根據(jù)當前position判斷,重新制做樣式if (attentionArr.contains(position)) {attention.setTextColor(Color.RED);}else {attention.setTextColor(Color.BLACK);}holder.attentntion.setOnClickListener(new View.OnClickListener() { @Overridepublic void onClick(View v) {// TODO Auto-generated method stubattention.setTextColor(Color.RED);attentionArr.add(position);//在點擊時將position加入其中}});return convertView; }
理解代碼難度不大,首先在OnClickListener時,將position加入到attentionArr數(shù)組中,然后在getView里,判斷當前position是不是用戶點擊過的,即是否包含在attentionArr數(shù)組中,如果是,則將“關(guān)注”置為紅色,否則置為初始色,黑色。




總結(jié)

以上是生活随笔為你收集整理的Android之BaseAdapter—convertView回收机制与动态控件响应的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。