【问题标题】:Problem deleting entry from Recycler View从回收站视图中删除条目时出现问题
【发布时间】:2020-07-31 07:48:28
【问题描述】:

这个问题快把我逼疯了。到目前为止,我已经尝试了 20 种不同的方法。所以让我们看看这里是否有人可以帮助我。

我正在制作一个使用RecyclerView 的待办事项列表应用程序,它将数据存储在 Firebase 中。

该应用程序有一个TasksActivity,其中所有任务都出现在RecyclerView 中。我有一个按钮可以进入任务创建对话框。我可以创建任务,然后它们出现在RecyclerView(任务活动)中,并在火力库中更新,没有问题。我可以关闭应用程序并稍后再回来,一切正常,当我加载它时,所有条目都会再次出现在应用程序中。我也可以滑动删除一个条目,该条目也会从 firebase 的数据库中删除。

问题是,当我创建一个任务并且没有关闭应用程序时,我尝试删除我刚刚创建的任务。它不允许我这样做。当我创建一个新任务并在关闭应用程序之前立即将其删除时,它会再次出现。但是,如果我随后关闭应用程序并再次加载它,则可以正常删除该条目,但如果我在创建该条目的同一会话中,则无法删除它。

我使用了一些Log.d 参数来查看它是如何变化的。我认为由于各种原因,问题是OnDataChange()。但到目前为止,我还没有找到问题的根源。这是TaskActivity类,之后我将粘贴TasksCreation(我认为不需要粘贴Adapter)。

public class TasksActivity extends AppCompatActivity  {

    DatabaseReference reference;
    RecyclerView myTasks;
    ArrayList<TaskItems> myTasksList;
    TasksAdapter tasksAdapter;


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tasks);
        myTasks = findViewById(R.id.my_tasks);   // RecyclerView that I defined as part of the layout. This is the id of it

        myTasks.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false));
        myTasksList = new ArrayList<>();
        Button openCreateTask = findViewById(R.id.openCreateTask);
        tasksAdapter = new TasksAdapter(this,myTasksList); 
        myTasks.setAdapter(tasksAdapter);
        new ItemTouchHelper(itemTouchHelper).attachToRecyclerView(myTasks);


        openCreateTask.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent_task = new Intent(getApplicationContext(), TasksCreation.class);
                startActivity(intent_task);
            }
        });


        reference = FirebaseDatabase.getInstance().getReference().child("MotApp"); // Name of the App in the database .child("MotApp")

        reference.addValueEventListener(new ValueEventListener() {


            @Override
            public void onDataChange(@NonNull DataSnapshot dataSnapshot) {   // It gets the info from the database
                Log.d("data Changed called", "onDataChange: is called");
                Log.d("whatever", "onDataChange BEGIN Array of myTasksList size is "+myTasksList.size());
                myTasksList.clear();                                            // Added later to avoid duplication




                for(DataSnapshot elements: dataSnapshot.getChildren()){


                    TaskItems p = elements.getValue(TaskItems.class);
                    myTasksList.add(p);

                }


                tasksAdapter.notifyDataSetChanged(); // If this is put outside of onDataChange, it displays a blank list.
                Log.d("whatever", "onDataChange END Array of myTasksList size is "+myTasksList.size());
            }

            @Override
            public void onCancelled(@NonNull DatabaseError databaseError) {
                Toast.makeText(getApplicationContext(), "No data", Toast.LENGTH_SHORT).show();
            }


        });

    }


    ItemTouchHelper.SimpleCallback itemTouchHelper = new ItemTouchHelper.SimpleCallback(0,ItemTouchHelper.RIGHT) {
        @Override
        public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
            return false;
        }

        @Override
        public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
            int position = viewHolder.getAdapterPosition();
            Log.d("ARRAY SIZE", "onSwiped BEGIN Array of myTasksList size is "+myTasksList.size());
            String key =    myTasksList.get(position).getKey();
            reference= FirebaseDatabase.getInstance().getReference().child("MotApp").child(key);


        Toast.makeText(getApplicationContext(),"This is key "+key,Toast.LENGTH_LONG).show();
         reference.removeValue();
        myTasksList.remove(position);
        tasksAdapter.notifyItemRemoved(position);

            Log.d("ARRAY SIZE", "onSwiped END Array of myTasksList size is "+myTasksList.size());

        }
    };

}

这是为TasksCreation活动:

public class TasksCreation extends AppCompatActivity {

    DatabaseReference referenceCreation;
    ArrayList<String> list;
    EditText taskName;
    EditText taskDescr;
    Button selectDates;
    TextView taskDate;
    Button createTask;
    Button cancel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tasks_creation);

        taskName = findViewById(R.id.et_TaskName);
        taskDescr = findViewById(R.id.et_TaskDescr);
        selectDates = findViewById(R.id.selectDates);
        taskDate= findViewById(R.id.tv_Dates);
        createTask = findViewById(R.id.createTask);
        cancel = findViewById(R.id.cancelButton);


        createTask.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                if (!TextUtils.isEmpty(taskName.getText()) && !TextUtils.isEmpty(taskDate.getText())) {

                    referenceCreation = FirebaseDatabase.getInstance().getReference().child("MotApp").push(); //saves it with custom key created by Firebase
                    final String key = referenceCreation.getKey();

                    referenceCreation.addValueEventListener(new ValueEventListener() {
                        @Override
                        public void onDataChange(@NonNull DataSnapshot dataSnapshot) {       
                            dataSnapshot.getRef().child("key").setValue(key);

                            dataSnapshot.getRef().child("taskTitle").setValue(taskName.getText().toString());
                            dataSnapshot.getRef().child("taskDescription").setValue(taskDescr.getText().toString());
                            dataSnapshot.getRef().child("taskDate").setValue(taskDate.getText().toString());

                        }


                        @Override
                        public void onCancelled(@NonNull DatabaseError databaseError) {

                        }
                    });


                    Intent intent = new Intent(getApplicationContext(), TasksActivity.class);

                    startActivity(intent);

                } else{
                    Toast.makeText(getApplicationContext(), "You haven't filled all the fields", Toast.LENGTH_SHORT).show();
                }
            }
        });

        cancel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(getApplicationContext(), TasksActivity.class);

                startActivity(intent);
            }
        });

        selectDates.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                createAlertDialogue();
            }
        });

    }

    private void createAlertDialogue(){
        list = new ArrayList<String>();

        AlertDialog.Builder builder = new AlertDialog.Builder(this,R.style.MyDialogTheme);


        builder.setTitle("Select days");
        builder.setMultiChoiceItems(R.array.Days, null, new DialogInterface.OnMultiChoiceClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which, boolean isChecked) {

                String arr[] = getResources().getStringArray(R.array.Days);

                if(isChecked){
                    list.add(arr[which]);
                }else if(list.contains(arr[which])){
                    list.remove(arr[which]);

                }

            }
        });

         builder.setPositiveButton("Save", new DialogInterface.OnClickListener() {

            String data = "";
            @Override
            public void onClick(DialogInterface dialog, int which) {
            for(String elements: list){
                elements = elements.substring(0,3);
                data= elements+" "+data;

            }
                taskDate.setText(data);

            }
        });


        builder.create();

        builder.show();

    }

}

我真的很感激这个问题。

非常感谢

P:S:这是 TaskAdapter 的代码(忽略 dayoftheweek 部分只是我评论的部分代码)

public class TasksAdapter extends RecyclerView.Adapter<TasksAdapter.MyViewHolder> { // V 1.3 added OnClickListener


    Context context;
    ArrayList<TaskItems> tasks;
    DatabaseReference reference;
    SimpleDateFormat sdf = new SimpleDateFormat("EEEE");
    Date d = new Date();
    final String dayOfTheWeek = sdf.format(d).substring(0,3);


    public TasksAdapter(Context context, ArrayList<TaskItems> tasks) {
        this.context = context;
        this.tasks = tasks;
    }


    public class MyViewHolder extends RecyclerView.ViewHolder {  // V1.3 Added implements View.OnClickListener

        TextView taskTitle;
        TextView taskDate;
        TextView taskDescription;
        CheckBox taskCheckBox;
        ConstraintLayout constraintLayout;  // Added to change background  of each RecyclerView item.



        public MyViewHolder(@NonNull View itemView) {
            super(itemView);

            taskTitle = itemView.findViewById(R.id.taskTitle);
            taskDate = itemView.findViewById(R.id.taskDate);
            taskDescription = itemView.findViewById(R.id.taskDescription);
            taskCheckBox = itemView.findViewById(R.id.taskCheckBox);
            constraintLayout = (ConstraintLayout) itemView.findViewById(R.id.item_task_layout);


            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int pos = getAdapterPosition();

                    //            if(pos != RecyclerView.NO_POSITION){//Checks if item still exists
                    TaskItems clickedDataItem = tasks.get(pos);
                    Toast.makeText(v.getContext(), "You clicked " + clickedDataItem.getTaskTitle(), Toast.LENGTH_SHORT).show();
                    //          }
                }
            });

        }

    }

    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {                  // standard code for onCreateViewHolder

        return new MyViewHolder(LayoutInflater.from(context).inflate(R.layout.item_task,parent,false));
    }



    @Override
    public void onBindViewHolder(final @NonNull MyViewHolder holder, final int position) {  // This method is called once for each item on the list.

            holder.taskTitle.setText(tasks.get(position).getTaskTitle());
            holder.taskDescription.setText(tasks.get(position).getTaskDescription());
            holder.taskDate.setText(tasks.get(position).getTaskDate());
            holder.taskCheckBox.setChecked(tasks.get(position).isChecked());
            holder.taskCheckBox.setTag(tasks.get(position).getKey());


            holder.taskCheckBox.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {

                    FirebaseDatabase database = FirebaseDatabase.getInstance();


                    String post = (String) holder.taskCheckBox.getTag();
                    Toast.makeText(context,"This is checkbox number: "+post,Toast.LENGTH_SHORT).show();  // Working Well.        //change for new logic

                    reference =  database.getReference("MotApp").child(post);

                    boolean checkboxStatus =  holder.taskCheckBox.isChecked();
                    Log.d("Checked", "onClick: The taskcheckbox checked is "+holder.taskCheckBox.isChecked());

                    TaskItems value = new TaskItems(tasks.get(position).getKey(),tasks.get(position).getTaskTitle(), tasks.get(position).getTaskDate(),tasks.get(position).getTaskDescription(),checkboxStatus);
                    reference.setValue(value);
                   Toast.makeText(context,"This is a checkbox belonging to item "+tasks.get(position).getTaskTitle(),Toast.LENGTH_LONG).show();

                }
            });

    }

    @Override
    public int getItemCount() { //tells the adapter the size . if it's zero then it won't create anything
        return tasks.size();
    }

}

【问题讨论】:

  • 通常当我看到支持适配器的数据作为 Activity 中的引用时,这就是错误的来源。摆脱对 ArrayList myTasksList 的引用,直接使用 Adapter 进行添加和删除,这样可以更清晰地了解如何通过更紧密地封装来修改数据。
  • 您能详细介绍一下吗?也许举个例子。这是我正在做的第一个大应用程序,所以我没有太多经验。到目前为止,在我看过的所有教程中,他们都使用 ArrayList 作为存储值的一种方式。谢谢。
  • 如果尚未解决,您能否发布 TasksAdapter 的代码
  • 完成。我编辑了帖子,它在消息的末尾。

标签: java android firebase firebase-realtime-database android-recyclerview


【解决方案1】:

我终于找到了解决方法。

首先,我在 TaskActivity 和 TaskCreation 中使用 addListenerforSingleValueEvent 更改了所有 addValueEventListener。使用 addValueEventListener 我意识到每次创建一个新任务时都会产生几个循环,这会导致各种问题。

我还更改了在 TaskCreation 中设置 startIntent 的方式。我最初是在 Listener 块之后设置的。但是 onDataChange 的异步特性使得每次我加载意图时,更新的信息都没有设置为刚刚添加的新任务。

把它放在 onDataChange 里面就解决了。我已经进行了几次测试,现在它运行良好。

我浪费了很多天试图解决这个问题。但在这个过程中我学到了很多教训。所以我想他们并没有浪费时间:)。

感谢所有帮助我解决这个问题的人。

【讨论】:

  • 很高兴你找到它?.. 这些天将使你成为更强大的开发人员,并在未来为你节省大量时间
【解决方案2】:

当你从TasksCreation返回时,你需要重建TasksActivity中的RecyclerView数据;这是因为TasksActivityonCreate() 回调仅在您打开应用程序时被调用;这就是为什么删除仅在您关闭应用并重新打开时才有效的原因。

当你从TasksCreate返回时onCreate()不会被调用,因为当你从TasksActivity转移到TasksCreation时,TasksActivity并没有被破坏,而是刚刚停止,因此当你来的时候回到TasksActivity 它将开始并恢复;因此,将onCreate() 上的代码转移到onResume(),以便根据最近的更改更新列表。

所以把你的TasksActivity改成下面的

public class TasksActivity extends AppCompatActivity  {

    DatabaseReference reference;
    RecyclerView myTasks;
    ArrayList<TaskItems> myTasksList;
    TasksAdapter tasksAdapter;


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tasks);

    }

    @Override
    protected void onResume() {
        super.onResume();

        myTasks = findViewById(R.id.my_tasks);   // RecyclerView that I defined as part of the layout. This is the id of it

        myTasks.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false));
        myTasksList = new ArrayList<>();
        Button openCreateTask = findViewById(R.id.openCreateTask);
        tasksAdapter = new TasksAdapter(this,myTasksList); 
        myTasks.setAdapter(tasksAdapter);
        new ItemTouchHelper(itemTouchHelper).attachToRecyclerView(myTasks);


        openCreateTask.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent_task = new Intent(getApplicationContext(), TasksCreation.class);
                startActivity(intent_task);
            }
        });


        reference = FirebaseDatabase.getInstance().getReference().child("MotApp"); // Name of the App in the database .child("MotApp")

        reference.addValueEventListener(new ValueEventListener() {


            @Override
            public void onDataChange(@NonNull DataSnapshot dataSnapshot) {   // It gets the info from the database
                Log.d("data Changed called", "onDataChange: is called");
                Log.d("whatever", "onDataChange BEGIN Array of myTasksList size is "+myTasksList.size());
                myTasksList.clear();                                            // Added later to avoid duplication

                for(DataSnapshot elements: dataSnapshot.getChildren()){


                    TaskItems p = elements.getValue(TaskItems.class);
                    myTasksList.add(p);

                }

                tasksAdapter.notifyDataSetChanged(); // If this is put outside of onDataChange, it displays a blank list.
                Log.d("whatever", "onDataChange END Array of myTasksList size is "+myTasksList.size());
            }

            @Override
            public void onCancelled(@NonNull DatabaseError databaseError) {
                Toast.makeText(getApplicationContext(), "No data", Toast.LENGTH_SHORT).show();
            }

        });

    }


    ItemTouchHelper.SimpleCallback itemTouchHelper = new ItemTouchHelper.SimpleCallback(0,ItemTouchHelper.RIGHT) {
        @Override
        public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
            return false;
        }

        @Override
        public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
            int position = viewHolder.getAdapterPosition();
            Log.d("ARRAY SIZE", "onSwiped BEGIN Array of myTasksList size is "+myTasksList.size());
            String key =    myTasksList.get(position).getKey();
            reference= FirebaseDatabase.getInstance().getReference().child("MotApp").child(key);


        Toast.makeText(getApplicationContext(),"This is key "+key,Toast.LENGTH_LONG).show();
         reference.removeValue();
        myTasksList.remove(position);
        tasksAdapter.notifyItemRemoved(position);

            Log.d("ARRAY SIZE", "onSwiped END Array of myTasksList size is "+myTasksList.size());

        }

    };


}

【讨论】:

  • 感谢 Zain 的帮助,但不幸的是,这并没有解决问题。我刚刚测试了您的建议,问题仍然存在。我怀疑问题出在“onDataChange”上。但你的想法至少给了我一些澄清。我也想过像你提议的那样做一些事情,但我缺乏知道如何去做的技术专长。我们至少现在知道不是那样。
  • @SoulAndBone 感谢您的评论,如果我发现更多内容,我会尝试检查代码
  • @SoulAndBone,你在onDataChanged中有一个日志Log.d("whatever", "onDataChange BEGIN Array of myTasksList size is "+myTasksList.size());,当你创建一个新任务并返回TasksActivity;此日志是否加一?
  • 你好@Zain 再次感谢。这是日志。从 Swipe 开始,在我有一个初始任务(行为正常)的情况下,在不退出会话的情况下创建了第二个任务。 imagehost.com.au//d7amw0Kd9r/Log_5e9c0adbdfec4.png。如您所见, onDataChange 被多次调用。即使我正在执行的唯一操作是滑动以删除刚刚创建的任务。
  • @SoulAndBone 再次感谢您提供新信息。我会仔细检查
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-06-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-17
  • 1970-01-01
  • 2021-11-14
相关资源
最近更新 更多