我希望我的ListView包含按钮,但在活动设置按钮的XML属性的onClick =“myFunction的”,然后将一个公共无效myFunction的(android.view.View视图)方法会导致NoSuchMethodException(堆栈跟踪为null)到被抛出,因为虽然onClick的监听器那里,它不火myFunction的(...),并导致活性关闭。
如何创建在一个ListView中的每一行View.OnClickListener连接到一个按钮自定义适配器?
我的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>
下面是如何创建自定义适配器,View.OnClickListener连接到ListView,每行一个按钮...
1.创建一个典型的行布局
在这种情况下,行由三个视图组件组成:
- 名字(EditText上)
- 值(的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处理程序,因为我们要参考其作用于特定的列表项。
这样做意味着该处理器将在活动文件中实施,每个按钮都知道它属于哪个列表项。
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;
}
}
在这里,我们列出了支付类项目。
这里有三个最重要的因素:
- PayListAdapter构造:设置一些私人领域,并调用父类的构造。 这也得到支付对象的列表。 它的实施是强制性的。
- PaymentHolder:保存到我在这个列表项来设置所有视图引用静态类。 我还留着支付对象,在列表中选择此特定项目的引用。 我将它作为标记为ImageButton的,这将帮助我找到名单上的付款项,用户想要删除
- 重写getView方法:通过超调用。 它的目标是返回一个列表行。 我们创造的领域和设置它们的值,并将其存储在静态持有。 持有人则放在行的标签元素。 请注意,有一个性能问题,因为该行每显示一次被重新创建。 我用添加一些标志像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.添加ListView控件到活动布局xml文件
在其simpliest形式,这将足以添加此视图活动布局:
<ListView
android:id="@+id/EnterPays_PaysList"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
</ListView>
4.设置适配器在活动Java代码此列表视图
为了在ListView中显示的项目,你需要设置它的适配器,并将其映射到支付对象的一些其他的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);
5.添加/删除项目的ListView(和它的适配器)
适配器的处理就像任何其他的ArrayList,因此增加新的元素是简单的:
Payment testPayment = new Payment("Test", 13);
adapter.add(testPayment);
adapter.remove(testPayment);
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);
}
支付对象存储在的ImageButton的标签元素。 现在,它足以从标签读取它,从适配器删除此项目。
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的活动的方法:
OnClickListener removeCalcButtonClickListener = new OnClickListener() {
public void onClick(View v) {
showDialog(DIALOG_REMOVE_CALC);
}
};
ShowDialog的(int)方法调用onCreateDialog(在活性的类定义)。 覆盖它并告诉你的应用程序,如果被请求的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);
}
}
看看这个博客帖子我就正好此事写道:
创建自定义ArrayAdapter
有解释的每一个动作我做的适配器评论。 这里是短的解释:
因此,让例如采用一排,你要放置一个CheckBox
, ImageView
和TextView
,而所有的人都点击。 这意味着你可以单击行它的自我为走向另一个Actvity
,详细了解该行,检查其CHEC 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代码中第二界定一个ViewHolder
,一个ViewHolder
旨在更快地持有该行的观点和方法操作是:
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文件被创建,在这种情况下,我使用的“列表”的ID在XML文件中定义的一个列表:
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.最后,如果我们希望能够按在它的排它的自我,而不是只有在一定的看法,我们应该分配onItemClickListener
的ListView
:
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();
}
});
首先,使用添加的onClick =“功能” XML中的听众的方式被弃用。 你需要一个ViewHolder类在XML链接按钮,您的Java代码。 然后你就可以实现该onClickListener。
里面的getView()实现CustomAdapter的,你可以尝试像下面。
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;
}
文章来源: ListView row buttons: How do I create a custom Adapter that connects a View.OnClickListener to a button on each row of a ListView?