看板 AndroidDev 關於我們 聯絡資訊
※ 引述《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
laiair: 想請問 simpleAdapter 和 baseAdapter 的差異是? 10/23 01:37
givemepass: baseadapter比較彈性, 可操作的地方沒有限制, 假設現 10/23 08:53
givemepass: 在每列背景顏色不同, 或者複雜一點的view就掛了, 例如 10/23 08:53
givemepass: line的聊天室, fb的塗鴉牆 10/23 08:53
laiair: ^^ 謝謝解惑 10/23 09:36
JULONE780701: 太用心!可是這程式在我的Nikia 3310跑不起來 10/23 09:41
bengohard: 詳細的講解+程式碼,馬上修改看看,太感謝你囉!!:D 10/23 11:41
zerofinal: 推一個 10/24 10:07
xisland: 我想每個item放不同背景圖耶,最好是由程式來畫,不用png檔 10/24 11:09
xisland: 半夜12點起來試到現在還沒成功 10/24 11:10
givemepass: 設條件就可以完成了, 還是你可以把程式碼貼上來看看 10/24 12:56
xisland: OK了 10/24 14:20
xisland: holder.bmp =Bitmap.createBitmap( //下略 10/24 14:23
xisland: view.setImageBitmap(holder.bmp); 10/24 14:23
xisland: 加這二行就可以了 10/24 14:24
xisland: http://imgur.com/1Ajx1rd 10/25 11:50
baobomb: 建議還是先弄懂oo 不然這類客製化的view你都會寫的很痛苦 10/27 16:01
baobomb: 尤其是很容易不知道自己在寫什麼 10/27 16:01