从嵌套的 RecyclerView 中删除项目
Removing an item from a nested RecyclerView
我已经为这个问题纠结好几个小时了。我有一个嵌套的 RecyclerView(即包含内部 Recycler 视图的 RecyclerView)。父回收者视图和子回收者视图都是动态的。我遇到的问题是,当发生 CRUD,尤其是删除时,我找不到正确通知子(内部)回收器视图的方法。起初它工作正常,但后来我从 "You must be a direct descend view" 或 getAdapterPosition 返回 -1 或只是不正确的位置得到各种随机错误。我认为我的实现非常标准,所以我问通知内部回收器视图的正确方法是什么。
我非常接近回到我以前的实现,它涉及一个片段数组,每个片段都包含一个回收视图,但我质疑这种设计的性能。我的代码如下:
父 RecyclerView
public class RecipeRecyclerAdapter extends RecyclerView.Adapter<RecipeRecyclerAdapter.ViewHolder>
{
public interface OnRecipeRecyclerListener
{
//--------------------------- Proxy methods for OnDishRecyclerListener -----------------
void renameDish(int DishPosition, int RecipePosition);
void deleteDish(int DishPosition, int RecipePosition);
//--------------------------- OnRecipeRecyclerListener methods ----------------------------
void deleteRecipe(int RecipePosition);
void renameRecipe(int RecipePosition);
}
//Recycler Pool and tools
private RecyclerView.RecycledViewPool viewPool = new RecyclerView.RecycledViewPool();
//Recycler Parameters
private ArrayList<Recipe> allRecipes;
private Context context;
//Listener
@Setter
private OnRecipeRecyclerListener onRecipeRecyclerListener;
public RecipeRecyclerAdapter(Context context, ArrayList<Recipe> allRecipes)
{
this.allRecipes = allRecipes;
this.context = context;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType)
{
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_Recipe, parent, false);
return new RecipeRecyclerAdapter.ViewHolder(view, onRecipeRecyclerListener, context);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position)
{
Recipe Recipe = allRecipes.get(position);
holder.RecipeName.setText(Utils.colourFirstLetter(context, Recipe.getRecipeName(), R.color.progressFxBar));
holder.RecipeDate.setText(Utils.getDate(Recipe.getTimestamp()));
// Create layout manager with initial prefetch item count
LinearLayoutManager layoutManager = new LinearLayoutManager(
holder.DishsRecycler.getContext(),
LinearLayoutManager.VERTICAL,
false
);
layoutManager.setInitialPrefetchItemCount(Recipe.getDishs().size());
DishRecyclerAdapter DishsRecyclerAdapter = new DishRecyclerAdapter(Recipe.getDishs(), holder, context);
holder.DishsRecycler.setLayoutManager(layoutManager);
holder.DishsRecycler.setAdapter(DishsRecyclerAdapter);
holder.DishsRecycler.setRecycledViewPool(viewPool);
}
@Override
public int getItemCount()
{
return allRecipes.size();
}
static class ViewHolder extends RecyclerView.ViewHolder implements DishRecyclerAdapter.OnDishRecyclerListener
private OnRecipeRecyclerListener onRecipeRecyclerListener;
private Context context;
TextView RecipeName, RecipeDate;
ImageView addDish;
//The Dishs Recycler
RecyclerView DishsRecycler;
public ViewHolder(@NonNull View itemView, OnRecipeRecyclerListener onRecipeRecyclerListener, Context context)
{
super(itemView);
this.onRecipeRecyclerListener = onRecipeRecyclerListener;
this.context = context;
RecipeName = itemView.findViewById(R.id.RecipeName);
RecipeDate = itemView.findViewById(R.id.RecipeDate);
addDish = itemView.findViewById(R.id.addDish);
DishsRecycler = itemView.findViewById(R.id.DishsRecyclerView);
loadListeners(itemView);
}
private void loadListeners(@NonNull View initView)
{
RecipeName.setOnClickListener(v ->
{
PopupMenu popup = new PopupMenu(context, v);
MenuInflater inflater = popup.getMenuInflater();
inflater.inflate(R.menu.Recipe_floating_menu, popup.getMenu());
popup.show();
popup.setOnMenuItemClickListener(item ->
{
switch (item.getItemId())
{
case R.id.menuDeleteRecipe:
onRecipeRecyclerListener.deleteRecipe(getAdapterPosition());
return true;
case R.id.menuRenameRecipe:
onRecipeRecyclerListener.renameRecipe(getAdapterPosition());
return true;
case R.id.menuRecipeProps:
onRecipeRecyclerListener.RecipeProps(getAdapterPosition());
return true;
default:
return false;
}
});
});
addDish.setOnClickListener(v ->
{
onRecipeRecyclerListener.addDish(getAdapterPosition());
});
}
//******************************* OnDishRecyclerListener *******************************
@Override
public void renameDish(int position)
{
onRecipeRecyclerListener.renameDish(position, getAdapterPosition());
}
@Override
public void deleteDish(int position)
{
onRecipeRecyclerListener.deleteDish(position, getAdapterPosition());
}
}
}
子(内部)RecyclerView
public class DishRecyclerAdapter extends RecyclerView.Adapter<DishRecyclerAdapter.ViewHolder>
{
public interface OnDishRecyclerListener
{
void renameDish(int position);
void deleteDish(int position);
}
private OnDishRecyclerListener onDishRecyclerListener;
private ArrayList<Dish> allDishs;
private Context context;
public DishRecyclerAdapter(ArrayList<Dish> allDishs, OnDishRecyclerListener onDishRecyclerListener, Context context)
{
this.onDishRecyclerListener = onDishRecyclerListener;
this.allDishs = allDishs;
this.context = context;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType)
{
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_Dishs, parent, false);
return new ViewHolder(context, view, onDishRecyclerListener);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position)
{
Dish Dish = allDishs.get(position);
holder.DishName.setText(Dish.getDishName());
}
@Override
public int getItemCount()
{
return allDishs.size();
}
public class ViewHolder extends RecyclerView.ViewHolder
{
private Context context;
TextView DishName; //plus a bunch of other Views I just removed for the sake of simplicity
OnDishRecyclerListener onDishRecyclerListener;
public ViewHolder(Context context, @NonNull View itemView, OnDishRecyclerListener onDishRecyclerListener)
{
super(itemView);
this.context = context;
DishName = itemView.findViewById(R.id.DishName);
this.onDishRecyclerListener = onDishRecyclerListener;
loadListeners(itemView);
}
private void loadListeners(@NonNull View v)
{
//Rename an Dish
DishName.setOnClickListener(view ->
{
PopupMenu popup = new PopupMenu(context, v);
MenuInflater inflater = popup.getMenuInflater();
inflater.inflate(R.menu.Dish_floating_menu, popup.getMenu());
popup.show();
popup.setOnMenuItemClickListener(item ->
{
switch (item.getItemId())
{
case R.id.menuDeleteDish:
onDishRecyclerListener.deleteDish(getAdapterPosition());
return true;
case R.id.menuRenameDish:
onDishRecyclerListener.renameDish(getAdapterPosition());
return true;
case R.id.menuDishProps:
return true;
default:
return false;
}
});
});
}
}
}
提取调用父回收器视图的片段:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.fragment_Recipe_panel, container, false);
recyclerRecipe = view.findViewById(R.id.RecipeRecyclerView);
SimpleItemAnimator simpleItemAnimator = (SimpleItemAnimator) recyclerRecipe.getItemAnimator();
if(simpleItemAnimator !=null)
{
simpleItemAnimator.setSupportsChangeAnimations(true);
}
RecipeAdapter = new RecipeRecyclerAdapter(getContext(), allRecipes);
RecipeAdapter.setOnRecipeRecyclerListener(this);
//recyclerRecipe.setHasFixedSize(true);
recyclerRecipe.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerRecipe.setAdapter(RecipeAdapter);
return view;
}
public void createRecipe(String RecipeName)
{
Recipe Recipe = new Recipe(RecipeName, getContext());
allRecipes.add(0,Recipe);
RecipeAdapter.notifyItemInserted(0);
}
@Override
public void deleteRecipe(int RecipePosition)
{
allRecipes.remove(RecipePosition);
RecipeAdapter.notifyItemRemoved(RecipePosition);
}
@Override
public void addDish(int RecipePosition)
{
allRecipes.get(RecipePosition).getDishs().add(new Dish(DishName));
RecipeAdapter.notifyItemChanged(RecipePosition);
}
@Override
public void deleteDish(int DishPosition, int RecipePosition)
{
Recipe Recipe = allRecipes.get(RecipePosition);
Dish Dish = Recipe.getDishs().get(DishPosition);
Dish.getTimer().destroyTimer();
Recipe.getDishs().remove(DishPosition);
RecipeAdapter.notifyItemChanged(RecipePosition);
}
我弄清楚了问题所在(经过数小时)。我需要先通知父回收商,然后再通知子回收商。
//adding an item to the inner list
recipeAdapter.notifyItemChanged(recipePosition);
dishsRecycler.getAdapter().notifyItemInserted(recipe.getDishs().size()-1);
//deleting an inner list item
recipeAdapter.notifyItemChanged(recipePosition);
dishsRecycler.getAdapter().notifyItemRemoved()
然而,最大的罪魁祸首是所有内部回收器视图都有一个共同的回收器池,因此从代码中删除了这一行
//REMOVED THESE LINES
private RecyclerView.RecycledViewPool viewPool = new RecyclerView.RecycledViewPool();
holder.DishsRecycler.setRecycledViewPool(viewPool);
此外,我没有使用 notifyDataSet(),因为出于某种原因会抛出 NO_POSITION (-1)。
我正在实施一个类似的案例。
我有 2 个 RecyclerView,其中一个是嵌套的。您可以从嵌套或 parent RecyclerView.
中删除项目
我猜你必须在每次更改或删除项目时更新 Recyclers。
为了理解我先看了这篇文章:
https://medium.com/android-news/recyclerview-optimisations-a4b141dd433d
我同意 Ken John 的回答,他说你需要先通知 RecyclerView 更新到 parent,然后再通知到嵌套;否则你会得到一个错误,你的应用程序将会崩溃。
然而,另一个重要的事情是如何进行通知更新。
对于嵌套的 RecyclerView,我使用了
// for items updated
notifyItemChanged(position);
// for items deleted
notifyItemRemoved(position);
但是上面提到的对于 parent RecyclerView 不能很好地工作,我真的不知道为什么,但我解决了如下问题:
// for items updated
notifyItemChanged(position);
// for items deleted
notifyItemRemoved(position); // this line does not work for me
notifyDataSetChanged(); // it works fine
最后一条指令花费了更多时间,但工作正常。
注意:我还不知道为什么 notifyItemRemoved(position)
对 parent 不起作用,我已经打电话给 notifyDataSetChanged()
我已经为这个问题纠结好几个小时了。我有一个嵌套的 RecyclerView(即包含内部 Recycler 视图的 RecyclerView)。父回收者视图和子回收者视图都是动态的。我遇到的问题是,当发生 CRUD,尤其是删除时,我找不到正确通知子(内部)回收器视图的方法。起初它工作正常,但后来我从 "You must be a direct descend view" 或 getAdapterPosition 返回 -1 或只是不正确的位置得到各种随机错误。我认为我的实现非常标准,所以我问通知内部回收器视图的正确方法是什么。
我非常接近回到我以前的实现,它涉及一个片段数组,每个片段都包含一个回收视图,但我质疑这种设计的性能。我的代码如下:
父 RecyclerView
public class RecipeRecyclerAdapter extends RecyclerView.Adapter<RecipeRecyclerAdapter.ViewHolder>
{
public interface OnRecipeRecyclerListener
{
//--------------------------- Proxy methods for OnDishRecyclerListener -----------------
void renameDish(int DishPosition, int RecipePosition);
void deleteDish(int DishPosition, int RecipePosition);
//--------------------------- OnRecipeRecyclerListener methods ----------------------------
void deleteRecipe(int RecipePosition);
void renameRecipe(int RecipePosition);
}
//Recycler Pool and tools
private RecyclerView.RecycledViewPool viewPool = new RecyclerView.RecycledViewPool();
//Recycler Parameters
private ArrayList<Recipe> allRecipes;
private Context context;
//Listener
@Setter
private OnRecipeRecyclerListener onRecipeRecyclerListener;
public RecipeRecyclerAdapter(Context context, ArrayList<Recipe> allRecipes)
{
this.allRecipes = allRecipes;
this.context = context;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType)
{
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_Recipe, parent, false);
return new RecipeRecyclerAdapter.ViewHolder(view, onRecipeRecyclerListener, context);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position)
{
Recipe Recipe = allRecipes.get(position);
holder.RecipeName.setText(Utils.colourFirstLetter(context, Recipe.getRecipeName(), R.color.progressFxBar));
holder.RecipeDate.setText(Utils.getDate(Recipe.getTimestamp()));
// Create layout manager with initial prefetch item count
LinearLayoutManager layoutManager = new LinearLayoutManager(
holder.DishsRecycler.getContext(),
LinearLayoutManager.VERTICAL,
false
);
layoutManager.setInitialPrefetchItemCount(Recipe.getDishs().size());
DishRecyclerAdapter DishsRecyclerAdapter = new DishRecyclerAdapter(Recipe.getDishs(), holder, context);
holder.DishsRecycler.setLayoutManager(layoutManager);
holder.DishsRecycler.setAdapter(DishsRecyclerAdapter);
holder.DishsRecycler.setRecycledViewPool(viewPool);
}
@Override
public int getItemCount()
{
return allRecipes.size();
}
static class ViewHolder extends RecyclerView.ViewHolder implements DishRecyclerAdapter.OnDishRecyclerListener
private OnRecipeRecyclerListener onRecipeRecyclerListener;
private Context context;
TextView RecipeName, RecipeDate;
ImageView addDish;
//The Dishs Recycler
RecyclerView DishsRecycler;
public ViewHolder(@NonNull View itemView, OnRecipeRecyclerListener onRecipeRecyclerListener, Context context)
{
super(itemView);
this.onRecipeRecyclerListener = onRecipeRecyclerListener;
this.context = context;
RecipeName = itemView.findViewById(R.id.RecipeName);
RecipeDate = itemView.findViewById(R.id.RecipeDate);
addDish = itemView.findViewById(R.id.addDish);
DishsRecycler = itemView.findViewById(R.id.DishsRecyclerView);
loadListeners(itemView);
}
private void loadListeners(@NonNull View initView)
{
RecipeName.setOnClickListener(v ->
{
PopupMenu popup = new PopupMenu(context, v);
MenuInflater inflater = popup.getMenuInflater();
inflater.inflate(R.menu.Recipe_floating_menu, popup.getMenu());
popup.show();
popup.setOnMenuItemClickListener(item ->
{
switch (item.getItemId())
{
case R.id.menuDeleteRecipe:
onRecipeRecyclerListener.deleteRecipe(getAdapterPosition());
return true;
case R.id.menuRenameRecipe:
onRecipeRecyclerListener.renameRecipe(getAdapterPosition());
return true;
case R.id.menuRecipeProps:
onRecipeRecyclerListener.RecipeProps(getAdapterPosition());
return true;
default:
return false;
}
});
});
addDish.setOnClickListener(v ->
{
onRecipeRecyclerListener.addDish(getAdapterPosition());
});
}
//******************************* OnDishRecyclerListener *******************************
@Override
public void renameDish(int position)
{
onRecipeRecyclerListener.renameDish(position, getAdapterPosition());
}
@Override
public void deleteDish(int position)
{
onRecipeRecyclerListener.deleteDish(position, getAdapterPosition());
}
}
}
子(内部)RecyclerView
public class DishRecyclerAdapter extends RecyclerView.Adapter<DishRecyclerAdapter.ViewHolder>
{
public interface OnDishRecyclerListener
{
void renameDish(int position);
void deleteDish(int position);
}
private OnDishRecyclerListener onDishRecyclerListener;
private ArrayList<Dish> allDishs;
private Context context;
public DishRecyclerAdapter(ArrayList<Dish> allDishs, OnDishRecyclerListener onDishRecyclerListener, Context context)
{
this.onDishRecyclerListener = onDishRecyclerListener;
this.allDishs = allDishs;
this.context = context;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType)
{
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_Dishs, parent, false);
return new ViewHolder(context, view, onDishRecyclerListener);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position)
{
Dish Dish = allDishs.get(position);
holder.DishName.setText(Dish.getDishName());
}
@Override
public int getItemCount()
{
return allDishs.size();
}
public class ViewHolder extends RecyclerView.ViewHolder
{
private Context context;
TextView DishName; //plus a bunch of other Views I just removed for the sake of simplicity
OnDishRecyclerListener onDishRecyclerListener;
public ViewHolder(Context context, @NonNull View itemView, OnDishRecyclerListener onDishRecyclerListener)
{
super(itemView);
this.context = context;
DishName = itemView.findViewById(R.id.DishName);
this.onDishRecyclerListener = onDishRecyclerListener;
loadListeners(itemView);
}
private void loadListeners(@NonNull View v)
{
//Rename an Dish
DishName.setOnClickListener(view ->
{
PopupMenu popup = new PopupMenu(context, v);
MenuInflater inflater = popup.getMenuInflater();
inflater.inflate(R.menu.Dish_floating_menu, popup.getMenu());
popup.show();
popup.setOnMenuItemClickListener(item ->
{
switch (item.getItemId())
{
case R.id.menuDeleteDish:
onDishRecyclerListener.deleteDish(getAdapterPosition());
return true;
case R.id.menuRenameDish:
onDishRecyclerListener.renameDish(getAdapterPosition());
return true;
case R.id.menuDishProps:
return true;
default:
return false;
}
});
});
}
}
}
提取调用父回收器视图的片段:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.fragment_Recipe_panel, container, false);
recyclerRecipe = view.findViewById(R.id.RecipeRecyclerView);
SimpleItemAnimator simpleItemAnimator = (SimpleItemAnimator) recyclerRecipe.getItemAnimator();
if(simpleItemAnimator !=null)
{
simpleItemAnimator.setSupportsChangeAnimations(true);
}
RecipeAdapter = new RecipeRecyclerAdapter(getContext(), allRecipes);
RecipeAdapter.setOnRecipeRecyclerListener(this);
//recyclerRecipe.setHasFixedSize(true);
recyclerRecipe.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerRecipe.setAdapter(RecipeAdapter);
return view;
}
public void createRecipe(String RecipeName)
{
Recipe Recipe = new Recipe(RecipeName, getContext());
allRecipes.add(0,Recipe);
RecipeAdapter.notifyItemInserted(0);
}
@Override
public void deleteRecipe(int RecipePosition)
{
allRecipes.remove(RecipePosition);
RecipeAdapter.notifyItemRemoved(RecipePosition);
}
@Override
public void addDish(int RecipePosition)
{
allRecipes.get(RecipePosition).getDishs().add(new Dish(DishName));
RecipeAdapter.notifyItemChanged(RecipePosition);
}
@Override
public void deleteDish(int DishPosition, int RecipePosition)
{
Recipe Recipe = allRecipes.get(RecipePosition);
Dish Dish = Recipe.getDishs().get(DishPosition);
Dish.getTimer().destroyTimer();
Recipe.getDishs().remove(DishPosition);
RecipeAdapter.notifyItemChanged(RecipePosition);
}
我弄清楚了问题所在(经过数小时)。我需要先通知父回收商,然后再通知子回收商。
//adding an item to the inner list
recipeAdapter.notifyItemChanged(recipePosition);
dishsRecycler.getAdapter().notifyItemInserted(recipe.getDishs().size()-1);
//deleting an inner list item
recipeAdapter.notifyItemChanged(recipePosition);
dishsRecycler.getAdapter().notifyItemRemoved()
然而,最大的罪魁祸首是所有内部回收器视图都有一个共同的回收器池,因此从代码中删除了这一行
//REMOVED THESE LINES
private RecyclerView.RecycledViewPool viewPool = new RecyclerView.RecycledViewPool();
holder.DishsRecycler.setRecycledViewPool(viewPool);
此外,我没有使用 notifyDataSet(),因为出于某种原因会抛出 NO_POSITION (-1)。
我正在实施一个类似的案例。
我有 2 个 RecyclerView,其中一个是嵌套的。您可以从嵌套或 parent RecyclerView.
中删除项目我猜你必须在每次更改或删除项目时更新 Recyclers。
为了理解我先看了这篇文章:
https://medium.com/android-news/recyclerview-optimisations-a4b141dd433d
我同意 Ken John 的回答,他说你需要先通知 RecyclerView 更新到 parent,然后再通知到嵌套;否则你会得到一个错误,你的应用程序将会崩溃。
然而,另一个重要的事情是如何进行通知更新。
对于嵌套的 RecyclerView,我使用了
// for items updated
notifyItemChanged(position);
// for items deleted
notifyItemRemoved(position);
但是上面提到的对于 parent RecyclerView 不能很好地工作,我真的不知道为什么,但我解决了如下问题:
// for items updated
notifyItemChanged(position);
// for items deleted
notifyItemRemoved(position); // this line does not work for me
notifyDataSetChanged(); // it works fine
最后一条指令花费了更多时间,但工作正常。
注意:我还不知道为什么 notifyItemRemoved(position)
对 parent 不起作用,我已经打电话给 notifyDataSetChanged()