Re: [問題] ListView如何動態改變item的屬性

作者: givemepass (λ)   2014-10-23 00:44:21
※ 引述《bengohard (我的歌聲裡)》之銘言:
: 問題:我有一個ListView,我想做到按一個Button後,把此ListView裡某item的屬性改變,
: 請問該怎麼做(不使用onItemClick)? 此ListView使用了一個SimpleAdapter.
: 我試過以下的方法無效,雖然取的到屬性值,但無法改變屬性值,為何?
: View view = Adapter.getView(0, ListView.getChildAt(0), ListView);
: ImageView img = (ImageView)view.findViewById(R.id.ID_Image);
: int vis = img.getVisibility(); //值正確
: img.setVisibility(View.INVISIBLE); //改變此值但無作用
: Adapter.notifyDataSetChanged();
: //再抓一次值結果還是原本的值 >"<
: View view = Adapter.getView(0, ListView.getChildAt(0), ListView);
: ImageView img = (ImageView)view.findViewById(R.id.ID_Image);
: int v = img.getVisibility();
BaseAdapter的特性就是可以重用View來節省資源,
ListView的重用結構會長這樣,
http://www.b2creativedesigns.com/Tutorials/CustomListView/recycle.png
這張圖可以很清楚看到 item 9 一開始在recycle pool,
當使用者手滑動, item 1被滑出去item 9就進來listview可視範圍
item 1進入recycle pool, 照這個邏輯,
如果使用者繼續往上滑, 那麼item 2, 3...就會進入回收區,
item 9, 1, 2 ...就會出現在畫面上。
這代表什麼意思? 代表著就算你資料有一百筆, 一千筆,
畫面呈現的也只有9個item, 這邊代表著view也只生成9個,
就不會佔用太多的資源。
那我怎麼知道他是第幾筆資料?
這個不用擔心 BaseAdapter幫你處理好這件事情了。
只要你資料順序塞好, 不管你是丟在怎樣的資料結構,
不管是ArrayList, Array, HashMap...等等。
只要你資料設定好, 就可以開始使用這個有趣的設計了。
程式怎麼做呢?
一開始宣告一個MyAdapter繼承BaseAdapter
public class MyAdapter extends BaseAdapter {
@Override
public int getCount() {
// TODO Auto-generated method stub
return 0;
}
@Override
public Object getItem(int arg0) {
// TODO Auto-generated method stub
return null;
}
@Override
public long getItemId(int arg0) {
// TODO Auto-generated method stub
return 0;
}
@Override
public View getView(int arg0, View arg1, ViewGroup arg2) {
// TODO Auto-generated method stub
return null;
}
}
上面你會看到四個方法, 基本上只要處理兩個方法
getCount跟getView即可,
剩下的兩個方法暫時不會用到, 有興趣的可以研究看看。
getCount就是跟adapter說你有幾筆資料要呈現,
假設現在你有100筆資料, 塞在arraylist內,
那麼我們來模擬一下
private ArrayList<Integer> mList;
public MyAdapter(){
mList = new ArrayList<Integer>();
for(int i = 0; i < 100; i++){
mList.add(i);
}
}
首先在建構子上建立一個arraylist並且塞值到這個陣列內,
@Override
public int getCount() {
// TODO Auto-generated method stub
return mList.size();
}
接著把資料結構的長度塞給getCount方法,
讓listview知道你的資料有多長。
接著到重頭戲getView
這個方法就是listView會不斷的來存取, 是一個callback function,
而不是由你自己去存取,
在看一次這個方法 我把參數改成英文名稱比較好認
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
return null;
}
第一個是我們的item到哪一個位置?
第二個是我們這個item所使用的view
第三個是我們item的parent
一開始會先把目前的view抓出來 判斷他是不是被初始化過
如果有被初始化過 代表著他之前就被使用過了
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
if(null == view){
} else{
}
return view;
}
這樣一來我們就可以把這個實體化過的view傳回去給listview了。
接著如果他是空的 則對他進行初始化
要初始化之前 先取得inflate
回到建構子補上
private LayoutInflater mLayoutInflater;
public MyAdapter(Context mContext){
mLayoutInflater = LayoutInflater.from(mContext);
...
}
再回到getView
if(null == view){
view = mLayoutInflater.inflate(R.layout.listview_item, null);
} else{
...
}
那個layout就是我們item view所裝的xml布局
透過LayoutInflater可以實體化成為一個view的物件
這樣一來就可以使用程式做很多靈活的操作了。
那layout裡面長怎樣呢?
<?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="match_parent" >
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/item_text"
/>
</RelativeLayout>
因為是範例所以寫得很簡單 只放入一個textview
接著就要把這個textview存在holder裡面
什麼是holder?
就是幫我們記住item內所有元件的內容
先宣告一個類別來存放
class Holder{
TextView itemText;
}
因為我們item layout很簡單 所以這樣就完成一個宣告
接著回到getView
Holder holder;
if(null == view){
view = mLayoutInflater.inflate(R.layout.listview_item, null);
holder = new Holder();
holder.itemText = (TextView) view.findViewById(R.id.item_text);
view.setTag(holder);
}
由上面可以看到 當view是空的時候
就把holder new出來 然後把itemText裝進去
在透過view設定tag 裝進holder
那麼下次view不是空的條件下 就可以直接把holder拿出來用
就完成了reuse了功效了
else{
holder = (Holder) view.getTag();
}
這個adapter基本型態已經完成,
然後把資料結構內的資料塞進每一個item
holder.itemText.setText(mList.get(position) + "");
就會出現這樣的畫面
http://ppt.cc/qgYw
現在要來說明怎麼透過改變資料 來更新view
假設我們要改變某一列
假設是第2筆資料出現的地方背景就顯示紅色
那麼只要在retun view;之前加入
if(position == 2){
view.setBackgroundColor(Color.RED);
}
就會出現第二列是紅色的
http://ppt.cc/9HSk
但是很奇怪 你往上滑動 居然第24列也是紅色的
http://ppt.cc/WD2r
這個就是reuse要注意的地方
因為listview就是把滑出去的item拿過來重用
所以那個重用的item背景還是紅色的
因此每一個item一開始都要先初始化
view.setBackgroundColor(Color.WHITE);
if(position == 2){
view.setBackgroundColor(Color.RED);
}
這樣就正常了
http://ppt.cc/PBHX
至於你的問題 想要讓一個Button按下去 某一個值進行變化
其實很簡單 只要開一個方法
public void setRowColor(int pos, int value){
mList.set(pos, value);
notifyDataSetChanged();
}
notifyDataSetChanged是用來刷新listview的畫面
也就是會再去讀取一圈getview
假設剛剛變化背景的判斷式改成
if(mList.get(position) == 200){
view.setBackgroundColor(Color.RED);
}
外面設定一個button 按下的時候 將arraylist的值改變
那麼你就會看到listview有一列變紅色的
這就是用值去改變view
而不是用view去修正值
程式碼
http://uploadingit.com/file/ffokgjkq0kjer5tq/TestAndroid.zip
作者: laiair (大頭)   2014-10-23 01:37:00
想請問 simpleAdapter 和 baseAdapter 的差異是?
作者: givemepass (λ)   2014-10-23 08:53:00
baseadapter比較彈性, 可操作的地方沒有限制, 假設現在每列背景顏色不同, 或者複雜一點的view就掛了, 例如line的聊天室, fb的塗鴉牆
作者: laiair (大頭)   2014-10-23 09:36:00
^^ 謝謝解惑
作者: JULONE780701 (亞)   2014-10-23 09:41:00
太用心!可是這程式在我的Nikia 3310跑不起來
作者: bengohard (我的歌聲裡)   2014-10-23 11:41:00
詳細的講解+程式碼,馬上修改看看,太感謝你囉!!:D
作者: zerofinal (人生~)   2014-10-24 10:07:00
推一個
作者: xisland (窄宅)   2014-10-24 11:09:00
我想每個item放不同背景圖耶,最好是由程式來畫,不用png檔半夜12點起來試到現在還沒成功
作者: givemepass (λ)   2014-10-24 12:56:00
設條件就可以完成了, 還是你可以把程式碼貼上來看看
作者: xisland (窄宅)   2014-10-24 14:20:00
OK了holder.bmp =Bitmap.createBitmap( //下略view.setImageBitmap(holder.bmp);加這二行就可以了
作者: baobomb (baobomb)   2014-10-27 16:01:00
建議還是先弄懂oo 不然這類客製化的view你都會寫的很痛苦尤其是很容易不知道自己在寫什麼

Links booklink

Contact Us: admin [ a t ] ucptt.com