为什么在 Recycler View 中滚动后值会消失?
Why do values disappear after scrolling in Recycler View?
滚动前的数据
滚动后的数据
我的应用程序的问题如上图所示。
输入数据后,如果我在将项目添加为可滚动后滚动,数据会消失。
进一步说明,有时输入的数据会出现在已添加的其他项目中。
解释一下这个应用,它是一个运动记录应用,它使用multi-type recycler view
。
我用了ListAdapter
,也用了DiffUtil
。和图片相关的是Detail item
.
TextWatcher
用于保存输入的数据。
我一直在寻找解决这个问题的方法。
搜索次数最多的两个解决方案是 here
使用 getItemViewType()
、getItemId()
->我使用了这个link中的方法,但是没有解决问题。
Using setIsRecyclable(false)
inside holder
-> 这个方法奏效了。但是我听说setIsRecyclable(false)
是一个没有recycle
的函数。
如果我使用它,这不是一个好方法吗,因为使用 RecyclerView
没有任何优势?
RoutineAdapter.java
public class RoutineListAdapter extends ListAdapter<Object, RecyclerView.ViewHolder> {
Context context;
RoutineListAdapter.OnRoutineItemClickListener routinelistener;
RoutineListAdapter.OnRoutineAddClickListener routineAddListener;
final static int TYPE_ROUTINE = 1;
final static int TYPE_ROUTINE_DETAIL = 2;
final static int TYPE_ROUTINE_FOOTER = 3;
public RoutineListAdapter(@NonNull DiffUtil.ItemCallback<Object> diffCallback) {
super(diffCallback);
}
// add routine interface
public interface OnRoutineAddClickListener {
public void onAddRoutineClick();
}
public void setOnAddRoutineClickListener(RoutineListAdapter.OnRoutineAddClickListener listener) {
this.routineAddListener = listener;
}
// add/remove detail interface
public interface OnRoutineItemClickListener {
public void onAddBtnClicked(int curRoutinePos);
public void onDeleteBtnClicked(int curRoutinePos);
public void onWritingCommentBtnClicked(int curRoutinePos);
}
public void setOnRoutineClickListener(RoutineListAdapter.OnRoutineItemClickListener listener) {
this.routinelistener = listener;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
context = parent.getContext();
View itemView;
if(viewType == TYPE_ROUTINE){
itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.routine_item, parent, false);
return new RoutineListAdapter.RoutineViewHolder(itemView);
}
else if(viewType == TYPE_ROUTINE_DETAIL){
itemView = LayoutInflater.from(context).inflate(R.layout.routine_detail_item, parent, false);
return new RoutineListAdapter.RoutineDetailViewHolder(itemView);
}
else {
itemView = LayoutInflater.from(context).inflate(R.layout.add_routine_item, parent, false);
return new RoutineListAdapter.RoutineAddFooterViewHolder(itemView);
}
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
Object curItem;
switch (getItemViewType(position)) {
case TYPE_ROUTINE:
curItem = getItem(position);
setRoutineData((RoutineListAdapter.RoutineViewHolder) holder, (RoutineModel) curItem);
break;
case TYPE_ROUTINE_DETAIL:
curItem = getItem(position);
RoutineDetailModel item = (RoutineDetailModel) curItem;
((RoutineListAdapter.RoutineDetailViewHolder) holder).bind(item);
((RoutineDetailViewHolder) holder).weight.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
item.setWeight(((RoutineDetailViewHolder) holder).weight.getText().toString());
}
});
break;
case TYPE_ROUTINE_FOOTER:
break;
}
}
private void setRoutineData(RoutineListAdapter.RoutineViewHolder holder, RoutineModel routineItem){
holder.routine.setText(routineItem.getRoutine());
}
public Object getRoutineItem(int position) {
if(getCurrentList() == null || position < 0 || position >= getCurrentList().size())
return null;
return getItem(position);
}
@Override
public int getItemCount() {
return getCurrentList().size() + 1;
}
@Override
public int getItemViewType(int position) {
if(position == getCurrentList().size()) {
return TYPE_ROUTINE_FOOTER;
}
else {
Object obj = getItem(position);
if(obj instanceof RoutineModel) {
return TYPE_ROUTINE;
}
else {
// obj instanceof RoutineDetailModel
return TYPE_ROUTINE_DETAIL;
}
}
}
private class RoutineViewHolder extends RecyclerView.ViewHolder {
public TextView routine;
public Button addSet;
public Button deleteSet;
public Button comment;
public RoutineViewHolder(@NonNull View itemView) {
super(itemView);
routine = itemView.findViewById(R.id.routine);
addSet = itemView.findViewById(R.id.add_set);
deleteSet = itemView.findViewById(R.id.delete_set);
comment = itemView.findViewById(R.id.write_comment);
addSet.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(routinelistener != null && getAdapterPosition() != RecyclerView.NO_POSITION)
routinelistener.onAddBtnClicked(getAdapterPosition());
}
});
deleteSet.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(routinelistener != null && getAdapterPosition() != RecyclerView.NO_POSITION)
routinelistener.onDeleteBtnClicked(getAdapterPosition());
}
});
comment.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(routinelistener != null && getAdapterPosition() != RecyclerView.NO_POSITION)
routinelistener.onWritingCommentBtnClicked(getAdapterPosition());
}
});
}
}
private class RoutineDetailViewHolder extends RecyclerView.ViewHolder {
private TextView set;
private EditText weight;
public RoutineDetailViewHolder(@NonNull View itemView) {
super(itemView);
set = itemView.findViewById(R.id.set);
weight = itemView.findViewById(R.id.weight);
}
private void bind(RoutineDetailModel item) {
set.setText(item.getSet().toString() + "set");
weight.setText(item.getWeight());
}
}
private class RoutineAddFooterViewHolder extends RecyclerView.ViewHolder {
TextView textView;
public RoutineAddFooterViewHolder(@NonNull View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.add_text);
ConstraintLayout regionForClick = itemView.findViewById(R.id.clickable_layout);
regionForClick.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (routineAddListener != null) {
routineAddListener.onAddRoutineClick();
}
}
});
}
}
}
已更新
适配器
public class RoutineListAdapter extends ListAdapter<Object, RecyclerView.ViewHolder> {
// detail add / remove iterface
public interface OnRoutineItemClickListener {
public void onAddBtnClicked(int curRoutinePos);
public void onDeleteBtnClicked(int curRoutinePos);
public void onWritingCommentBtnClicked(int curRoutinePos);
public void onWritingWeight(int curRoutinePos, View view); // write weight
}
public void setOnRoutineClickListener(RoutineListAdapter.OnRoutineItemClickListener listener) {
if(this.routinelistener != null)
this.routinelistener = null;
this.routinelistener = listener;
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
Object curItem;
switch (getItemViewType(position)) {
case TYPE_ROUTINE:
curItem = getItem(position);
setRoutineData((RoutineListAdapter.RoutineViewHolder) holder, (RoutineModel) curItem);
break;
case TYPE_ROUTINE_DETAIL:
((RoutineListAdapter.RoutineDetailViewHolder) holder).bind();
break;
case TYPE_ROUTINE_FOOTER:
break;
}
}
private class RoutineDetailViewHolder extends RecyclerView.ViewHolder {
private TextView set;
private EditText weight;
public RoutineDetailViewHolder(@NonNull View itemView) {
super(itemView);
set = itemView.findViewById(R.id.set);
weight = itemView.findViewById(R.id.weight);
weight.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
routinelistener.onWritingWeight(getAdapterPosition(), itemView);
}
});
}
private void bind() {
RoutineDetailModel item = (RoutineDetailModel) getItem(getAdapterPosition());
set.setText(item.getSet().toString() + "set");
weight.setText(item.getWeight()); // Setting the saved value
}
}
Activity
public class WriteRoutineActivity extends AppCompatActivity implements WritingCommentDialogFragment.OnDialogClosedListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_write_routine);
initViews();
setPageTitle(getIntent());
setRoutineRecyclerview();
diffUtil2 = new RoutineDiffUtil2();
listAdapter = new RoutineListAdapter(diffUtil2);
items = new ArrayList<>();
routine_rv.setAdapter(listAdapter);
listAdapter.setOnRoutineClickListener(new RoutineListAdapter.OnRoutineItemClickListener() {
@Override
public void onWritingWeight(int curRoutinePos, View v) {
RoutineDetailModel item = (RoutineDetailModel) listAdapter.getRoutineItem(curRoutinePos);
EditText weight = v.findViewById(R.id.weight);
item.setWeight(weight.getText().toString()); // This is saved to set the value again when recycled.
});
}
}
如果您需要任何其他额外代码,请告诉我
问题在于您在 onBindViewHolder
中添加的 TextWatcher
。
目前您已设置好,以便每次 RecyclerView
绑定一个视图(每个实际视图可能发生多次),您将添加一个新的 TextWatcher
,然后还将文本设置为项目的权重,然后触发您之前添加的观察者,将项目的权重设置为其他内容,在本例中为空字符串。
您应该做的是在添加另一个听众之前移除所有听众,或者在 onCreateViewHolder
中添加听众并使用支架的适配器位置来获取您的物品。
这里有一些伪代码来阐明我的建议:
在onCreateViewHolder
中添加监听器
RoutineDetailViewHolder {
private EditText weight;
RoutineDetailViewHolder {
weight.addTextChangedListener {
items[adapterPosition].setWeight(...)
}
}
}
再次绑定前删除监听器:
RoutineDetailViewHolder {
private EditText weight;
private TextWatcher weightWatcher;
void bind() {
weight.removeTextChangedListener(weightWatcher);
weightWatcher = new TextWatcher();
weight.addOnTextChangedListener(weightWatcher);
}
}
滚动前的数据
滚动后的数据
我的应用程序的问题如上图所示。
输入数据后,如果我在将项目添加为可滚动后滚动,数据会消失。
进一步说明,有时输入的数据会出现在已添加的其他项目中。
解释一下这个应用,它是一个运动记录应用,它使用multi-type recycler view
。
我用了ListAdapter
,也用了DiffUtil
。和图片相关的是Detail item
.
TextWatcher
用于保存输入的数据。
我一直在寻找解决这个问题的方法。
搜索次数最多的两个解决方案是 here
使用
getItemViewType()
、getItemId()
->我使用了这个link中的方法,但是没有解决问题。Using
setIsRecyclable(false)
inside holder -> 这个方法奏效了。但是我听说setIsRecyclable(false)
是一个没有recycle
的函数。 如果我使用它,这不是一个好方法吗,因为使用RecyclerView
没有任何优势?
RoutineAdapter.java
public class RoutineListAdapter extends ListAdapter<Object, RecyclerView.ViewHolder> {
Context context;
RoutineListAdapter.OnRoutineItemClickListener routinelistener;
RoutineListAdapter.OnRoutineAddClickListener routineAddListener;
final static int TYPE_ROUTINE = 1;
final static int TYPE_ROUTINE_DETAIL = 2;
final static int TYPE_ROUTINE_FOOTER = 3;
public RoutineListAdapter(@NonNull DiffUtil.ItemCallback<Object> diffCallback) {
super(diffCallback);
}
// add routine interface
public interface OnRoutineAddClickListener {
public void onAddRoutineClick();
}
public void setOnAddRoutineClickListener(RoutineListAdapter.OnRoutineAddClickListener listener) {
this.routineAddListener = listener;
}
// add/remove detail interface
public interface OnRoutineItemClickListener {
public void onAddBtnClicked(int curRoutinePos);
public void onDeleteBtnClicked(int curRoutinePos);
public void onWritingCommentBtnClicked(int curRoutinePos);
}
public void setOnRoutineClickListener(RoutineListAdapter.OnRoutineItemClickListener listener) {
this.routinelistener = listener;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
context = parent.getContext();
View itemView;
if(viewType == TYPE_ROUTINE){
itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.routine_item, parent, false);
return new RoutineListAdapter.RoutineViewHolder(itemView);
}
else if(viewType == TYPE_ROUTINE_DETAIL){
itemView = LayoutInflater.from(context).inflate(R.layout.routine_detail_item, parent, false);
return new RoutineListAdapter.RoutineDetailViewHolder(itemView);
}
else {
itemView = LayoutInflater.from(context).inflate(R.layout.add_routine_item, parent, false);
return new RoutineListAdapter.RoutineAddFooterViewHolder(itemView);
}
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
Object curItem;
switch (getItemViewType(position)) {
case TYPE_ROUTINE:
curItem = getItem(position);
setRoutineData((RoutineListAdapter.RoutineViewHolder) holder, (RoutineModel) curItem);
break;
case TYPE_ROUTINE_DETAIL:
curItem = getItem(position);
RoutineDetailModel item = (RoutineDetailModel) curItem;
((RoutineListAdapter.RoutineDetailViewHolder) holder).bind(item);
((RoutineDetailViewHolder) holder).weight.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
item.setWeight(((RoutineDetailViewHolder) holder).weight.getText().toString());
}
});
break;
case TYPE_ROUTINE_FOOTER:
break;
}
}
private void setRoutineData(RoutineListAdapter.RoutineViewHolder holder, RoutineModel routineItem){
holder.routine.setText(routineItem.getRoutine());
}
public Object getRoutineItem(int position) {
if(getCurrentList() == null || position < 0 || position >= getCurrentList().size())
return null;
return getItem(position);
}
@Override
public int getItemCount() {
return getCurrentList().size() + 1;
}
@Override
public int getItemViewType(int position) {
if(position == getCurrentList().size()) {
return TYPE_ROUTINE_FOOTER;
}
else {
Object obj = getItem(position);
if(obj instanceof RoutineModel) {
return TYPE_ROUTINE;
}
else {
// obj instanceof RoutineDetailModel
return TYPE_ROUTINE_DETAIL;
}
}
}
private class RoutineViewHolder extends RecyclerView.ViewHolder {
public TextView routine;
public Button addSet;
public Button deleteSet;
public Button comment;
public RoutineViewHolder(@NonNull View itemView) {
super(itemView);
routine = itemView.findViewById(R.id.routine);
addSet = itemView.findViewById(R.id.add_set);
deleteSet = itemView.findViewById(R.id.delete_set);
comment = itemView.findViewById(R.id.write_comment);
addSet.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(routinelistener != null && getAdapterPosition() != RecyclerView.NO_POSITION)
routinelistener.onAddBtnClicked(getAdapterPosition());
}
});
deleteSet.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(routinelistener != null && getAdapterPosition() != RecyclerView.NO_POSITION)
routinelistener.onDeleteBtnClicked(getAdapterPosition());
}
});
comment.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(routinelistener != null && getAdapterPosition() != RecyclerView.NO_POSITION)
routinelistener.onWritingCommentBtnClicked(getAdapterPosition());
}
});
}
}
private class RoutineDetailViewHolder extends RecyclerView.ViewHolder {
private TextView set;
private EditText weight;
public RoutineDetailViewHolder(@NonNull View itemView) {
super(itemView);
set = itemView.findViewById(R.id.set);
weight = itemView.findViewById(R.id.weight);
}
private void bind(RoutineDetailModel item) {
set.setText(item.getSet().toString() + "set");
weight.setText(item.getWeight());
}
}
private class RoutineAddFooterViewHolder extends RecyclerView.ViewHolder {
TextView textView;
public RoutineAddFooterViewHolder(@NonNull View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.add_text);
ConstraintLayout regionForClick = itemView.findViewById(R.id.clickable_layout);
regionForClick.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (routineAddListener != null) {
routineAddListener.onAddRoutineClick();
}
}
});
}
}
}
已更新
适配器
public class RoutineListAdapter extends ListAdapter<Object, RecyclerView.ViewHolder> {
// detail add / remove iterface
public interface OnRoutineItemClickListener {
public void onAddBtnClicked(int curRoutinePos);
public void onDeleteBtnClicked(int curRoutinePos);
public void onWritingCommentBtnClicked(int curRoutinePos);
public void onWritingWeight(int curRoutinePos, View view); // write weight
}
public void setOnRoutineClickListener(RoutineListAdapter.OnRoutineItemClickListener listener) {
if(this.routinelistener != null)
this.routinelistener = null;
this.routinelistener = listener;
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
Object curItem;
switch (getItemViewType(position)) {
case TYPE_ROUTINE:
curItem = getItem(position);
setRoutineData((RoutineListAdapter.RoutineViewHolder) holder, (RoutineModel) curItem);
break;
case TYPE_ROUTINE_DETAIL:
((RoutineListAdapter.RoutineDetailViewHolder) holder).bind();
break;
case TYPE_ROUTINE_FOOTER:
break;
}
}
private class RoutineDetailViewHolder extends RecyclerView.ViewHolder {
private TextView set;
private EditText weight;
public RoutineDetailViewHolder(@NonNull View itemView) {
super(itemView);
set = itemView.findViewById(R.id.set);
weight = itemView.findViewById(R.id.weight);
weight.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
routinelistener.onWritingWeight(getAdapterPosition(), itemView);
}
});
}
private void bind() {
RoutineDetailModel item = (RoutineDetailModel) getItem(getAdapterPosition());
set.setText(item.getSet().toString() + "set");
weight.setText(item.getWeight()); // Setting the saved value
}
}
Activity
public class WriteRoutineActivity extends AppCompatActivity implements WritingCommentDialogFragment.OnDialogClosedListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_write_routine);
initViews();
setPageTitle(getIntent());
setRoutineRecyclerview();
diffUtil2 = new RoutineDiffUtil2();
listAdapter = new RoutineListAdapter(diffUtil2);
items = new ArrayList<>();
routine_rv.setAdapter(listAdapter);
listAdapter.setOnRoutineClickListener(new RoutineListAdapter.OnRoutineItemClickListener() {
@Override
public void onWritingWeight(int curRoutinePos, View v) {
RoutineDetailModel item = (RoutineDetailModel) listAdapter.getRoutineItem(curRoutinePos);
EditText weight = v.findViewById(R.id.weight);
item.setWeight(weight.getText().toString()); // This is saved to set the value again when recycled.
});
}
}
如果您需要任何其他额外代码,请告诉我
问题在于您在 onBindViewHolder
中添加的 TextWatcher
。
目前您已设置好,以便每次 RecyclerView
绑定一个视图(每个实际视图可能发生多次),您将添加一个新的 TextWatcher
,然后还将文本设置为项目的权重,然后触发您之前添加的观察者,将项目的权重设置为其他内容,在本例中为空字符串。
您应该做的是在添加另一个听众之前移除所有听众,或者在 onCreateViewHolder
中添加听众并使用支架的适配器位置来获取您的物品。
这里有一些伪代码来阐明我的建议:
在onCreateViewHolder
RoutineDetailViewHolder {
private EditText weight;
RoutineDetailViewHolder {
weight.addTextChangedListener {
items[adapterPosition].setWeight(...)
}
}
}
再次绑定前删除监听器:
RoutineDetailViewHolder {
private EditText weight;
private TextWatcher weightWatcher;
void bind() {
weight.removeTextChangedListener(weightWatcher);
weightWatcher = new TextWatcher();
weight.addOnTextChangedListener(weightWatcher);
}
}