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

看板AndroidDev作者 (〆)時間11年前 (2014/10/23 00:44), 編輯推噓7(7011)
留言18則, 7人參與, 最新討論串2/2 (看更多)
※ 引述《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 -- posted from android bbs reader on my Nokia 3310 -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 36.231.138.193 ※ 文章網址: http://www.ptt.cc/bbs/AndroidDev/M.1413996267.A.CC8.html

10/23 01:37, , 1F
想請問 simpleAdapter 和 baseAdapter 的差異是?
10/23 01:37, 1F

10/23 08:53, , 2F
baseadapter比較彈性, 可操作的地方沒有限制, 假設現
10/23 08:53, 2F

10/23 08:53, , 3F
在每列背景顏色不同, 或者複雜一點的view就掛了, 例如
10/23 08:53, 3F

10/23 08:53, , 4F
line的聊天室, fb的塗鴉牆
10/23 08:53, 4F

10/23 09:36, , 5F
^^ 謝謝解惑
10/23 09:36, 5F

10/23 09:41, , 6F
太用心!可是這程式在我的Nikia 3310跑不起來
10/23 09:41, 6F

10/23 11:41, , 7F
詳細的講解+程式碼,馬上修改看看,太感謝你囉!!:D
10/23 11:41, 7F

10/24 10:07, , 8F
推一個
10/24 10:07, 8F

10/24 11:09, , 9F
我想每個item放不同背景圖耶,最好是由程式來畫,不用png檔
10/24 11:09, 9F

10/24 11:10, , 10F
半夜12點起來試到現在還沒成功
10/24 11:10, 10F

10/24 12:56, , 11F
設條件就可以完成了, 還是你可以把程式碼貼上來看看
10/24 12:56, 11F

10/24 14:20, , 12F
OK了
10/24 14:20, 12F

10/24 14:23, , 13F
holder.bmp =Bitmap.createBitmap( //下略
10/24 14:23, 13F

10/24 14:23, , 14F
view.setImageBitmap(holder.bmp);
10/24 14:23, 14F

10/24 14:24, , 15F
加這二行就可以了
10/24 14:24, 15F

10/25 11:50, , 16F
10/25 11:50, 16F

10/27 16:01, , 17F
建議還是先弄懂oo 不然這類客製化的view你都會寫的很痛苦
10/27 16:01, 17F

10/27 16:01, , 18F
尤其是很容易不知道自己在寫什麼
10/27 16:01, 18F
文章代碼(AID): #1KHzxhp8 (AndroidDev)
文章代碼(AID): #1KHzxhp8 (AndroidDev)