如標題所說,本篇主要介紹如何做出含有按鈕之ListView。
正文-------------------------------------------------
在iOS的UITableView中,有一種預設的模式會有文字與一個小圖示:
若是直接點擊文字或是空白處,會觸發didSelectRowAtIndexPath,若點擊旁邊的小圖示,則會觸發accessoryButtonTappendForRowWithIndexPath,所以在GIS系統開發上,就可以很容易的將定位與顯示屬性分別在這兩個事件上實現,提供使用者一個很不錯的操作方式。
但反觀Android,這樣的需求就必須要經過自己重新撰寫,才能達到類似的效果;在之前曾經有發過相關文章,這篇剛好可以補足完整客製化時的教學。
一維陣列:Android學習_ArrayAdapter的使用
自行處理的二維陣列:Android學習_SimpleAdapter的使用
Cursor的處理:Android學習_SimpleCursorAdapter的使用
光是單純的變化都需要自行實作,更何況在ListView上面加個與事件綁定的小圖示。
--------------------------實作-------------------------
所以本篇的目標,就是客製化一個帶有按鈕的ListView,目標像下面這張:
可以顯示相關小圖示,主要標題、副標與一個帶有事件的按鈕。
觀念:觀察要將資料放入ListView的方法是setAdapter(adapter),所以唯一方法即是客製化adapter。
步驟:
1. 一個依照需求設計好的adapter.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="3dip"
android:descendantFocusability="blocksDescendants" >
<ImageView
android:id="@+id/ItemImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="2dip"
android:contentDescription="@string/adapter_btnText"
/>
<LinearLayout android:layout_toRightOf="@id/ItemImage"
android:layout_toLeftOf="@+id/ItemButton"
android:layout_alignTop="@+id/ItemImage"
android:layout_alignBottom="@+id/ItemImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/ItemName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="left|center_vertical"
android:textSize="20dip"
/>
<TextView
android:id="@+id/ItemInfo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="left|center_vertical"
android:textSize="20dip"
/>
</LinearLayout>
<Button android:id="@+id/ItemButton"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:background="@drawable/ic_launcher"
android:contentDescription="@string/adapter_btnText"
/>
</RelativeLayout>
2. 建立一個繼承於Adapter的類別(本篇使用BaseAdapter)
可以看到若是繼承於BaseAdapter,預設會有四個方法:
A. public int getCount()
B. public Object getItem(int position)
C. public long getItemId(int position)
D. public View getView(int position, View convertView, ViewGroup parent)
當中最重要的方法是getView,該方法會在每次重新繪製ListView任一個Item時被呼叫,也就是說我們要客製化每一列,就必須在這個方法中描繪我們需要的內容,並返回一個處理好的View。
除了上述四個方法必須要複寫外,通常還會增加一個建構函式,在建立該Adapter時將所需要的外部變數丟入,也就是丟入我們要顯示的值與圖案。
最後,因為我們希望除了點擊ListView可以有反應外,點擊自己加上的按鈕也可以有相對應的事件觸發,所以需要增加一個OnClickListener來監聽。
程式碼:
package tw.com.wangshifu.ola;
import java.util.ArrayList;
import java.util.HashMap;
import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
public class lv_BtnAdapter extends BaseAdapter {
private ArrayList<HashMap<String, Object>> mAppList;
private LayoutInflater mInflater;
private Context mContext;
private String[] keyString;
private int[] valueViewID;
private ItemView itemView;
private class ItemView {
ImageView ItemImage;
TextView ItemName;
TextView ItemInfo;
Button ItemButton;
}
public lv_BtnAdapter(Context c, ArrayList<HashMap<String, Object>> appList, int resource, String[] from, int[] to) {
mAppList = appList;
mContext = c;
mInflater = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
keyString = new String[from.length];
valueViewID = new int[to.length];
System.arraycopy(from, 0, keyString, 0, from.length);
System.arraycopy(to, 0, valueViewID, 0, to.length);
}
@Override
public int getCount() {
// TODO Auto-generated method stub
//return 0;
return mAppList.size();
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
//return null;
return mAppList.get(position);
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
//return 0;
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
//return null;
if (convertView != null) {
itemView = (ItemView) convertView.getTag();
} else {
convertView = mInflater.inflate(R.layout.adapter_button, null);
itemView = new ItemView();
itemView.ItemImage = (ImageView)convertView.findViewById(valueViewID[0]);
itemView.ItemName = (TextView)convertView.findViewById(valueViewID[1]);
itemView.ItemInfo = (TextView)convertView.findViewById(valueViewID[2]);
itemView.ItemButton = (Button)convertView.findViewById(valueViewID[3]);
convertView.setTag(itemView);
}
HashMap<String, Object> appInfo = mAppList.get(position);
if (appInfo != null) {
int mid = (Integer)appInfo.get(keyString[0]);
String name = (String) appInfo.get(keyString[1]);
String info = (String) appInfo.get(keyString[2]);
int bid = (Integer)appInfo.get(keyString[3]);
itemView.ItemName.setText(name);
itemView.ItemInfo.setText(info);
itemView.ItemImage.setImageDrawable(itemView.ItemImage.getResources().getDrawable(mid));
itemView.ItemButton.setBackgroundDrawable(itemView.ItemButton.getResources().getDrawable(bid));
itemView.ItemButton.setOnClickListener(new ItemButton_Click(position));
}
return convertView;
}
class ItemButton_Click implements OnClickListener {
private int position;
ItemButton_Click(int pos) {
position = pos;
}
@Override
public void onClick(View v) {
int vid=v.getId();
if (vid == itemView.ItemButton.getId())
Log.v("ola_log",String.valueOf(position) );
}
}
}
3. 使用步驟二所建立的adapter,置入ListView中
我們用簡單的迴圈模擬外部要放入的資料,並且將剛剛所建立的adapter new起來:
protected void putDataToListView()
{
ArrayList<HashMap<String, Object>> Item = new ArrayList<HashMap<String, Object>>();
for(int i=0; i<20; i++)
{
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("ItemImage", R.drawable.ic_launcher);
map.put("ItemName", "Name");
map.put("ItemInfo", "Info");
map.put("ItemButton", R.drawable.ic_launcher);
Item.add(map);
}
lv_BtnAdapter Btnadapter = new lv_BtnAdapter(
this,
Item,
R.layout.adapter_button,
new String[] {"ItemImage","ItemName", "ItemInfo","ItemButton"},
new int[] {R.id.ItemImage,R.id.ItemName,R.id.ItemInfo,R.id.ItemButton}
);
lv.setAdapter(Btnadapter);
}
效果:
稍微對於輸入的內容做些變更,就可以看出客製化的效果:
最後的列表效果可以說是非常之讚。
可以請問一下
回覆刪除lv.setAdapter(Btnadapter);
的lv是指甚麼嗎???
有點不懂
就是layout檔內ListView的id對應名稱。
回覆刪除不好意思
回覆刪除請問image 如果要改是在哪做修改
是在 lv_BtnAdapter
還是在 HashMap map
在HashMap put的時候換,也就是上面文章的"map.put("ItemImage", R.drawable.ic_launcher);"這句,跑迴圈時滿足某條件就put圖1,另一個條件就put進圖2,以此類推。
回覆刪除謝謝
回覆刪除請問一下客製化的那張圖
回覆刪除按一下去會連結網址嗎??
那這樣功能要怎麼達成??
在按鈕的onClick事件撰寫置換右邊webView的網址即可。
回覆刪除抱歉...不是很懂
回覆刪除那這樣每個按鈕都要不一樣的網址要怎麼去對應??
有code可以提供參考嗎?
謝謝~
在第二步驟的最後:
回覆刪除@Override
public void onClick(View v) {
int vid=v.getId();
if (vid == itemView.ItemButton.getId())
Log.v("ola_log",String.valueOf(position) );
}
這句可以取得你按下哪一列,也就是position這個變數,有了這個變數就可以對應回你在ArrayList> appList這個裡面的網址。
當然,你必須要在組出ArrayList> item的地方就給他每一個項目的對應網址。
所以流程就是:
1. 按下按鈕取得按到哪一列。
2. 由哪一列去陣列裡面換出網址字串。
3. 將網址塞到webview當中。
謝謝~~
回覆刪除我嘗試看看!!
感謝你的分享^^
想請問您一下
回覆刪除如果是在Fragment底下,要製作出這樣的功能
有什麼方法可以達到呢?
謝謝~
我沒有實際做過,但是你應該仍然可以將ListView放進你所使用的框架,然後完成一樣的效果,這要嘗試才知道。
回覆刪除感謝~ 我在嘗試看看!!
回覆刪除adapter_button檔在哪裡 是哪一個?
回覆刪除是一個檔名為adapter_button的layout,裡面有1個ImageView、2個TextView跟1個Button。
回覆刪除請問一下,要如何在adapter內調用database呢?
回覆刪除想要在每一個listitem都加個按鈕來刪除那一個item。
因為我是從SQL取出資料填到list內,所以刪除時想打開SQLiteDatabase並對其動作,但在adapter內沒有出現openOrCreateDatabase這個function。
是否在adapter內無法調用database?我該如何解決呢?
謝謝:)
(因為點擊listitem還有別的工作,希望只有在點擊listitem內的按鈕才做調用DB的動作)
這邊就單純的在按下按鈕的事件中使用刪除該列的方法。
回覆刪除理論上我們會把操作SQLite的方法都寫在另一個類別裡面,你要做的只是把那個類別new起來,然後把id丟給刪除的方法。
但是如果你是把全部的東西都寫在你的Activity裡面,那你有兩個選擇,把SQLite相關的東西傳進adapter內來操作,第二個就是使用委派的方法來操作(可參考:http://wangshifuola.blogspot.tw/2012/03/androidimitate-ioss-delegate-callback.html),但我還是建議將SQLite相關操作獨立出來,因為當程式變複雜的時候,操作SQLite還是一樣單純。
您好:
回覆刪除請問是否能做到只有某部份加按鈕,另外一部份不加
例如,我現在有6個項目,上面3個項目要加按鈕另外3個項目不加,如果能做到可否大概說明一下要如何做,或是在何處可參考,謝謝您
在getView的方法內將itemView.ItemButton隱藏即可。
回覆刪除-----------------------------------------
getView就是畫出一個一個cell的方法,所以一每一個cell要畫的時候都會去執行,在該方法利用判斷式則可做到有些有按鈕有些沒有按鈕的效果。(或是你可以將圖換成不同的來告知使用者也可以。)
不知道大哥你有沒有一個專案檔可供下載??
回覆刪除目前沒有提供相關的下載空間。 :D
回覆刪除你好,想請問一下在這一段程式碼中 "3. 使用步驟二所建立的adapter,置入ListView中" 請問要加入在哪一個,是在lv_BtnAdapter當中嗎? 還是在MainActivity裡面,如果在lv_BtnAdapter那邊,那麼MainActivity裡應該也需要呼應的程式碼吧???抱歉,小弟才疏學淺,但是對於你的這個介面非常有興趣!!
回覆刪除在你要呈現列表的Activity內。
回覆刪除不知道你這隻程式的架構是不是這個樣子??(不好意思麻煩你點擊一下圖片)
回覆刪除http://i194.photobucket.com/albums/z201/brontosaur16/test/p1.jpg
如果正確,那麼我在看到你下面的回覆有講到說adapter_button的layout,裡面有1個ImageView、2個TextView跟1個Button,因此我在創立了一個layout並且拉了1個ImageView、2個TextView跟1個Button,如此圖片
http://i194.photobucket.com/albums/z201/brontosaur16/test/p2.jpg
最後是你說將最後一段程式碼貼在Activity內,如此圖片
http://i194.photobucket.com/albums/z201/brontosaur16/test/p3.jpg
我都按照你說的去做,可能不知道哪邊有問題,執行起來都是空白一片,想請教你一下,是不是我哪邊出了問題,抱歉,小弟問題很多,真的很不好意思。
webview = new WebView(v.getContext());
回覆刪除webview.getSettings().setJavaScriptEnabled(true); webview.loadUrl("http://www.google.com.tw/");
想請問一下,我在
@Override
public void onClick(View v) {
int vid=v.getId();
if (vid == itemView.ItemButton.getId())
上面所述的;
}
但是點選都沒有到網頁去,是我哪邊錯了嗎?
看你的第三張圖片,好像沒看到呼叫PutDataoListView這個方法。
回覆刪除這樣我不能確定耶,可能要先確認:
回覆刪除1. 有沒有進到判斷式內。
2. webview有沒有抓到。
你好,我想再加一個textview進入到listview裡面,
回覆刪除請問是否在Mainactivity程式碼那邊的加入protected void putDataToListView()方法裡加入
map.put("Itemadd", "我新加的");
lv_BtnAdapter Btnadapter = new lv_BtnAdapter(
this,
Item,
R.layout.adapter,
new String[] {"ItemImage","ItemName", "ItemInfo","ItemButton","Itemadd"},
new int[] {R.id.ItemImage,R.id.ItemName,R.id.ItemInfo,R.id.ItemButton,R.id.aatextView1}
);
而在lv_BtnAdapter程式碼的getView()方法中那邊修改並加入
itemView.Itemadd = (TextView)convertView.findViewById(valueViewID[4]);
............
String add = (String) appInfo.get(keyString[4]);
itemView.Itemadd.setText(add);
但是我改完後,圖形介面完全沒有我所加入的設定><
請問還有需要修改哪裡嗎?
layout.xml裡面有加入textview物件嗎?
回覆刪除有喔!也沒有任何錯誤訊息,所以才不知道錯在哪裡!!
回覆刪除都還是原來的顯示。
順便請教一下,我現在是想要點擊ListView來讓我加在裡面的radiobutton有選項的動作,在網路查了很多,它們說只要將XML的radiobutton裡加入
android:focusable="false"
android:focusableInTouchMode="false"
這兩行即可,在setOnItemClickListener的事件中,也能做出我要的事件 例如 Toast.makeText(),但是我在裡面設定radiobutton.setcheck(true)去沒有跟著動作。請問你有遇過這樣的問題嗎?
印象中在處理listview內有radio時好像沒有碰過,這種細節的問題必須要配合程式碼才好測試,不好意思。
回覆刪除抱歉現在才來留言~
回覆刪除把打開SQL的指令另開一個class真的就可以進行SQLite的指令了~
不過我比較懶XD,只有把create table的指令寫在另一個class
其他的指令在把database開啟後就可以直接接".delete"
".close"等指令了
非常感謝您的幫助~:)
---
另外分享個碰到的小問題,就是如果我想在list的adapter開啟另一個activity的話,要使用自己命名的Context去執行startActivity的指令
ex: context.startActivity(intent)
因為平常指令完整版其實是"this.startActivity(intent)"
不過真正的解決方法是不是這樣就不得而知了(總覺得在程式的架構上會有點小問題...?)
這個問題困擾了我一陣子,找到解答後發現自己對android的架構果然還是不是很熟悉O_Q
---
再次感謝:D
想請問一下我listview裡面的按鈕要call電話,那請問要怎第一個按鈕是撥出第一組的號碼,第二個按鈕是撥第二組電話號碼以此類推 請問是要在button 裡面怎寫才能按第幾組撥出第幾組號碼!!感謝~
回覆刪除不好意思 請問可否提供 這個範例的完整專案檔 想要更深入的研究 感激不盡
回覆刪除按按鈕打電話跟前面有人問的按按鈕開網頁是一樣的,所以方式就是:
回覆刪除在第二步驟的最後:
@Override
public void onClick(View v) {
int vid=v.getId();
if (vid == itemView.ItemButton.getId())
Log.v("ola_log",String.valueOf(position) );
}
這句可以取得你按下哪一列,也就是position這個變數,有了這個變數就可以對應回你在ArrayList> appList這個裡面的電話。
當然,你必須要在組出ArrayList> item的地方就給他每一個項目的對應電話。
所以流程就是:
1. 按下按鈕取得按到哪一列。
2. 由哪一列去陣列裡面換出電話號碼。
3. 再撥出電話號碼。
文章裡面已經是完整程式碼了。 @.@
回覆刪除請問一下 這個範例 如何用switch case 切換到不同的Activity 或 layout 應該怎麼修改
回覆刪除你已經在onClick事件取得了position,如果你要用switch case判斷,那就是把你的switch case判斷加onClick事件中,再以position判斷執行哪一個Case,用intent轉到各自的Activity。
回覆刪除其實只是單純把判斷寫在onclick內而已。
不好意思請問要在onclick裡面取得他按下哪一列
回覆刪除那請問陣列我該怎寫呢去取出他的號碼呢?是用迴圈去跑嗎?該怎給他對應的電話
請問此方法可以使用.addFooterView嗎??
回覆刪除如果版主還可以回復我的話~
回覆刪除我想知道的是:如何用一個外在的按鈕,改變item 裡面的 textView
example:我另外設置一個按鈕,(不再listView)裡面,但在同一個layout裡面。
我利用這個按鈕,控制item裡面的值? 可以做得到嗎?
就按下一個按鈕,原本 "info"這字串改成 "123" 這樣。
我目前只有想到 , 按下按鈕,刪除整個 listView 然後在同一的動作下,把他新增回來。
可以請問在item裡面的button點擊後如何取到同一個item的textview的值或其他物件嗎
回覆刪除還不太懂那個view怎麼玩...我抓xml裡面那個物件,好像只會固定取第一個item而已
在click事件裡面已經取得了position,也就是點擊到第幾個資料列,就可以利用這個數值跟你塞進List裡面的array進行對應,取得你需要的值。
回覆刪除若是要用外部按鈕變更list內的內容,其實就是按下按鈕後,去改變array裡面的內容, 再重新繪製就可以了。
回覆刪除版大您好,想請教一下,
回覆刪除我想要用listview跟startActivity做一個商品列表,點擊蘋果用跑到蘋果的Activity,點擊橘子跑到橘子的Activity,
但這個Activity是同一個,也就是說這個Activity因為點擊的不同而有相對應的呈現,那我該如何讓listview傳值,使傳送過去的Activity知道要呈現甚麼面貌
1. 在click事件內,用Array進行判別,是點到蘋果還是橘子。
回覆刪除2. 要轉換Activity時,利用Bundle將參數代過去即可。
請問要怎麼讓使用者按下按鈕後,開啟另外一個activity呢??
回覆刪除我試過用intent,但是intente的setClass方法第一個參數怎麼填都不太對。
謝謝!
完整程式作法如下:
回覆刪除01.Studio 建一專案:名字自取 (如 Ola_ListView)
02. Activity: MainActivity, layout: activity_main, Title: MainActivity
Package: tw.com.wangshifu.ola
03. layout 新建 adapter_button.xml (即 Ola 步驟 1. adapter.xml)
04. java 新鍵 lv_BtnAdapter.java (即 Ola 步驟 2. adapter 類別)
05. 加入 protected void putDataToListView() 於
新建的 MainActivity.java 內 onCreate 之後
06. 於 新建的 MainActivity.java
新增 private ListView lv;
07. onCreate 內新增:
lv = (ListView) findViewById(R.id.mlistView);
putDataToListView();
08. onCreate() 後複製
protected void putDataToListView() (即 Ola 步驟 3. putDataToListView() )
09. 於 string.xml 新增
隨便填
10. 於 layout下 拉一個 id 改為 mListView
android:id="@+id/mlistView" (配合 onCreate 內的 lv 變數)
修正(MainActivity)內容
public class MainActivity extends Activity {
private ListView lv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lv = (ListView) findViewById(R.id.mlistView); //新增
putDataToListView(); //新增
}
protected void putDataToListView() … (複製 Ola 步驟 3. 的內容)
09. 於 string.xml 新增 應該是:
回覆刪除要新建 string name="adapter_btnText" 隨便填命名
不好意思有個問題想請教一下
回覆刪除我在listview中的每行設置了一個editText跟Button
由於editText跟Button的值在最初生成的時候就已經設定好了
後來修正HsahMap的內容沒有用
所以想請教一下要怎麼樣去修改ListView中的每行的物件的值
It's really helpful for me
回覆刪除tks a lot!!