【问题标题】:RecyclerView slow scrolling performance and lag/halt sometime while scrollingRecyclerView 滚动性能缓慢,并且在滚动时有时会延迟/停止
【发布时间】:2020-11-19 10:08:30
【问题描述】:

我正在使用 recyclerview 显示项目,项目大小可能是 0 - 500。

但它的滚动性能非常慢,有时它会滞后/停止视图 1~2 秒。 我希望在 recyclerView 中有流畅的滚动体验。

即使我从使用 Glide 加载的 cardview 布局中删除图像,它对性能没有影响。

fragment_home.xml

<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentStart="true"
        android:layout_below="@+id/layoutGames">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerViewGamesToday"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:scrollbars="vertical"
            android:scrollbarThumbVertical="@android:color/darker_gray"
            android:scrollbarSize="5dp"/>
    </LinearLayout>

我的 cardview 布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <android.support.v7.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content">


        <android.support.constraint.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:minHeight="35dp">


            <ImageView
                android:id="@+id/imageTeamLogo"
                android:layout_width="20dp"
                android:layout_height="20dp"
                android:layout_marginLeft="8dp"
                android:layout_marginStart="8dp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:srcCompat="@drawable/transparent" />

            <TextView
                android:id="@+id/textViewHomeTeam"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_centerInParent="false"
                android:layout_centerVertical="true"
                android:layout_marginEnd="2dp"
                android:layout_marginLeft="2dp"
                android:layout_marginRight="2dp"
                android:layout_marginStart="2dp"
                android:gravity="right"
                android:text="TextView"
                android:textColor="@color/colorTeamNameListing"
                android:textSize="12sp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toStartOf="@+id/imageViewTeamLogo"
                app:layout_constraintHorizontal_chainStyle="packed"
                app:layout_constraintStart_toEndOf="@+id/imageTeamLogo"
                app:layout_constraintTop_toTopOf="parent" />

            <ImageView
                android:id="@+id/imageViewTeamLogo"
                android:layout_width="25dp"
                android:layout_height="25dp"
                android:layout_centerInParent="false"
                android:layout_centerVertical="true"

                android:layout_marginEnd="2dp"
                android:layout_marginLeft="2dp"
                android:layout_marginRight="2dp"
                android:layout_marginStart="2dp"
                android:padding="4dp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toStartOf="@+id/textViewMatchStatus"
                app:layout_constraintHorizontal_bias="0.5"
                app:layout_constraintStart_toEndOf="@+id/textViewHomeTeam"
                app:layout_constraintTop_toTopOf="parent" />

            <TextView
                android:id="@+id/textViewScore"
                android:layout_width="0dp"
                android:layout_height="15dp"
                android:layout_centerInParent="false"
                android:layout_marginTop="2dp"
                android:gravity="center_vertical|center"
                android:text="3-1"
                android:textColor="@color/colorScoreListing"
                android:textSize="13sp"
                android:textStyle="bold"
                app:layout_constraintEnd_toEndOf="@+id/textViewMatchStatus"
                app:layout_constraintHorizontal_bias="0.5"
                app:layout_constraintStart_toStartOf="@+id/textViewMatchStatus"
                app:layout_constraintTop_toTopOf="parent" />

            <TextView
                android:id="@+id/textViewMatchStatus"
                android:layout_width="70dp"
                android:layout_height="15dp"
                android:layout_below="@+id/textViewStatus"
                android:layout_centerInParent="false"

                android:layout_marginBottom="2dp"
                android:gravity="center_vertical|center"
                android:text="Not Started"
                android:textColor="@color/colorMatchStatus"
                android:textSize="10sp"
                android:textStyle="bold"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.5"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/textViewScore" />

            <ImageView
                android:id="@+id/imageViewAwayTeamLogo"
                android:layout_width="25dp"
                android:layout_height="25dp"
                android:layout_centerInParent="false"
                android:layout_centerVertical="true"

                android:layout_marginEnd="2dp"
                android:layout_marginLeft="2dp"
                android:layout_marginRight="2dp"
                android:layout_marginStart="2dp"
                android:padding="4dp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toStartOf="@+id/textViewAwayTeam"
                app:layout_constraintHorizontal_bias="0.5"
                app:layout_constraintStart_toEndOf="@+id/textViewMatchStatus"
                app:layout_constraintTop_toTopOf="parent" />

            <TextView
                android:id="@+id/textViewAwayTeam"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_alignParentEnd="true"
                android:layout_alignParentTop="true"
                android:layout_marginEnd="2dp"
                android:layout_marginLeft="2dp"
                android:layout_marginRight="2dp"
                android:layout_marginStart="2dp"
                android:gravity="left"
                android:text="TextView"
                android:textColor="@color/colorTeamNameListing"
                android:textSize="12sp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toStartOf="@+id/imageViewFav"
                app:layout_constraintStart_toEndOf="@+id/imageViewAwayTeamLogo"
                app:layout_constraintTop_toTopOf="parent" />

            <ImageView
                android:id="@+id/imageViewFav"
                android:layout_width="20dp"
                android:layout_height="20dp"
                android:layout_marginEnd="8dp"
                android:layout_marginRight="8dp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:srcCompat="@drawable/star" />

        </android.support.constraint.ConstraintLayout>
        <View
            android:layout_width="match_parent"
            android:background="#efefef"
            android:layout_height="1dp" />
    </android.support.v7.widget.CardView>
</LinearLayout>

来自我的适配器类的 onBindViewHolder 方法

@Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        final GamesToday games = gamesTodayList.get(position);
        switch (holder.getItemViewType()) {

            case gamesOnlyRows:
                final GamesTodayViewHolder gamesTodayViewHolder = (GamesTodayViewHolder) holder;
                Glide.with(mCtx)
                        .load(games.getHomeTeamLogo())
                        .apply(options)
                        .into(gamesTodayViewHolder.imageViewHomeTeamLogo);
                Glide.with(mCtx)
                        .load(games.getAwayTeamLogo())
                        .apply(options)
                        .into(gamesTodayViewHolder.imageViewAwayTeamLogo);

                gamesTodayViewHolder.textViewHomeTeam.setText(games.getHomeName());
                if (games.getHomeScore().equals("null")) {
                    gamesTodayViewHolder.textViewScore.setText(games.getDate());
                } else {
                    gamesTodayViewHolder.textViewScore.setText(games.getHomeScore() + " - " + games.getAwayScore());
                }

                gamesTodayViewHolder.imageViewEmptyCircle.setImageResource(R.drawable.transparent);
                if (games.getStatus().equals("CLOSED")) {
                    gamesTodayViewHolder.imageViewEmptyCircle.setImageResource(R.drawable.ft);
                }
                if (games.getStatus().equals("ACTIVE")) {
                    gamesTodayViewHolder.imageViewEmptyCircle.setImageResource(R.drawable.active_icon);
                }

                switch (games.getStatus()) {
                    case "CLOSED":
                        gamesTodayViewHolder.textViewMatchStatus.setText("Finished");
                        break;

                    case "FINISHED_CONFIRMED_1":
                        gamesTodayViewHolder.textViewMatchStatus.setText("Finished");
                        break;

                    case "FINISHED_CONFIRMED_2":
                        gamesTodayViewHolder.textViewMatchStatus.setText("Finished");
                        break;

                    case "NOT_STARTED":
                        gamesTodayViewHolder.textViewMatchStatus.setText("Not Started");
                        break;
                    case "ACTIVE":
                        gamesTodayViewHolder.textViewMatchStatus.setText("Active");
                        break;
                    case "CANCELLED":
                        gamesTodayViewHolder.textViewMatchStatus.setText("Cancelled");
                        break;

                    case "POSTPONED_UNDECIDED":
                        gamesTodayViewHolder.textViewMatchStatus.setText("Postponed");
                        break;

                    default:
                        gamesTodayViewHolder.textViewMatchStatus.setText(games.getStatus());
                }

                gamesTodayViewHolder.textViewAwayTeam.setText(games.getAwayName());
                gamesTodayViewHolder.imageViewFav.setImageResource(R.drawable.star);
                if (checkFavoriteItem(games.getId())) {
                    gamesTodayViewHolder.imageViewFav.setImageResource(R.drawable.fav);
                }

                gamesTodayViewHolder.imageViewFav.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if (checkFavoriteItem(games.getId())) {
                            gamesTodayViewHolder.imageViewFav.setImageResource(R.drawable.star);
                            sharedPreference.removeFavorite(mCtx, games.getId());
                        } else {
                            gamesTodayViewHolder.imageViewFav.setImageResource(R.drawable.fav);
                            sharedPreference.addFavorite(mCtx, games.getId());
                        }
                    }
                });
                break;

            case NotGamesOnlyRows:
                final GamesTodayWithLeagueViewHolder gamesTodayWithLeagueViewHolder = (GamesTodayWithLeagueViewHolder) holder;
                gamesTodayWithLeagueViewHolder.imageViewFavLeague.setImageResource(R.drawable.transparent);
                gamesTodayWithLeagueViewHolder.imageViewFavLeague.setImageResource(R.drawable.star);

                if (checkFavoriteLeagueItem(games.getLeagueId())) {
                    gamesTodayWithLeagueViewHolder.imageViewFavLeague.setImageResource(R.drawable.fav);
                } else {
                    gamesTodayWithLeagueViewHolder.imageViewFavLeague.setImageResource(R.drawable.transparent);
                    gamesTodayWithLeagueViewHolder.imageViewFavLeague.setImageResource(R.drawable.star);
                }
                gamesTodayWithLeagueViewHolder.imageViewFavLeague.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if (checkFavoriteLeagueItem(games.getLeagueId())) {
                            gamesTodayWithLeagueViewHolder.imageViewFavLeague.setImageResource(R.drawable.star);
                            sharedPreference.removeFavoriteLeagues(mCtx, games.getLeagueId());
                        } else {
                            gamesTodayWithLeagueViewHolder.imageViewFavLeague.setImageResource(R.drawable.fav);
                            sharedPreference.addFavoriteLeagues(mCtx, games.getLeagueId());
                        }
                    }
                });
                gamesTodayWithLeagueViewHolder.textViewLeagueName.setText(games.getLeague());
                Glide.with(mCtx)
                        .load(games.getCountryId())
                        .into(gamesTodayWithLeagueViewHolder.imageViewCountryFlag);
                break;

            default:
        }
    }

【问题讨论】:

  • 尝试一次删除子布局中的约束布局
  • @Redman 我尝试删除约束布局并将其替换为线性布局,尽管我的布局变得混乱但现在它的滚动很顺畅。如何使用约束布局修复它?谢谢
  • 约束布局作为回收器子项有一些性能问题,只看到很少发生这种情况并且不知道它发生的原因。所以我不能帮你。只是避免使用约束布局作为列表/回收器子项

标签: android android-recyclerview


【解决方案1】:

它将按需加载适合我的图像

 @Override
 public void onViewAttachedToWindow(@NonNull RecyclerView.ViewHolder holder) {
    super.onViewAttachedToWindow(holder);
    Glide.with(context)
         .load(URL)
         .into(holder.imageView);
}

【讨论】:

  • 给那个人一枚奖章!并在当前位置获取项目:.load(dataList.get(holder.getLayoutPosition()).getImage())
【解决方案2】:

使用这些变通方法进行平滑滚动

a) recyclerView.setHasFixedSize(true);
b) recyclerView.setItemViewCacheSize(20);
recyclerView.setDrawingCacheEnabled(true);
recyclerView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);

c) 在onBindView() 它将提供内存管理缓存以减少图像加载时间

Glide.with(mCtx).load(games.getAwayTeamLogo()).
                        .diskCacheStrategy(DiskCacheStrategy.ALL)
                        .apply(options)
                        .into(gamesTodayViewHolder.imageViewAwayTeamLogo);

【讨论】:

    【解决方案3】:

    不要使用ConstraintLayout,使用LinearLayout 和/或RelativeLayout

    试一试,看看性能变化。

    【讨论】:

    • 线性布局是在回收器视图中使用的错误解决方案,线性布局从子视图执行视图到父视图,因此首先它必须执行子视图然后执行其父视图,这是耗时和处理工作。请阅读约束布局文档。推荐使用,但也不推荐嵌套约束布局
    【解决方案4】:

    我认为 bindViewHolder 中的逻辑让它变慢了,因为在滚动时会为每个即将显示的项目调用 bindViewHolder。

    另外,你可以合并逻辑

    if (games.getStatus().equals("CLOSED")) {
    
       gamesTodayViewHolder.imageViewEmptyCircle.setImageResource(R.drawable.ft);
                }
    if (games.getStatus().equals("ACTIVE")) {
                    gamesTodayViewHolder.imageViewEmptyCircle.setImageResource(R.drawable.active_icon);
                }
    

    与下一个开关盒...

    另外,不要在 bindview 中设置点击监听器,它不好...你可以在那个持有者类中设置

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-03-23
      • 1970-01-01
      • 1970-01-01
      • 2021-04-11
      • 1970-01-01
      • 1970-01-01
      • 2017-06-23
      相关资源
      最近更新 更多