2

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

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

我的 ListView 创建如下...

[activity.java 内容..]

public void myFunction(android.view.View view)
{
    //Do stuff
}

[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>

[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>
4

4 回答 4

9

以下是如何创建自定义适配器,将 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);
    }
}
于 2013-04-18T08:17:51.550 回答
3

看看我就这个问题写的这篇博文:

创建自定义 ArrayAdapter

有一些注释解释了我在适配器中所做的每一个动作。这是简短的解释:

因此,例如,让我们在您要放置 a 的地方取一行,CheckBoxImageView 所有TextView这些都是可点击的。这意味着您可以单击它自己的行以转到另一个Actvity以获取有关该行的更多详细信息,检查其检查k框或按ImageView执行另一个操作。

所以你应该做的是:

1.首先为您的行创建一个XML layout文件:ListView

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<CheckBox
    android:id="@+id/cbCheckListItem"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="10dp" />
<TextView
    android:id="@+id/tvItemTitle"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="item string" />
<ImageView
    android:id="@+id/iStatus"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:clickable="true"
    android:contentDescription="@drawable/ic_launcher"
    android:src="@drawable/ic_launcher" /> 
  </LinearLayout>

2.第二个在你的java代码中定义a ViewHolder,aViewHolder 旨在保存行视图,这样操作更快:

static class ViewHolder 
{
TextView title;
CheckBox checked;
ImageView changeRowStatus;
}

3.现在我们必须定义CustomArrayAdapter,使用数组适配器,我们可以根据该行的内容或其位置精确地定义每行所需的输出。我们可以通过重写 getView 方法来做到这一点:

private class CustomArrayAdapter extends ArrayAdapter<RowData>
{   
private ArrayList<RowData> list;

//this custom adapter receives an ArrayList of RowData objects.
//RowData is my class that represents the data for a single row and could be anything.
public CustomArrayAdapter(Context context, int textViewResourceId, ArrayList<RowData> rowDataList) 
{
    //populate the local list with data.
    super(context, textViewResourceId, rowDataList);
    this.list = new ArrayList<RowData>();
    this.list.addAll(rowDataList);
}

public View getView(final int position, View convertView, ViewGroup parent)
{
    //creating the ViewHolder we defined earlier.
    ViewHolder holder = new ViewHolder();) 

    //creating LayoutInflator for inflating the row layout.
    LayoutInflater inflator = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);

    //inflating the row layout we defined earlier.
    convertView = inflator.inflate(R.layout.row_item_layout, null);

    //setting the views into the ViewHolder.
    holder.title = (TextView) convertView.findViewById(R.id.tvItemTitle);
    holder.changeRowStatus = (ImageView) convertView.findViewById(R.id.iStatus);
    holder.changeRowStatus.setTag(position);

    //define an onClickListener for the ImageView.
    holder.changeRowStatus.setOnClickListener(new OnClickListener() 
    {           
        @Override
        public void onClick(View v) 
        {
            Toast.makeText(activity, "Image from row " + position + " was pressed", Toast.LENGTH_LONG).show();
        }
    });
    holder.checked = (CheckBox) convertView.findViewById(R.id.cbCheckListItem);
    holder.checked.setTag(position);

    //define an onClickListener for the CheckBox.
    holder.checked.setOnClickListener(new OnClickListener() 
    {       
        @Override
        public void onClick(View v)
        {
            //assign check-box state to the corresponding object in list.    
            CheckBox checkbox = (CheckBox) v;
            rowDataList.get(position).setChecked(checkbox.isChecked());
            Toast.makeText(activity, "CheckBox from row " + position + " was checked", Toast.LENGTH_LONG).show();    
        }
    });

    //setting data into the the ViewHolder.
    holder.title.setText(RowData.getName());
    holder.checked.setChecked(RowData.isChecked());

    //return the row view.
    return convertView;
}
}

4.现在你需要设置这个适配器,作为你的适配器ListView。这ListView可以在 java 中创建或使用 XML 文件创建,在这种情况下,我使用的是在 XML 文件中使用“列表”ID 定义的列表:

public void onCreate(Bundle savedInstanceState) 
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout); 
ListView list = (ListView)findViewById(R.id.list);
CustomArrayAdapter dataAdapter = new CustomArrayAdapter(this, R.id.tvItemTitle, rowDataList);
list.setAdapter(dataAdapter);
}

5.最后,如果我们希望能够按下它自己的行,而不仅仅是其中的某个视图,我们应该将 an 分配onItemClickListenerListView

list.setOnItemClickListener(new OnItemClickListener() 
{
public void onItemClick(AdapterView<?> parent, View view,int position, long id) 
{
    Toast.makeText(activity, "row " + position + " was pressed", Toast.LENGTH_LONG).show();
}
});
于 2013-04-18T08:32:52.727 回答
0

首先,不推荐使用 onClick="function" 在 xml 中添加侦听器的方式。您需要一个 ViewHolder 类来将 xml 中的按钮链接到您的 java 代码。然后你可以为此实现 onClickListener 。

于 2013-04-18T07:34:45.550 回答
0

在 CustomAdapter 的 getView() 实现中,您可以尝试如下所示。

   public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;

        if (convertView == null) {
            convertView = inflater.inflate(R.layout.xxxxx, null);
            holder = new ViewHolder();
            holder.invite = (Button) convertView.findViewById(R.id.button);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }


        final int pos = position;
        holder.button.setOnClickListener(new View.OnClickListener() {
                @Override
        public void onClick(View v) {
            handleClick(pos);   
            }
        });
 }

    class ViewHolder {
        Button button;
    }
于 2013-04-18T07:47:32.183 回答