【问题标题】:Setting click listener on button in Listview row layout在 Listview 行布局中的按钮上设置单击侦听器
【发布时间】:2018-01-27 12:53:08
【问题描述】:

我有ListView,如下图所示

每个白框都是一个单独的ListView 行。每个ListView 行的右上角都有一个Button

我想要什么

我正在尝试在每一行的Button 上设置一个OnClickListener。每当单击此Button 时,我都想删除该行。

每个ListView 行中的数据来自Firebase 数据库。因此,每当在ListView 的任何行中单击交叉Button 时,我都会得到Timestamp 的值,然后将此Timestamp 作为参数传递给在数据库中搜索此Timestamp 的单独方法. Firebase 数据库中以 Timestamp 为子项的任何键都将被删除。

用于删除ListView 行数据的方法工作正常,因为它删除了与在此方法中作为参数传递的Timestamp 关联的数据。

问题

问题是我在任何ListView 上单击按钮时得到的Timestamp 值是错误的。例如,在上图中,如果我单击带有27-01-2018 05-31-22AMTimestamp 的第二行ListView,而不是得到这个Timestamp,我得到的是ListView 最后一行的Timestamp,因此删除了错误的数据。

以下是相关代码:

onClick() 方法

@Override
public void onClick(View view) {
        //get the parent view i.e. listview row on which delete button is clicked
        ConstraintLayout listviewRow = (ConstraintLayout) btnDeleteMsg.getParent();

        //get the textview containing receiver username from the view on which delete button is clicked
        TextView textviewReceiver = listviewRow.findViewById(R.id.outbox_msgReceiverUsername);

        //get the textview containing timestamp value from the view on which delete button is clicked
        TextView textTimestamp = listviewRow.findViewById(R.id.outbox_msgSentTime);

        //get the values of message receiver username and timestamp
        //timestamp value will be used to determine which message to delete
        //from database
        String receiverUsername = textviewReceiver.getText().toString();
        String timestamp = textTimestamp.getText().toString();

        //call delSentMsg() method in SentMessages Fragment using SentMessages Fragment
        //instance passed in the constructor of this class
        sentMsgsFragment.deleteSentMsg(receiverUsername, timestamp);
}

ListView 行布局

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:padding="15dp"
    android:background="#fff">

    <Button
        android:id="@+id/btn_deleteMsg_SMF"
        android:layout_width="17dp"
        android:layout_height="17dp"
        android:background="@drawable/cross_image"
        app:layout_constraintRight_toRightOf="parent" />

    <TextView
        android:id="@+id/outbox_msgText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="#000"
        android:textSize="22sp"
        android:paddingTop="10dp"
        app:layout_constraintTop_toBottomOf="@id/btn_deleteMsg_SMF"/>

    <TextView
        android:id="@+id/outbox_msgReceiverLabel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/to_text_label"
        android:textColor="#000"
        android:textSize="18sp"
        app:layout_constraintTop_toBottomOf="@id/outbox_msgText"
        app:layout_constraintLeft_toLeftOf="parent"
        android:paddingTop="10dp"/>

    <TextView
        android:id="@+id/outbox_msgReceiverUsername"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="18sp"
        app:layout_constraintLeft_toRightOf="@id/outbox_msgReceiverLabel"
        android:layout_marginLeft="10dp"
        app:layout_constraintTop_toBottomOf="@id/outbox_msgText"
        android:paddingTop="10dp"/>

    <TextView
        android:id="@+id/outbox_msgDateLabel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/sent_on_label"
        android:textColor="#000"
        android:textSize="18sp"
        app:layout_constraintTop_toBottomOf="@id/outbox_msgReceiverLabel"
        app:layout_constraintLeft_toLeftOf="parent"
        android:paddingTop="10dp"/>

    <TextView
        android:id="@+id/outbox_msgSentTime"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="16sp"
        app:layout_constraintLeft_toRightOf="@id/outbox_msgDateLabel"
        app:layout_constraintTop_toBottomOf="@id/outbox_msgReceiverLabel"
        android:layout_marginLeft="10dp"
        app:layout_constraintBaseline_toBaselineOf="@id/outbox_msgDateLabel"
        android:paddingTop="10dp"/>

</android.support.constraint.ConstraintLayout>

ListView 适配器类

public class CustomOutboxListAdapter extends BaseAdapter implements View.OnClickListener{

    private ArrayList<SentMessageTemplate> sentMsgsList;
    private Context context;
    private Button btnDeleteMsg;
    private SentMessages sentMsgsFragment;

    public CustomOutboxListAdapter(ArrayList<SentMessageTemplate> list, Context cont, SentMessages sm){
        this.sentMsgsList = list;
        this.context = cont;
        this.sentMsgsFragment = sm;
    }

    @Override
    public int getCount() {
        return this.sentMsgsList.size();
    }

    @Override
    public Object getItem(int position) {
        return this.sentMsgsList.get(position);
    }

    @Override
    public long getItemId(int i) {
        return i;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder = null;

        if(convertView == null){
            LayoutInflater inf = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inf.inflate(R.layout.listview_outbox_messages_row, null);

            //private inner class used to minimize the calls to "findViewById" method
            holder = new ViewHolder();
            holder.datetimeText = convertView.findViewById(R.id.outbox_msgSentTime);
            holder.messageText = convertView.findViewById(R.id.outbox_msgText);
            holder.receiverUsernameText = convertView.findViewById(R.id.outbox_msgReceiverUsername);

            convertView.setTag(holder);

            //click listener for delete button
            btnDeleteMsg = convertView.findViewById(R.id.btn_deleteMsg_SMF);
            btnDeleteMsg.setOnClickListener(this);
        }
        else {
            holder = (ViewHolder)convertView.getTag();
        }

        SentMessageTemplate stu = sentMsgsList.get(position);
        holder.datetimeText.setText(stu.getTimestamp());
        holder.messageText.setText(stu.getMessageContent());
        holder.receiverUsernameText.setText(stu.getMsgReceiver());

        return convertView;
    }

    @Override
    public void onClick(View view) {
        //get the parent view i.e. listview row on which delete button is clicked
        ConstraintLayout listviewRow = (ConstraintLayout) btnDeleteMsg.getParent();
        //get the textview containing receiver username from the view on which delete button is clicked
        TextView textviewReceiver = listviewRow.findViewById(R.id.outbox_msgReceiverUsername);
        //get the textview containing timestamp value from the view on which delete button is clicked
        TextView textTimestamp = listviewRow.findViewById(R.id.outbox_msgSentTime);
        //get the values of message receiver username and timestamp
        //timestamp value will be used to determine which message to delete
        //from database
        String receiverUsername = textviewReceiver.getText().toString();
        String timestamp = textTimestamp.getText().toString();
        //call delSentMsg() method in SentMessages Fragment using SentMessages Fragment
        //instance passed in the constructor of this class
        sentMsgsFragment.deleteSentMsg(receiverUsername, timestamp);
    }

    private static class ViewHolder{
        public TextView datetimeText;
        public TextView messageText;
        public TextView receiverUsernameText;
    }
}

问题

我认为问题在于我无法正确获取单击交叉按钮的ListView 行。单击按钮时如何确定ListView所在的行?

【问题讨论】:

  • 请添加 ListView 适配器代码
  • @fisher3421 已添加。

标签: android listview onclicklistener


【解决方案1】:

尝试对CustomOutboxListAdapter 类进行这些更改。

CustomOutboxListAdapter 中不要实现View.OnClickListener。更改它的类声明:

public class CustomOutboxListAdapter extends BaseAdapter implements View.OnClickListener{

到:

public class CustomOutboxListAdapter extends BaseAdapter {

并去掉里面的onClick方法:

@Override
public void onClick(View view) {

public View getView(int position, View convertView, ViewGroup parent) 替换为:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder holder = null;

    if(convertView == null){
        LayoutInflater inf = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = inf.inflate(R.layout.listview_outbox_messages_row, null);

        //private inner class used to minimize the calls to "findViewById" method
        holder = new ViewHolder();
        holder.datetimeText = convertView.findViewById(R.id.outbox_msgSentTime);
        holder.messageText = convertView.findViewById(R.id.outbox_msgText);
        holder.receiverUsernameText = convertView.findViewById(R.id.outbox_msgReceiverUsername);

        convertView.setTag(holder);
    }
    else {
        holder = (ViewHolder)convertView.getTag();
    }

    SentMessageTemplate stu = sentMsgsList.get(position);
    holder.datetimeText.setText(stu.getTimestamp());
    holder.messageText.setText(stu.getMessageContent());
    holder.receiverUsernameText.setText(stu.getMsgReceiver());


    // use local variable so it is always referenced correctly
    Button deleteBtn = convertView.findViewById(R.id.btn_deleteMsg_SMF);
    // Cache row position inside the button using `setTag`
    deleteBtn.setTag(position); 

    // Attach the click event handler
    deleteBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            int position = (Integer) view.getTag();

            // Access the row position here to get the correct data item
            SentMessageTemplate sentMessageTemplate = getItem(position);

            String receiverUsername = sentMessageTemplate.getTimestamp();
            String timestamp = sentMessageTemplate.getMsgReceiver();

            sentMsgsFragment.deleteSentMsg(receiverUsername, timestamp);
        }
    });

    return convertView;
}

只需删除不再使用的成员变量private Button btnDeleteMsg;即可。

对于getItemId(int i) 的实现,我认为应该是这样的:

@Override
public long getItemId( int position ) {
    return this.sentMsgsList.get(position).getId();
}

那么在SentMessageTemplate类中,它应该实现:

public int getId() {
    // return unique ID for object
    ....
}

【讨论】:

  • 感谢您的回复,我打算尝试您建议的解决方案,但我对 返回每个对象的唯一 IDgetID 方法有点困惑。我没有为SentMessageTemplate 类的每个实例设置任何唯一ID,那么如何为SentMessageTemplate 的每个实例设置唯一ID。
  • 那么对于 getId (int position) ,只需回到您之前返回 position 的实现即可。随意尝试其余的代码,看看它是否有帮助,祝你好运!
  • getView 方法中覆盖onClick() 方法解决了这个问题。
【解决方案2】:

错误是您仅在适配器为ListView 创建新视图时才调用btnDeleteMsg.setOnClickListener(this);。它仅在应用程序启动时发生,之后 ListView 重用视图并且不调用btnDeleteMsg.setOnClickListener(this);。因此,要解决您的问题,您应该将此行移到holder.receiverUsernameText.setText(stu.getMsgReceiver());

下面

如下所示

@Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder = null;

        if(convertView == null){
            LayoutInflater inf = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inf.inflate(R.layout.listview_outbox_messages_row, null);

            //private inner class used to minimize the calls to "findViewById" method
            holder = new ViewHolder();
            holder.datetimeText = convertView.findViewById(R.id.outbox_msgSentTime);
            holder.messageText = convertView.findViewById(R.id.outbox_msgText);
            holder.receiverUsernameText = convertView.findViewById(R.id.outbox_msgReceiverUsername);

            convertView.setTag(holder);
        }
        else {
            holder = (ViewHolder)convertView.getTag();
        }

        SentMessageTemplate stu = sentMsgsList.get(position);
        holder.datetimeText.setText(stu.getTimestamp());
        holder.messageText.setText(stu.getMessageContent());
        holder.receiverUsernameText.setText(stu.getMsgReceiver());
        //click listener for delete button
        btnDeleteMsg = convertView.findViewById(R.id.btn_deleteMsg_SMF);
        btnDeleteMsg.setOnClickListener(this);


        return convertView;
    }

【讨论】:

  • 感谢您的回复,我已经尝试过这个解决方案,但问题仍然存在。
【解决方案3】:

我不确定我是否完全理解了这个问题,但是:

您可以通过在视图上调用 setId 来设置 ID。为您的行提供唯一编号。然后在 onClick 函数中使用 getId 检索 ID,以了解单击了哪一行,以便将其删除。这比使用 Timestamp 更好,因为它们可能会巧合重叠,而您可以确保 ID 都是不同的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-07-31
    • 2011-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-25
    相关资源
    最近更新 更多