Drawable Recyclerview 背景变化缓慢,在主线程上做太多工作时出错
Drawable Recyclerview background change slow, error doing too much work on the main thread
我正在尝试设置在 recyclerview 上选择的项目的状态。 UI 通过将单个单元格框架布局的背景从一个可绘制对象 xml 更改为另一个来实现。
在recyclerview onBindViewHolder 中我做了这样的改变:
@Override
public void onBindViewHolder(final DeviceAlarmTonesHolder holder, final int position) {
final DeviceAlarmTone alarmTone = alarmTones.get(position);
// set click listener
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
holder.alarmTonesButtonBackground.setBackgroundResource(R.drawable.layout_bg_selected);
itemClickListener.onItemClicked(holder, alarmTone, position, oldPostion);
// Refresh the ui for the previous button
if (oldPostion != -1){
notifyItemChanged(oldPostion);
}
oldPostion = position;
}
});
String alarmToneString = alarmTones.get(position).getNotificationTitle();
holder.alarmTonesNameTextView.setText(alarmToneString);
}
基本上在 onclick 中,我正在更改当前项目的背景,然后在旧项目上调用 notifyitemchanged 以将背景恢复为未选中状态。
正在更改的背景是 xml 个像这样的可绘制对象:
未选择的背景:
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#212121"/>
<corners android:radius="10dip"/>
<padding android:left="0dip" android:top="0dip" android:right="0dip" android:bottom="0dip" />
</shape>
选择的背景:
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#0091EA"/>
<corners android:radius="10dip"/>
<padding android:left="0dip" android:top="0dip" android:right="0dip" android:bottom="0dip" />
</shape>
在此先感谢您的帮助。
更新这是我的完整适配器:
public class DeviceAlarmToneAdapter extends RecyclerView.Adapter<DeviceAlarmTonesHolder>{
Context context;
ArrayList<DeviceAlarmTone> alarmTones;
DeviceAlarmToneClickListener itemClickListener;
int oldPostion = -1;
public DeviceAlarmToneAdapter(Context context, ArrayList<DeviceAlarmTone> alarmTones, DeviceAlarmToneClickListener itemClickListener) {
this.context = context;
this.alarmTones = alarmTones;
this.itemClickListener = itemClickListener;
}
@Override
public DeviceAlarmTonesHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.singlecell_devicealarmtoneslist, parent, false);
DeviceAlarmTonesHolder holder = new DeviceAlarmTonesHolder(v, context);
return holder;
}
@Override
public void onBindViewHolder(final DeviceAlarmTonesHolder holder, final int position) {
final DeviceAlarmTone alarmTone = alarmTones.get(position);
// set click listener
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
holder.alarmTonesButtonBackgroundUnselected.setVisibility(View.INVISIBLE);
holder.alarmTonesButtonBackgroundSelected.setVisibility(View.VISIBLE);
itemClickListener.onItemClicked(holder, alarmTone, position, oldPostion);
// Refresh the ui for the previous button
if (oldPostion != -1){
notifyItemChanged(oldPostion);
}
oldPostion = position;
}
});
String alarmToneString = alarmTones.get(position).getNotificationTitle();
holder.alarmTonesNameTextView.setText(alarmToneString);
}
@Override
public int getItemCount() {
return alarmTones.size();
}
}
public class DeviceAlarmToneAdapter extends RecyclerView.Adapter<DeviceAlarmTonesHolder>{
Context context;
ArrayList<DeviceAlarmTone> alarmTones;
DeviceAlarmToneClickListener itemClickListener;
int oldPostion = -1;
public DeviceAlarmToneAdapter(Context context, ArrayList<DeviceAlarmTone> alarmTones, DeviceAlarmToneClickListener itemClickListener) {
this.context = context;
this.alarmTones = alarmTones;
this.itemClickListener = itemClickListener;
}
@Override
public DeviceAlarmTonesHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.singlecell_devicealarmtoneslist, parent, false);
DeviceAlarmTonesHolder holder = new DeviceAlarmTonesHolder(v, context);
return holder;
}
@Override
public void onBindViewHolder(final DeviceAlarmTonesHolder holder, final int position) {
final DeviceAlarmTone alarmTone = alarmTones.get(position);
String alarmToneString = alarmTones.get(position).getNotificationTitle();
holder.alarmTonesNameTextView.setText(alarmToneString);
}
@Override
public int getItemCount() {
return alarmTones.size();
}
public class DeviceAlarmTonesHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
// view fields are here
public DeviceAlarmTonesHolder(View itemView){
super(itemView);
// find view by ids by using itemview.findViewById(id)
itemView.setOnClickListener(this);
}
@Override
public void onClick(View view) {
// you shouldnt use two views as background objects to show unselected or selected background. Use selector drawables xml instead... and call view.setSelected(true or false);
// Since your modifyng view object itself you dont need to call notifyItemChanged...
// with this tecnique you have only one View.OnClickListener object which belongs to your holder that have only one instance. This will lower your memory usage and fluid scrolls.
if (itemClickListener != null){
int position = getAdapterPosition();
alarmTone = alarmTones.get(position);
itemClickListener.onAlarmClicked(alarmTone);
}
}
}
}
我只是绕过更新以前的背景来检查是否从那里出现问题。
您需要降低onBindHolder 中的进程并避免在其中创建对象。因为每次滚动时都不想创建更多对象,而是使用旧对象。
// 你不应该使用两个视图作为背景对象来显示未选择或选择的背景。使用选择器 drawables xml 代替...并调用 view.setSelected(true or false);同时优化你的背景。缩小它们的尺寸。有很多在线工具可以为您完成这项工作。另一方面,如果它们是 jpeg 和 png 之类的图片,请将它们放入 drawable-nodpi 文件夹以排除调整大小。
由于您修改了视图对象本身,因此您不需要调用 notifyItemChanged...
使用此技术,您只有一个 View.OnClickListener 对象,它属于您的持有者且只有一个实例。
这将降低您的内存使用率和流畅的卷轴。
如果出现问题,您可能需要使用 Single Choice RecyclerView 库或寻找其他解决方案。
祝你好运
我正在尝试设置在 recyclerview 上选择的项目的状态。 UI 通过将单个单元格框架布局的背景从一个可绘制对象 xml 更改为另一个来实现。
在recyclerview onBindViewHolder 中我做了这样的改变:
@Override
public void onBindViewHolder(final DeviceAlarmTonesHolder holder, final int position) {
final DeviceAlarmTone alarmTone = alarmTones.get(position);
// set click listener
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
holder.alarmTonesButtonBackground.setBackgroundResource(R.drawable.layout_bg_selected);
itemClickListener.onItemClicked(holder, alarmTone, position, oldPostion);
// Refresh the ui for the previous button
if (oldPostion != -1){
notifyItemChanged(oldPostion);
}
oldPostion = position;
}
});
String alarmToneString = alarmTones.get(position).getNotificationTitle();
holder.alarmTonesNameTextView.setText(alarmToneString);
}
基本上在 onclick 中,我正在更改当前项目的背景,然后在旧项目上调用 notifyitemchanged 以将背景恢复为未选中状态。
正在更改的背景是 xml 个像这样的可绘制对象:
未选择的背景:
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#212121"/>
<corners android:radius="10dip"/>
<padding android:left="0dip" android:top="0dip" android:right="0dip" android:bottom="0dip" />
</shape>
选择的背景:
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#0091EA"/>
<corners android:radius="10dip"/>
<padding android:left="0dip" android:top="0dip" android:right="0dip" android:bottom="0dip" />
</shape>
在此先感谢您的帮助。
更新这是我的完整适配器:
public class DeviceAlarmToneAdapter extends RecyclerView.Adapter<DeviceAlarmTonesHolder>{
Context context;
ArrayList<DeviceAlarmTone> alarmTones;
DeviceAlarmToneClickListener itemClickListener;
int oldPostion = -1;
public DeviceAlarmToneAdapter(Context context, ArrayList<DeviceAlarmTone> alarmTones, DeviceAlarmToneClickListener itemClickListener) {
this.context = context;
this.alarmTones = alarmTones;
this.itemClickListener = itemClickListener;
}
@Override
public DeviceAlarmTonesHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.singlecell_devicealarmtoneslist, parent, false);
DeviceAlarmTonesHolder holder = new DeviceAlarmTonesHolder(v, context);
return holder;
}
@Override
public void onBindViewHolder(final DeviceAlarmTonesHolder holder, final int position) {
final DeviceAlarmTone alarmTone = alarmTones.get(position);
// set click listener
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
holder.alarmTonesButtonBackgroundUnselected.setVisibility(View.INVISIBLE);
holder.alarmTonesButtonBackgroundSelected.setVisibility(View.VISIBLE);
itemClickListener.onItemClicked(holder, alarmTone, position, oldPostion);
// Refresh the ui for the previous button
if (oldPostion != -1){
notifyItemChanged(oldPostion);
}
oldPostion = position;
}
});
String alarmToneString = alarmTones.get(position).getNotificationTitle();
holder.alarmTonesNameTextView.setText(alarmToneString);
}
@Override
public int getItemCount() {
return alarmTones.size();
}
}
public class DeviceAlarmToneAdapter extends RecyclerView.Adapter<DeviceAlarmTonesHolder>{
Context context;
ArrayList<DeviceAlarmTone> alarmTones;
DeviceAlarmToneClickListener itemClickListener;
int oldPostion = -1;
public DeviceAlarmToneAdapter(Context context, ArrayList<DeviceAlarmTone> alarmTones, DeviceAlarmToneClickListener itemClickListener) {
this.context = context;
this.alarmTones = alarmTones;
this.itemClickListener = itemClickListener;
}
@Override
public DeviceAlarmTonesHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.singlecell_devicealarmtoneslist, parent, false);
DeviceAlarmTonesHolder holder = new DeviceAlarmTonesHolder(v, context);
return holder;
}
@Override
public void onBindViewHolder(final DeviceAlarmTonesHolder holder, final int position) {
final DeviceAlarmTone alarmTone = alarmTones.get(position);
String alarmToneString = alarmTones.get(position).getNotificationTitle();
holder.alarmTonesNameTextView.setText(alarmToneString);
}
@Override
public int getItemCount() {
return alarmTones.size();
}
public class DeviceAlarmTonesHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
// view fields are here
public DeviceAlarmTonesHolder(View itemView){
super(itemView);
// find view by ids by using itemview.findViewById(id)
itemView.setOnClickListener(this);
}
@Override
public void onClick(View view) {
// you shouldnt use two views as background objects to show unselected or selected background. Use selector drawables xml instead... and call view.setSelected(true or false);
// Since your modifyng view object itself you dont need to call notifyItemChanged...
// with this tecnique you have only one View.OnClickListener object which belongs to your holder that have only one instance. This will lower your memory usage and fluid scrolls.
if (itemClickListener != null){
int position = getAdapterPosition();
alarmTone = alarmTones.get(position);
itemClickListener.onAlarmClicked(alarmTone);
}
}
}
}
我只是绕过更新以前的背景来检查是否从那里出现问题。
您需要降低onBindHolder 中的进程并避免在其中创建对象。因为每次滚动时都不想创建更多对象,而是使用旧对象。
// 你不应该使用两个视图作为背景对象来显示未选择或选择的背景。使用选择器 drawables xml 代替...并调用 view.setSelected(true or false);同时优化你的背景。缩小它们的尺寸。有很多在线工具可以为您完成这项工作。另一方面,如果它们是 jpeg 和 png 之类的图片,请将它们放入 drawable-nodpi 文件夹以排除调整大小。
由于您修改了视图对象本身,因此您不需要调用 notifyItemChanged...
使用此技术,您只有一个 View.OnClickListener 对象,它属于您的持有者且只有一个实例。
这将降低您的内存使用率和流畅的卷轴。
如果出现问题,您可能需要使用 Single Choice RecyclerView 库或寻找其他解决方案。
祝你好运