【问题标题】:ListView Custom Adapter - The Last Row Is Removed Instead of The Wanted OneListView 自定义适配器 - 最后一行被删除而不是想要的
【发布时间】:2017-08-23 19:59:57
【问题描述】:

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

嗯,差不多就是这样。

为了显示进度条,我将按钮的可见性更改为不可见,并将进度条的可见性更改为可见。

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

这里是我的代码 sn-p 的更新版本:

    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();
                    }
                }
            });
        }
    }

我附上此活动的视频,显示问题: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();
        }
    }

【问题讨论】:

    标签: android listview firebase listview-adapter


    【解决方案1】:

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

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

    适配器初始化时getCount返回2;

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

    这会创建两个视图 view1,其中 viewholder vh1 里面有 user1 和 view2->vh2->user2

    当您按下接受时,您将运行您的代码并设置 view1 的可见性

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

    getcount 方法将返回 1

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

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

    发生的情况是 user1 已从数据库中删除,但仍存在于 view1 的 viewholder 中,并且 view2 已被释放,因为 getcount 返回 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) 块中声明点击处理程序,然后调用 accept视图持有者的用户对象的参数

    【讨论】:

    • 非常感谢您的详细解答!修复了问题!
    猜你喜欢
    • 2022-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-16
    • 1970-01-01
    相关资源
    最近更新 更多