为什么在 Recycler View 中滚动后值会消失? [英] Why do values disappear after scrolling in Recycler View?
问题描述
滚动前的数据
滚动后的数据
我的应用程序的问题如上图所示.
The problem with my app is shown in the pictures above.
输入数据后,如果我在将项目添加为可滚动后滚动,数据就会消失.
After entering the data, if i scroll after adding the item as scrollable, the data disappears.
作为进一步的解释,有时输入的数据会出现在已添加的其他项目中.
As a further explanation, sometimes the entered data appears in other items that have been added.
解释一下这个应用程序,它是一个运动记录应用程序,它使用了多类型回收器视图
.
To explain the app, it is an exercise recording app, and it uses a multi-type recycler view
.
我使用了 ListAdapter
和 DiffUtil
.而图片与Detail item
相关.
I used ListAdapter
and also DiffUtil
. And the picture is related to the Detail item
.
TextWatcher
用于保存输入的数据.
TextWatcher
was used to save the entered data.
我一直在寻找解决这个问题的方法.
I've been searching a lot to solve this.
搜索最多的两个解决方案是这里
The two most searched solutions are here
使用
getItemViewType()
,getItemId()
->我使用此链接中所示的方法,但没有解决问题.
using
getItemViewType()
,getItemId()
->I used this method as shown in this link, but it didn't solve the problem.
在容器内使用 setIsRecyclable(false)
->这个方法奏效了.但是听说setIsRecyclable(false)
是一个不recycle
的函数.如果我使用它,这不是一个好方法,因为使用 RecyclerView
没有优势吗?
Using setIsRecyclable(false)
inside the holder
-> This method worked. But I heard that setIsRecyclable(false)
is a function that does not recycle
.
If i use this, isn't it a good way to do it because there is no advantage to using 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
}
}
活动
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
.
The issue lies with your TextWatcher
that you're adding in onBindViewHolder
.
现在你已经设置好了,每次 RecyclerView
绑定一个视图(每个实际视图可能发生多次),你都会添加一个新的 TextWatcher
然后还将文本设置为项目的权重,然后触发您之前添加的观察者,将项目的权重设置为其他内容,在本例中为空字符串.
At the moment you have it setup so that every time the RecyclerView
binds a view (which can happen multiple times per actual view), you're adding a new TextWatcher
and then also setting the text to the item's weight, which is then triggering the previous watchers you added, setting the item's weight to something else, in this case an empty string.
您应该做的是在添加另一个侦听器之前删除所有侦听器,或者在 onCreateViewHolder
中添加侦听器并使用支架的适配器位置来获取您的项目.
What you should be doing is either removing all listeners before you add another one, or add the listener in onCreateViewHolder
and use the holder's adapter position to get your item.
这是一些伪代码来阐明我的建议:
Here is some pseudocode to clarify my suggestions:
在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);
}
}
这篇关于为什么在 Recycler View 中滚动后值会消失?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!