ListView 自定义适配器 - 最后一行被删除而不是想要的行

ListView Custom Adapter - The Last Row Is Removed Instead of The Wanted One

我有一个自定义的 ListView 适配器,用于显示用户收到的好友请求。每行都有一个 "accept" 按钮。当用户单击该按钮时,我会在更新数据库时用进度条替换该按钮。完成此过程后,我想删除该行。

好吧,差不多就是这样了。

为了显示进度条我把按钮的可见性改成不可见,把进度条的可见性改成可见。

行被正确删除,数据库相应更新。问题是在我调用 notifyDataSetChanged 方法后,唯一删除的行是最后一行。

这里是我的代码片段的更新版本:

    public class FriendRequestsReceived_UserListAdapter extends GeneralListAdapter {

        private List<User> friendRequestsReceived_UserList;

        public FriendRequestsReceived_UserListAdapter(Context context, int resource, List<User> items) {
            super(context, resource, items);
            this.friendRequestsReceived_UserList=items;
        }

        private class ViewHolder {
            TextView userEmail_TextView;
            TextView name_TextView;
            Button acceptBtn;
            ProgressBar acceptProgressBar;
            User user;
        }

        @Override
        public View getView(int position, View view, ViewGroup parent) {
            ViewHolder holder;
            LayoutInflater inflater = LayoutInflater.from(getContext());

            if (view == null) {
                view = inflater.inflate(R.layout.friend_requests_received_listview_row, parent, false);

                holder = new ViewHolder();
                holder.userEmail_TextView = (TextView) view.findViewById(R.id.user_email);
                holder.name_TextView = (TextView) view.findViewById(R.id.user_name);
                holder.acceptBtn = (Button) view.findViewById(R.id.acceptRequestBtn);
                holder.acceptProgressBar = (ProgressBar) view.findViewById(R.id.acceptProgressBar);
                holder.user = (User) getItem(position);
                view.setTag(holder);
            } else
                holder = (ViewHolder) view.getTag();


            if (holder.user!=null) {
                holder.userEmail_TextView.setText(holder.user.getEmail());
                holder.name_TextView.setText(holder.user.getName());
                holder.acceptBtn.setTag(holder);
                holder.acceptBtn.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        acceptRequest(view);
                    }
                });
            }
            return view;
        }

        private void acceptRequest(View acceptButtonView){
            final ViewHolder vh = (ViewHolder) acceptButtonView.getTag();

            vh.acceptProgressBar.setVisibility(View.VISIBLE);
            vh.acceptBtn.setVisibility(View.INVISIBLE);

//Some code for updating database's related variables...

            mDatabase.updateChildren(dataForDatabase, new DatabaseReference.CompletionListener() {
                @Override
                public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {
                    if (databaseError != null) showConnectionErrorToast();
                    else {
                        Toast.makeText(getContext(), "Success", Toast.LENGTH_SHORT).show();
                        friendRequestsReceived_UserList.remove(vh.user);
                        notifyDataSetChanged();
                    }
                }
            });
        }
    }

我附上了这个 activity 的视频,它显示了问题:https://www.youtube.com/watch?v=9JoJ3RuRwsY


编辑:最新的工作代码:

public class FriendRequestsReceived_UserListAdapter extends GeneralListAdapter {

    private String loggedUser;
    private ConnectivityManager cm;
    private List<User> friendRequestsReceived_UserList;

    User working_user = null;

    public FriendRequestsReceived_UserListAdapter(Context context, int resource, List<User> items) {
        super(context, resource, items);
        this.friendRequestsReceived_UserList=items;
    }

    private class ViewHolder {
        Button acceptBtn;
        ProgressBar acceptProgressBar;
        User user;
    }

    @Override
    public View getView(int position, View view, ViewGroup parent) {
        LayoutInflater vi = LayoutInflater.from(getContext());

        view = vi.inflate(R.layout.friend_requests_received_listview_row, null);
        final ViewHolder holder = new ViewHolder();
        TextView userEmail_TextView = (TextView) view.findViewById(R.id.user_email);
        TextView name_TextView = (TextView) view.findViewById(R.id.user_name);
        Button acceptBtn = (Button) view.findViewById(R.id.acceptRequestBtn);
        ProgressBar acceptProgressBar = (ProgressBar) view.findViewById(R.id.acceptProgressBar);
        User myUser = (User) getItem(position);

        holder.acceptBtn=acceptBtn;
        holder.acceptProgressBar=acceptProgressBar;
        holder.user=myUser;

        acceptBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                acceptRequest(view, holder);
            }
        });


        userEmail_TextView.setText(myUser.getEmail());
        name_TextView.setText(myUser.getName());


        if (holder.user == working_user) {
            holder.acceptProgressBar.setVisibility(View.VISIBLE);
            holder.acceptBtn.setVisibility(View.INVISIBLE);
        }
        else {
            holder.acceptProgressBar.setVisibility(View.INVISIBLE);
            holder.acceptBtn.setVisibility(View.VISIBLE);
        }
        return view;
    }

    private void acceptRequest(View acceptButtonView, final ViewHolder holder){
        Context context=getContext();
        working_user = holder.user;

        holder.acceptProgressBar.setVisibility(View.VISIBLE);
        holder.acceptBtn.setVisibility(View.INVISIBLE);

        loggedUser=SaveSharedPreference.getLoggedEmail(context);
        final DatabaseReference mDatabase = FirebaseDatabase.getInstance().getReference();

        final String senderEmail=holder.user.getEmail();

        cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);

        if (cm.getActiveNetworkInfo()!=null) {
            mDatabase.child("Users").child(loggedUser).addListenerForSingleValueEvent(new ValueEventListener() {
                @Override
                public void onDataChange(DataSnapshot dataSnapshot) {
                    if (dataSnapshot.exists()){
                        mDatabase.child("Users").child(senderEmail).addListenerForSingleValueEvent(new ValueEventListener() {
                            @Override
                            public void onDataChange(DataSnapshot dataSnapshot) {
                                if (dataSnapshot.exists()){

                                    Map data = new HashMap();
                                    data.put("Users/"+loggedUser+"/friend_requests_received/"+senderEmail, null);
                                    data.put("Users/"+loggedUser+"/friends_list/"+senderEmail,true);

                                    mDatabase.updateChildren(data, new DatabaseReference.CompletionListener() {
                                        @Override
                                        public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {
                                            if (databaseError!=null)
                                                showConnectionErrorToast();

                                            else {//Success
                                                Context context=getContext();
                                                Toast.makeText(context,
                                                        "Success", Toast.LENGTH_SHORT).show();
                                                friendRequestsReceived_UserList.remove(holder.user);
                                                notifyDataSetChanged();

                                            }
                                        }
                                    });

                                }
                                else {
                                    showConnectionErrorToast();
                                }
                            }

                            @Override
                            public void onCancelled(DatabaseError databaseError) {
                                showConnectionErrorToast();
                            }
                        });
                    }
                    else {
                        showConnectionErrorToast();
                    }
                }

                @Override
                public void onCancelled(DatabaseError databaseError) {
                    showConnectionErrorToast();
                }
            });
        }
        else {
            showConnectionErrorToast();
        }
    }

这是我 think 发生的事情(无法访问 android studio atm)

假设您从两个用户 user1 和 user2 开始

当适配器初始化getCount时returns 2;

GetView 运行s 两次,一次用于位置 0,一次用于位置 1

这将创建两个视图 view1,其中包含 viewholder vh1,其中包含 user1 和 view2->vh2->user2

当您按下接受您时 运行 您的代码并设置视图 1 的可见性

然后你调用 notifydatasetchanged 使整个适配器无效

getcount 方法将 return 1

getview 方法将初始化位置 0,但将从初始化的 View 参数 (view1) 开始

该参数将其视图可见性设置为显示进度条并引用 user1

发生的事情是 user1 已从数据库中删除,但它仍然存在于 view1 的查看器中,而 view2 已被处置,因为自从 getcount returned 1

后不再需要它

这就是我 think 应该工作的:

首先声明一个实例变量:

User _working_user = null;

然后

@Override
public View getView(int position, View view, ViewGroup parent) {
    ViewHolder holder;
    LayoutInflater inflater = LayoutInflater.from(getContext());

    if (view == null) {
        view = inflater.inflate(R.layout.friend_requests_received_listview_row, parent, false);

        holder = new ViewHolder();
        holder.userEmail_TextView = (TextView) view.findViewById(R.id.user_email);
        holder.name_TextView = (TextView) view.findViewById(R.id.user_name);
        holder.acceptBtn = (Button) view.findViewById(R.id.acceptRequestBtn);
        holder.acceptProgressBar = (ProgressBar) view.findViewById(R.id.acceptProgressBar);

        holder.acceptBtn.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View view) {
                acceptRequest(view);
             }
         });

        view.setTag(holder);
    } else
        holder = (ViewHolder) view.getTag();

    holder.user = (User) getItem(position);
    holder.userEmail_TextView.setText(holder.user.getEmail());
    holder.name_TextView.setText(holder.user.getName());
    holder.acceptBtn.setTag(holder); //not a big fan of this btw, seems to be a cyclic reference

    if (holder.user == _working_user) {
        holder.acceptProgressBar.setVisibility(View.VISIBLE);
        holder.acceptBtn.setVisibility(View.INVISIBLE);
    } else {
        holder.acceptProgressBar.setVisibility(View.INVISIBLE);
        holder.acceptBtn.setVisibility(View.VISIBLE);
    }


    return view;
}

private void acceptRequest(View acceptButtonView){
        final ViewHolder vh = (ViewHolder) acceptButtonView.getTag();

        _working_user = vh.user; 

        vh.acceptProgressBar.setVisibility(View.VISIBLE);
        vh.acceptBtn.setVisibility(View.INVISIBLE);
        //thew above two lines could be replaced with a notifyDataSetChanged


        //Some code for updating database's related variables...

        mDatabase.updateChildren(dataForDatabase, new DatabaseReference.CompletionListener() {
            @Override
            public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {
                if (databaseError != null) showConnectionErrorToast();
                else {
                    Toast.makeText(getContext(), "Success", Toast.LENGTH_SHORT).show();
                    friendRequestsReceived_UserList.remove(vh.user);
                    notifyDataSetChanged();
                }
            }
        });
    }

现在上面的应该可以了,但这里有一些注意事项

我们使用 _working_user 是因为在数据库内容执行时,其他东西可能会调用 NotifyDataSetChanged(),如果没有那样做,理论上应该不需要它

我不太喜欢 onclick 处理程序传递视图这一事实,您可以称之为个人偏好,我要做的是在 if (view==null) 块中声明点击处理程序,然后使用视图持有者的用户对象调用接受参数