以下是如何创建自定义适配器,将 View.OnClickListener 连接到 ListView,每行有一个按钮......
1.为典型行创建布局
在这种情况下,该行由三个视图组件组成:
- 名称(编辑文本)
- 值 (EditText:inputType="numberDecimal")
- 删除(按钮)
xml
pay_list_item.xml 布局如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<EditText
android:id="@+id/pay_name"
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_weight="2"
android:hint="Name" />
<EditText
android:id="@+id/pay_value"
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_weight="1"
android:inputType="numberDecimal"
android:text="0.0" />
<Button
android:id="@+id/pay_removePay"
android:layout_width="100dp"
android:layout_height="fill_parent"
android:text="Remove Pay"
android:onClick="removePayOnClickHandler" />
</LinearLayout>
注意:按钮在 xml 布局文件中定义了 onClick 处理程序,因为我们希望将其操作引用到特定的列表项。
这样做意味着处理程序将在 Activity 文件中实现,并且每个按钮都将知道它属于哪个列表项。
2.创建列表项适配器
这是作为 pay_list_item.xml 控制器的 java 类。
它保留所有视图的引用,并将这些引用放在标签中,扩展 ArrayAdapter 接口。
适配器:
public class PayListAdapter extends ArrayAdapter<Payment> {
private List<Payment> items;
private int layoutResourceId;
private Context context;
public PayListAdapter(Context context, int layoutResourceId, List<Payment> items) {
super(context, layoutResourceId, items);
this.layoutResourceId = layoutResourceId;
this.context = context;
this.items = items;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
PaymentHolder holder = null;
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
row = inflater.inflate(layoutResourceId, parent, false);
holder = new PaymentHolder();
holder.Payment = items.get(position);
holder.removePaymentButton = (ImageButton)row.findViewById(R.id.pay_removePay);
holder.removePaymentButton.setTag(holder.Payment);
holder.name = (TextView)row.findViewById(R.id.pay_name);
holder.value = (TextView)row.findViewById(R.id.pay_value);
row.setTag(holder);
setupItem(holder);
return row;
}
private void setupItem(PaymentHolder holder) {
holder.name.setText(holder.Payment.getName());
holder.value.setText(String.valueOf(holder.Payment.getValue()));
}
public static class PaymentHolder {
Payment Payment;
TextView name;
TextView value;
ImageButton removePaymentButton;
}
}
在这里,我们列出了 Payment 类项目。
这里有三个最重要的元素:
- PayListAdapter 构造函数:设置一些私有字段并调用超类构造函数。它还获取付款对象列表。它的实施是强制性的。
- PaymentHolder:静态类,包含对我必须在此列表项中设置的所有视图的引用。我还将引用此特定项目的 Payment 对象保留在列表中。我将它设置为 ImageButton 的标签,这将帮助我在列表中找到用户想要删除的付款项目
- 重写的 getView 方法:由超类调用。它的目标是返回单个 List 行。我们创建它的字段并设置它们的值并将它们存储在静态容器中。然后将持有人放在行的标签元素中。请注意,存在性能问题,因为每次显示时都会重新创建该行。我曾经在持有者中添加一些标志,如 isCreated,并在创建行后将其设置为 true。那么您可以添加 if 语句并读取标签的持有者,而不是从头开始创建它。
Payment.java 目前非常简单,它看起来有点像 BasicNameValuePair:
public class Payment implements Serializable {
private String name = "";
private double value = 0;
public Payment(String name, double value) {
this.setName(name);
this.setValue(value);
}
...
}
每个未显示的私有字段都有额外的获取和设置。
3.在activity布局xml文件中添加ListView
以最简单的形式,将这个视图添加到活动布局中就足够了:
<ListView
android:id="@+id/EnterPays_PaysList"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
</ListView>
4. 在 Activity Java 代码中设置此列表视图的适配器
为了在 ListView 中显示项目,您需要设置它的适配器并将其映射到其他一些支付对象的 ArrayList(因为我在这里扩展了一个 Array 适配器)。下面是负责将适配器绑定到 editPersonData.getPayments() ArrayList 的代码:
PayListAdapter adapter = new PayListAdapter(AddNewPerson.this, R.layout.pay_list_item, editPersonData.getPayments());
ListView PaysListView = (ListView)findViewById(R.id.EnterPays_PaysList);
PaysListView.setAdapter(adapter);
5. 向 ListView(及其适配器)添加/删除项目
Adapter 的处理方式与任何其他 ArrayList 一样,因此向其添加新元素非常简单:
Payment testPayment = new Payment("Test", 13);
adapter.add(testPayment);
adapter.remove(testPayment);
6.处理Remove Payment按钮点击事件
在显示 ListView 的活动代码中,添加将处理删除按钮单击操作的公共方法。方法名称必须与 pay_list_item.xml 中的名称完全相同:
android:onClick="removePayOnClickHandler"
The method body is as follows:
public void removePayOnClickHandler(View v) {
Payment itemToRemove = (Payment)v.getTag();
adapter.remove(itemToRemove);
}
Payment 对象存储在 ImageButton 的 Tag 元素中。现在从 Tag 中读取它就足够了,并从适配器中删除该项目。
7.合并删除确认对话框窗口
可能您还需要通过在确认对话框中向他询问其他问题来确保用户故意按下删除按钮。
对话
a) 创建对话框的 id 常量
这只是对话框的 ID。它在当前活动处理的任何其他对话框窗口中应该是唯一的。我是这样设置的:
protected static final int DIALOG_REMOVE_CALC = 1;
protected static final int DIALOG_REMOVE_PERSON = 2;
b) 构建对话框
我使用这种方法来构建对话窗口:
private Dialog createDialogRemoveConfirm(final int dialogRemove) {
return new AlertDialog.Builder(getApplicationContext())
.setIcon(R.drawable.trashbin_icon)
.setTitle(R.string.calculation_dialog_remove_text)
.setPositiveButton(R.string.calculation_dialog_button_ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
handleRemoveConfirm(dialogRemove);
}
})
.setNegativeButton(R.string.calculation_dialog_button_cancel, null)
.create();
}
这里使用了 AlertDialog 构建器模式。我不处理 NegativeButton 点击动作——默认情况下对话框只是被隐藏。如果单击对话框的确认按钮,则会调用我的 handleRemoveConfirm 回调并根据对话框的 ID 执行操作:
protected void handleRemoveConfirm(int dialogType) {
if(dialogType == DIALOG_REMOVE_PERSON){
calc.removePerson();
}else if(dialogType == DIALOG_REMOVE_CALC){
removeCalc();
}
}
c) 显示对话框
单击删除按钮后,我会显示对话框。showDialog(int) 是 Android 的 Activity 方法:
OnClickListener removeCalcButtonClickListener = new OnClickListener() {
public void onClick(View v) {
showDialog(DIALOG_REMOVE_CALC);
}
};
showDialog(int) 方法调用 onCreateDialog(也在 Activity 的类中定义)。覆盖它并告诉您的应用程序在请求 showDialog 时该怎么做:
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_REMOVE_CALC:
return createDialogRemoveConfirm(DIALOG_REMOVE_CALC);
case DIALOG_REMOVE_PERSON:
return createDialogRemoveConfirm(DIALOG_REMOVE_PERSON);
}
}