滚动完成前Recyclerview滚动不顺畅

Posted

技术标签:

【中文标题】滚动完成前Recyclerview滚动不顺畅【英文标题】:Recyclerview not scrolling smoothly before scrolling finish 【发布时间】:2018-12-15 20:21:13 【问题描述】:

我在滚动时遇到问题,我的意思是滚动速度很快,但在滚动完成之前看起来像是滞后了 这里我定义了 RecyclerView :

RecyclerView recyclerView=fragment.getRecyclerView();
        LinearLayoutManager layoutManager = new LinearLayoutManager(fragment.getActivity(), LinearLayoutManager.VERTICAL, false);
        recyclerView.setLayoutManager(layoutManager);
        recyclerView.addItemDecoration(new DividerItemDecoration(fragment.getActivity(), LinearLayoutManager.VERTICAL));
 ArrayList<InstaPostModel> rowListItems=getInstaPostListSample();
        InstaPostAdapter rcAdapter = new InstaPostAdapter(rowListItems);
        recyclerView.setAdapter(rcAdapter);

这里是onBindViewHolder

@Override
public void onBindViewHolder(ViewHolder holder, int position) 
    final InstaPostModel p = items.get(position);
    context = holder.itemView.getContext();
    Glide.with(context).load(R.mipmap.mee_small).into(holder.userPhoto);
    Glide.with(context).load(R.drawable.post_image).into(holder.photo_content);
    Glide.with(context).load(R.mipmap.love_gray_icon).into(holder.bt_like);
    Glide.with(context).load(R.mipmap.comment_icon).into(holder.bt_comment);
    Glide.with(context).load(R.mipmap.share_icon).into(holder.bt_share);

    holder.userNameTextView.setText(p.getPosterUserName());
    holder.postContent.setText(p.getText());
    holder.post_date.setReferenceTime(p.getDate().getTime());

这里是 RecyclerView.xml

<?xml version="1.0" encoding="utf-8"?>

    <android.support.v7.widget.RecyclerView
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/qatar_now_posts_recycler_view"
        android:layout_
        android:layout_
        android:scrollbars="none"
        tools:context=".uis.fragments.home.QatarNowFragment"
        />

编辑 我也在同一个片段中有底部导航栏,它在滚动时显示

编辑 2 这是视频 link 显示滞后 我尝试了所有解决方案,但没有人帮助我。 我已经添加了我所有的代码 请帮忙?

【问题讨论】:

片段的父布局是什么?如果是滚动条,请参考:***.com/questions/32525314/… 我只有 recyclerview 元素 @MohammadSommakia 这些图片的尺寸这么大吗? 不,大约 50 kb 在您的 recyclerview 的每个项目中,您都在加载相同的图像。那你为什么不在布局文件中设置这些图像呢? 【参考方案1】:

可能是图片太大

你可以使用滑动来调整它的大小

Glide.with(context)
    .load(imgUrl)
    .diskCacheStrategy(DiskCacheStrategy.ALL)
    .error(placeholder)
    .placeholder(placeholder)
    .dontTransform()
    .override(200,200)
    .into(imageView);

【讨论】:

【参考方案2】:

我没有发现您的代码有任何问题。仍然需要考虑以下几点。

(1) 如果您从 API 获得的 图像 尺寸太大,会发生什么情况。检查 API 的大小并使其最大减少 200 kb。 (100-150 kb 也足够了)。

(2) 您不应禁用缓存,因为它会通过将图像存储在缓存中并在下次通过缓存而不是通过 URL 加载来改善用户体验。

(3) 您现在不需要使用material-ripple 库来获得 Android 中可用的简单波纹效果。将此属性添加到您的 ImageView

 android:background="?attr/selectableItemBackground"

(4) 检查一次加载的项目数。您不应该一次加载许多项目。见Architecture component paging library

(5) 还要检查您的资源大小,例如love_gray_icon。我认为它应该是最大 70*70。如果你有比这更大的,你应该调整它的大小。 Resize multiple images at once。还可以在使用前压缩图像,这将减少图像大小高达 80%。调整大小后,compress images.

(6) Glide 是一个维护良好的库,所以onViewRecycled 是多余的。

最后对你的代码进行一点修改。

  private void loadImage(ImageView iv, String url) 
        Glide.with(iv.getContext()).load(url).thumbnail(0.5f).into(iv);
    

    private void loadImage(ImageView iv, int resId) 
        Glide.with(iv.getContext()).load(resId).thumbnail(0.5f).into(iv);
    

    // Replace the contents of a view (invoked by the layout manager)
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) 
        final InstaPostModel p = items.get(position);

        loadImage(holder.userPhoto, Urls.BASE_URI + p.getUserPhotoUrl());
        loadImage(holder.photo_content, Urls.BASE_URI + p.getPostPhotoUrl());
        loadImage(holder.bt_like, R.mipmap.love_gray_icon);
        loadImage(holder.bt_comment, R.mipmap.comment_icon);
        loadImage(holder.bt_share, R.mipmap.share_icon);

        holder.userNameTextView.setText(p.getPosterUserName());
        holder.postContent.setText(p.getText());
        holder.post_date.setReferenceTime(p.getDate().getTime());
    

【讨论】:

【参考方案3】:

我认为滚动问题可能是因为您在绑定时重新加载图像。您可以在加载照片时使用缓存,当然还有分页。

【讨论】:

如果您需要加载许多图像。您可以一次加载 10 个,当列表结束时,然后加载下一个图像块。 在我的例子中只有大约 5 个帖子,我看到内存大约 100 mb 我不知道如何减少它 尝试缓存一次。如果问题仍然存在,请检查分析器中的内存。【参考方案4】:

你可以优化你的代码,

@Override
public void onBindViewHolder(ViewHolder holder, int position) 
    final InstaPostModel p = items.get(position);
    context = holder.itemView.getContext();
   setImage(holder.userPhoto, p.getUserPhotoUrl());
   setImage(holder.photo_content, p.getPostPhotoUrl());
   setImage(holder.bt_like, R.mipmap.love_gray_icon);

        holder.userNameTextView.setText(p.getPosterUserName());
        holder.postContent.setText(p.getText());
        holder.post_date.setReferenceTime(p.getDate().getTime());



private void setImage(ImageView iv, String url)

          Glide.with(context)
            .load(Urls.BASE_URI +url).thumbnail(0.5f)
            .apply(new RequestOptions()
                    .diskCacheStrategy(DiskCacheStrategy.NONE)
                    .skipMemoryCache(true).dontAnimate()
            )
            .into(iv);

private void setImage(ImageView iv, int resource)

          Glide.with(context)
            .load(resource).thumbnail(0.5f)
            .apply(new RequestOptions()
                    .diskCacheStrategy(DiskCacheStrategy.NONE)
                    .skipMemoryCache(true).dontAnimate()
            )
            .into(iv);

渲染时肯定会减少负载。请确保仅在适配器中遍历时使用变量,因为每次调用 list.get(position) 都会占用更多内存。

【讨论】:

你刚刚在他的代码中做了方法,这不会提高性能,因为它和他的代码一样。如果你在 Constructor 中初始化一次 Glide 会有所不同。 是的..这将重用相同的 Glide 实例。 您的代码不会重用 Glide 实例,它会再次初始化,因为您已将其放入方法中。 那么如何在构造中初始化一次滑翔然后使用它@Khemraj? @MohammadSommakia 这不会产生可衡量的差异。 Glide 是一个维护良好的库。但是你还有很多事情要考虑,看我的回答。【参考方案5】:

我有几个改进建议给你。

您在列表项中加载了太多图像。尽可能减少图像加载。例如,您可能会考虑为喜欢和分享按钮设置默认图像,这些图像在您的应用程序中通常不会更改。在列表项布局中将它们设置为android:src。 检查 Instagram 图片是否太大。您可能会获得一个 API 来加载平均大小的图像。 使用选择器而不是创建嵌套视图可以在单击列表项中的点赞或分享按钮时产生连锁反应。尽量保持列表项布局尽可能简单,并尽量避免嵌套视图层次结构。 从您的适配器中删除onViewRecycled 函数,我认为这是不必要的。 使用在构造函数中传递给它的Context 初始化适配器。您不必每次都在您的onBindViewHodler 中获取它。 您不必每次都在onBindViewHolder 函数中初始化RequestOption。只需初始化一次并在每个 Glide 图像加载器中使用它。 删除按钮、评论和共享图像加载器,并在其中提供图像作为布局的来源,如前所述。 使用DiskCacheStrategy.ALL 缓存您的图像,以便在您每次打开应用程序或加载RecyclerView 时都不会加载它。

因此,最终的适配器应该如下所示。

public class InstaPostAdapter
        extends RecyclerView.Adapter<InstaPostAdapter.ViewHolder> 

    private List<InstaPostModel> items = new ArrayList<>();
    Context context;

    // Initialize the context once, use it everywhere
    public InstaPostAdapter(List<InstaPostModel> items, Context context) 
        this.items = items;
        this.context = context;
    

    @Override
    public InstaPostAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) 
        // create a new view
        View v = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.insta_post_list_item, parent, false);
        // set the view's size, margins, paddings and layout parameters
        return new ViewHolder(v);
    

    // Replace the contents of a view (invoked by the layout manager)
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) 
        final InstaPostModel p = items.get(position);
        RequestOptions requestOptions = new RequestOptions()
                .diskCacheStrategy(DiskCacheStrategy.ALL);

        Glide.with(context)
                .load(Urls.BASE_URI + items.get(position).getUserPhotoUrl()).thumbnail(0.5f)
                .apply(requestOptions)
                .into(holder.userPhoto);
        Glide.with(context)
                .load(Urls.BASE_URI + items.get(position).getPostPhotoUrl()).thumbnail(0.5f)
                .apply(requestOptions)
                .into(holder.photo_content);

        // Removed the like, comment, share images which should be set from layout.

        holder.userNameTextView.setText(p.getPosterUserName());
        holder.postContent.setText(p.getText());
        holder.post_date.setReferenceTime(p.getDate().getTime());
    

    // Return the size of your dataset (invoked by the layout manager)
    @Override
    public int getItemCount() 
        return items.size();
    

    @Override
    public long getItemId(int position) 
        return position;
    

    public void add(InstaPostModel post, int i) 
        items.add(post);
        notifyItemInserted(i);
    

    public class ViewHolder extends RecyclerView.ViewHolder 
        // each data item is just a string in this case
        public ImageView userPhoto;
        public TextView userNameTextView;
        public ImageView bt_more;
        public ImageView photo_content;
        public ImageView bt_like;
        public ImageView bt_comment;
        public ImageView bt_share;
        public TextView postContent;
        public RelativeTimeTextView post_date;

        public ViewHolder(View v) 
            super(v);
            userPhoto = v.findViewById(R.id.insta_user_photo);
            userNameTextView = v.findViewById(R.id.insta_user_name);
            bt_more = v.findViewById(R.id.insta_bt_more);
            photo_content = v.findViewById(R.id.insta_photo_content);
            bt_like = v.findViewById(R.id.insta_bt_like);
            bt_comment = v.findViewById(R.id.insta_bt_comment);
            bt_share = v.findViewById(R.id.insta_bt_share);
            postContent = v.findViewById(R.id.insta_post_content);
            post_date = v.findViewById(R.id.insta_post_date);
            setClickListener();
        

        private void setClickListener() 
            bt_more.setOnClickListener(view -> 
                PopupMenu popupMenu = new PopupMenu(context, view);
                popupMenu.setOnMenuItemClickListener(item -> 
                    Snackbar.make(view, item.getTitle() + " Clicked", Snackbar.LENGTH_SHORT).show();
                    return true;
                );
                popupMenu.inflate(R.menu.menu_feed_popup);
                popupMenu.show();
            );
            bt_like.setOnClickListener(view -> Snackbar.make(view, "Like Clicked", Snackbar.LENGTH_SHORT).show());
            bt_comment.setOnClickListener(view -> Snackbar.make(view, "Comment Clicked", Snackbar.LENGTH_SHORT).show());
            bt_share.setOnClickListener(view -> Snackbar.make(view, "Share Clicked", Snackbar.LENGTH_SHORT).show());
        
    

我想我为 Flickr 图片搜索做过一个类似的项目。您可能会找到project here in Github。

希望对您有所帮助!

【讨论】:

我认为问题不是来自图像,我认为它是 java 的内存泄漏和 android profillers 中的图形,java 为 25,图形为 25 mb【参考方案6】:

如果 recyclerview 中的项目大小相同,您可以尝试: recyclerview.setHasFixedSize(true);

【讨论】:

try : set image frome drawable don't need glide. setHasFixedSize 是关于 RecyclerView,而不是它包含的项目。如果您的 RecyclerView 将保持相同的高度,则应将其设置为 true。

以上是关于滚动完成前Recyclerview滚动不顺畅的主要内容,如果未能解决你的问题,请参考以下文章

NestedScrollview 内的 RecyclerView 滚动不顺畅

嵌套的 RecyclerView 滚动无法向下滚动 ViewPager2 的 BottomSheetBehavior

RecyclerView 优化—滑动性能提升

RecyclerView 优化—滑动性能提升

Android必知必会 - RecyclerView 恢复上次滚动位置

iCarousel 滚动不顺畅