ListView行按钮:如何创建将View.OnClickListener连接到ListView每行上的按钮的自定义适配器?

Kni*_*ins 2 java android listview onclick button

我希望我的ListView包含按钮,但设置按钮的xml属性,onClick ="myFunction",然后在活动中放置一个public void myFunction(android.view.View视图)方法会导致NoSuchMethodException(堆栈跟踪为null)抛出,因为虽然onclick监听器在那里,它不会触发myFunction(...)并导致活动关闭.

如何创建将View.OnClickListener连接到ListView的每一行上的按钮的自定义适配器?

我的ListView创建如下...

[activity.java content ..]

public void myFunction(android.view.View view)
{
    //Do stuff
}
Run Code Online (Sandbox Code Playgroud)

[activity.xml内容..]

<LinearLayout xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".FrmCustomerDetails" >
    <ListView android:id="@+id/LstCustomerDetailsList" android:layout_width="fill_parent" android:layout_height="0dip" android:layout_weight="1" android:clickable="true" android:clipChildren="true" android:divider="@null" android:dividerHeight="0dp" android:fastScrollEnabled="true" android:footerDividersEnabled="false" android:headerDividersEnabled="false" android:requiresFadingEdge="vertical" android:smoothScrollbar="true" />
</LinearLayout>
Run Code Online (Sandbox Code Playgroud)

[activity_row_item.xml内容..]

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/Llt" android:layout_width="match_parent" android:layout_height="match_parent" >
    <Button android:id="@+id/Btn" android:text="Click me" android:onClick="myFunction" />
</LinearLayout>
Run Code Online (Sandbox Code Playgroud)

Kni*_*ins 9

以下是如何创建自定义适配器,将View.OnClickListener连接到ListView,每行一个按钮...

1.为典型行创建布局

在这种情况下,该行由三个视图组件组成:

  • 名称(EditText)
  • value(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>
Run Code Online (Sandbox Code Playgroud)

注意:该按钮具有在xml布局文件中定义的onClick处理程序,因为我们希望将其操作引用到特定的列表项.

这样做意味着处理程序将在Activity文件中实现,每个按钮将知道它属于哪个列表项.

2.创建列表项适配器

这是java类,它是pay_list_item.xml的控制器.

它保留了对其所有视图的引用,并且还将这些引用放在标记中,从而扩展了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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这里我们列出付款类项目.

这里有三个最重要的元素:

  • PayListAdapter构造函数:设置一些私有字段并调用超类构造函数.它还获取付款对象列表.它的实施是强制性的.
  • PaymentHolder:包含我必须在此列表项中设置的所有视图的引用的静态类.我还保留了引用列表中此特定项的Payment对象.我将其设置为ImageButton的标记,这将帮助我找到用户想要删除的列表上的付款项目
  • Overriden 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);
    }
...
}
Run Code Online (Sandbox Code Playgroud)

对于未显示的每个私有字段,还有其他获取和集合.

3.将ListView添加到活动布局xml文件中

在最简单的形式中,将此视图添加到活动布局就足够了:

<ListView 
    android:id="@+id/EnterPays_PaysList"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content">
</ListView>
Run Code Online (Sandbox Code Playgroud)

4.在Activity Java代码中将适配器设置为此列表视图

为了在ListView中显示项目,您需要设置其适配器并将其映射到Payment对象的其他ArrayList(因为我在这里扩展了一个数组适配器).这是负责将适配器绑定到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);
Run Code Online (Sandbox Code Playgroud)

5.向ListView(及其适配器)添加/删除项目

适配器的处理方式与任何其他ArrayList一样,因此向其添加新元素非常简单:

Payment testPayment = new Payment("Test", 13);
adapter.add(testPayment);
adapter.remove(testPayment);
Run Code Online (Sandbox Code Playgroud)

6.处理删除付款按钮单击事件

在显示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);
}
Run Code Online (Sandbox Code Playgroud)

Payment对象存储在ImageButton的Tag元素中.现在只需从Tag中读取它,并从适配器中删除此项.

7.合并删除确认对话窗口

也许您还需要确保用户通过在确认对话框中询问其他问题来故意按下删除按钮.

对话

a)创建对话框的id常量

这只是对话框的ID.它应该是当前活动处理的任何其他对话窗口中唯一的.我这样设置:

protected static final int DIALOG_REMOVE_CALC = 1;
protected static final int DIALOG_REMOVE_PERSON = 2;
Run Code Online (Sandbox Code Playgroud)

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();
}
Run Code Online (Sandbox Code Playgroud)

这里使用AlertDialog构建器模式.我不处理NegativeButton点击操作 - 默认情况下,对话框只是被隐藏.如果单击对话框的确认按钮,则会调用handleRemoveConfirm回调并根据对话框的ID执行操作:

protected void handleRemoveConfirm(int dialogType) {
    if(dialogType == DIALOG_REMOVE_PERSON){
        calc.removePerson();
    }else if(dialogType == DIALOG_REMOVE_CALC){
        removeCalc();
    }
}
Run Code Online (Sandbox Code Playgroud)

c)显示对话框

单击删除按钮后显示对话框.showDialog(int)是Android的Activity的方法:

OnClickListener removeCalcButtonClickListener = new OnClickListener() {
    public void onClick(View v) {
        showDialog(DIALOG_REMOVE_CALC);
    }
};
Run Code Online (Sandbox Code Playgroud)

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 因为这是我博客文章中的精确复制粘贴:http://looksok.wordpress.com/2012/11/03/android-custom-listview-tutorial/你可以编辑你的答案并添加一个关于它的注释/网址或只是链接到网址? (5认同)