[英]Recyclerview - onCreateViewHolder called for each list item
[英]Recyclerview onCreateViewHolder called for every item
我在NestedScrollView
中有一个RecyclerView
,它显示了一些异步下载的数据。 问题是当项目被启动时存在显着的滞后。 经过一些测试后,我发现问题是每个项目都调用onCreateViewHolder,并且需要一些时间来扩充布局。 这是我的适配器:
public class EpisodeAdapter extends RecyclerView.Adapter<EpisodeAdapter.ViewHolder> {
private static final String TAG = "EpisodeAdapter";
private static final int NO_POSITION = -1;
private static final int EXPAND = 1;
private static final int COLLAPSE = 2;
private SparseArray<Episode> episodes;
private OnItemClickListener<Episode> downloadClickListener;
private OnItemClickListener<Episode> playClickListener;
private RecyclerView recyclerView;
private final EpisodeAnimator episodeAnimator;
private final Transition expandCollapse;
private int expandedPosition = NO_POSITION;
public EpisodeAdapter() {
episodes = new SparseArray<>();
episodeAnimator = new EpisodeAnimator();
expandCollapse = new AutoTransition();
}
//Called when first loading items
public void swapEpisodes(SparseArray<Episode> newEpisodes){
final int previousSize = episodes.size();
episodes = newEpisodes;
expandedPosition = NO_POSITION;
Log.e(TAG, "Swap called");
if(previousSize == 0) {
notifyItemRangeInserted(0, episodes.size());
}
else {
notifyItemRangeChanged(0, Math.max(previousSize, episodes.size()));
}
}
//Called when downloading other information, this seems to work fine without delay
public void setEpisodesDetails(final List<TmdbEpisode> episodeList){
for (TmdbEpisode episode : episodeList){
final int position = episodes.indexOfKey(episode.getNumber());
notifyItemChanged(position, episode);
}
}
@Override
public EpisodeAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Log.e(TAG, "Start createViewHolder");
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_episode, parent, false);
ViewHolder viewHolder = new ViewHolder(view);
viewHolder.downloadButton.setOnClickListener(v -> {
if(downloadClickListener != null)
downloadClickListener.onItemClick(v, episodes.valueAt(viewHolder.getAdapterPosition()));
});
viewHolder.playButton.setOnClickListener(v -> {
if(playClickListener != null)
playClickListener.onItemClick(v, episodes.valueAt(viewHolder.getAdapterPosition()));
});
viewHolder.itemView.setOnClickListener(v -> {
final int position = viewHolder.getAdapterPosition();
if(position == NO_POSITION) return;
TransitionManager.beginDelayedTransition(recyclerView, expandCollapse);
episodeAnimator.setAnimateMoves(false);
//Collapse any currently expanded items
if(expandedPosition != NO_POSITION){
notifyItemChanged(expandedPosition, COLLAPSE);
}
//Expand clicked item
if(expandedPosition != position){
expandedPosition = position;
notifyItemChanged(position, EXPAND);
}
else {
expandedPosition = NO_POSITION;
}
});
Log.e(TAG, "Finish createViewHolder");
return viewHolder;
}
@Override
public void onBindViewHolder(EpisodeAdapter.ViewHolder holder, int itemPosition) {
Log.e(TAG, "Start");
holder.number.setText(String.valueOf(episodes.keyAt(itemPosition)));
holder.details.setVisibility(View.GONE);
holder.itemView.setActivated(false);
Log.e(TAG, "Finish");
}
@Override
public void onBindViewHolder(ViewHolder holder, int position, List<Object> payloads) {
Log.e(TAG, "Start payloads");
if(payloads.contains(EXPAND) || payloads.contains(COLLAPSE)){
setExpanded(holder, position == expandedPosition);
}
else if(!payloads.isEmpty() && payloads.get(0) instanceof TmdbEpisode){
TmdbEpisode episode = (TmdbEpisode) payloads.get(0);
holder.title.setText(episode.getName());
holder.details.setText(episode.getOverview());
}
else {
onBindViewHolder(holder, position);
}
Log.e(TAG, "Finish payloads");
}
private void setExpanded(ViewHolder holder, boolean isExpanded) {
holder.itemView.setActivated(isExpanded);
holder.details.setVisibility((isExpanded) ? View.VISIBLE : View.GONE);
}
public void setPlayClickListener(OnItemClickListener<Episode> onItemClickListener){
playClickListener = onItemClickListener;
}
public void setDownloadClickListener(OnItemClickListener<Episode> onItemClickListener){
downloadClickListener = onItemClickListener;
}
@Override
public int getItemCount() {
return episodes.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
View itemView;
TextView number;
FadeTextSwitcher title;
ImageButton downloadButton;
FloatingActionButton playButton;
TextView details;
ViewHolder(View itemView) {
super(itemView);
Log.e(TAG, "Start constructor");
this.itemView = itemView;
number = itemView.findViewById(R.id.number);
title = itemView.findViewById(R.id.title);
downloadButton = itemView.findViewById(R.id.download_button);
playButton = itemView.findViewById(R.id.play_button);
details = itemView.findViewById(R.id.details);
Log.e(TAG, "Finish constructor");
}
}
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
this.recyclerView = recyclerView;
this.recyclerView.setItemAnimator(episodeAnimator);
expandCollapse.setDuration(recyclerView.getContext().getResources().getInteger(R.integer.episode_expand_collapse_duration));
expandCollapse.setInterpolator(AnimationUtils.loadInterpolator(this.recyclerView.getContext(), android.R.interpolator.fast_out_slow_in));
expandCollapse.addListener(new Transition.TransitionListener() {
@Override
public void onTransitionStart(android.transition.Transition transition) {
EpisodeAdapter.this.recyclerView.setOnTouchListener((v, event) -> true);
}
@Override
public void onTransitionEnd(android.transition.Transition transition) {
episodeAnimator.setAnimateMoves(true);
EpisodeAdapter.this.recyclerView.setOnTouchListener(null);
}
@Override
public void onTransitionCancel(android.transition.Transition transition) {}
@Override
public void onTransitionPause(android.transition.Transition transition) {}
@Override
public void onTransitionResume(android.transition.Transition transition) {}
});
}
static class EpisodeAnimator extends SlideInItemAnimator {
private boolean animateMoves = false;
EpisodeAnimator() {
super();
}
void setAnimateMoves(boolean animateMoves) {
this.animateMoves = animateMoves;
}
@Override
public boolean animateMove(RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY) {
if (!animateMoves) {
dispatchMoveFinished(holder);
return false;
}
return super.animateMove(holder, fromX, fromY, toX, toY);
}
}
}
有没有办法强制为每个项目重用相同的ViewHolder
? 因此onCreateViewHolder
将被调用一次。
我还在nestedScrollingEnabled="false"
设置了nestedScrollingEnabled="false"
。
我在
NestedScrollView
有一个RecyclerView
我猜你的<RecyclerView>
标签的高度定义为wrap_content
。 如果是这样,那意味着您要为数据集中的每个项目充气布局资源(并创建ViewHolder
对象); 可能有数以千计的布局通胀和对象创建。
只有当recyclerview的高度小于显示其子项所需的高度时, RecyclerView
的回收行为才有效。 对于ViewHolder
,创建一个小的两位数的ViewHolder
实例是ViewHolder
(通常你可以在屏幕上看到很多项目,加上一些只能在屏幕外优化视图),但这取决于你的recyclerview的大小这个事实受屏幕大小的限制(即您使用的是match_parent
或固定大小)。
对于在NestedScrollView
具有wrap_content
高度的NestedScrollView
,用户将无法一次看到所有项目,但Android框架只知道您拥有足够大的RecyclerView
来容纳每个项目。您的数据集,因此必须为每个项目创建一个查看器。
您必须找到一种方法来重新设计布局层次结构,以便您可以为RecyclerView
使用一些有限的高度。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.