You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ListView加载本地Drawable滚动卡顿,已做优化求后续解决方案

ListView滚动卡顿优化方案

你已经通过裁剪图片、使用ViewHolder、禁用缓存、改用ConstraintLayout做了不少性能优化,但滚动仍有卡顿,下面这些方案可以进一步尝试:

一、优化图片加载与处理

  • 使用图片加载库异步加载:哪怕是本地drawable资源,Glide或Picasso这类库也能自动处理图片解码、内存缓存和异步加载,避免主线程阻塞。示例代码:
    Glide.with(getContext())
         .load(currentLocation.getImageResourceId())
         .into(viewHolder.image);
    
  • 为多分辨率设备提供适配资源:在drawable-mdpi、drawable-hdpi、drawable-xhdpi等目录下放对应尺寸的图片,避免系统运行时动态缩放图片,减少计算开销。
  • 手动采样解码图片:如果图片原始尺寸远大于ImageView显示尺寸,可通过采样降低内存占用:
    private Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) {
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(res, resId, options);
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeResource(res, resId, options);
    }
    
    private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;
        if (height > reqHeight || width > reqWidth) {
            final int halfHeight = height / 2;
            final int halfWidth = width / 2;
            while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) {
                inSampleSize *= 2;
            }
        }
        return inSampleSize;
    }
    
    在getView中调用该方法获取Bitmap后设置给ImageView即可。

二、优化ListView配置

  • 开启平滑滚动条:给ListView添加android:smoothScrollbar="true",让滚动条绘制更顺滑,减少不必要的重绘。
  • 确认硬件加速开启:在对应Activity的AndroidManifest.xml中添加android:hardwareAccelerated="true"(默认通常开启,但可确认),利用GPU加速绘制提升流畅度。
  • 避免getView中做耗时操作:所有数据处理提前在后台线程完成,比如Location对象的属性提前准备好,不要在滚动时才计算。

三、精简布局层级

  • 去除冗余嵌套:你当前列表项外层的LinearLayout可以去掉,直接用ConstraintLayout作为根布局,减少布局层级。修改后的列表项XML:
    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout 
        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">
    
        <TextView 
            android:id="@+id/name_text_view"
            android:layout_width="0dp"
            android:layout_height="22dp"
            android:layout_marginBottom="8dp"
            android:layout_marginEnd="8dp"
            android:layout_marginStart="8dp"
            android:textSize="18sp"
            android:textStyle="bold"
            android:textColor="@android:color/white"
            app:layout_constraintBottom_toBottomOf="@+id/image_view"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            tools:text="Oleviste church"
            android:elevation="2dp" />
    
        <ImageView 
            android:id="@+id/image_view"
            android:layout_width="match_parent"
            android:layout_height="160dp"
            android:layout_marginEnd="8dp"
            android:layout_marginStart="8dp"
            android:layout_marginTop="8dp"
            android:contentDescription="@string/image_content_description"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
        <TextView 
            android:id="@+id/description_text_view"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginEnd="8dp"
            android:layout_marginStart="8dp"
            android:layout_marginTop="8dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/image_view"
            tools:text="This modern museum is located inside a former seaplane hangar and boasts a submarine and an ice breaker among its exhibits." />
    </android.support.constraint.ConstraintLayout>
    
  • 用ViewStub懒加载非必要视图:如果列表项中有不是每次都显示的视图,用ViewStub延迟加载,降低初始化布局的开销。

四、性能监控与内存优化

  • 用Android Profiler定位卡顿点:通过Android Studio的Profiler查看滚动时的CPU、内存、GPU使用情况,比如是否有频繁GC,或者某个方法占用过多CPU时间。
  • 排查内存泄漏:确保ViewHolder没有持有Activity强引用,检查Context使用是否合理,防止内存泄漏导致的性能下降。

附:你提供的相关代码

LocationAdapter 代码

public class LocationAdapter extends ArrayAdapter<Location> { 
    public LocationAdapter(@NonNull Context context, ArrayList<Location> locationArrayList) { 
        super(context, 0, locationArrayList); 
    } 

    static class ViewHolder { 
        public TextView nameText; 
        public TextView descriptionText; 
        public ImageView image; 
    } 

    @NonNull 
    @Override 
    public View getView(int position, @Nullable View listItemView, @NonNull ViewGroup parent) { 
        ViewHolder viewHolder; 
        // check if the existing view is being reused 
        if (listItemView == null) { 
            // if not, inflate the view using location_list_itemlist_item.xml 
            listItemView = LayoutInflater.from(getContext()).inflate(R.layout.location_list_item, parent, false); 
            // configure view holder 
            viewHolder = new ViewHolder(); 
            viewHolder.nameText = listItemView.findViewById(R.id.name_text_view); 
            viewHolder.image = listItemView.findViewById(R.id.image_view); 
            viewHolder.descriptionText = listItemView.findViewById(R.id.description_text_view); 
            listItemView.setTag(viewHolder); 
        } else { 
            viewHolder = (ViewHolder) listItemView.getTag(); 
        } 
        // getting Location object in this position in ArrayList 
        Location currentLocation = getItem(position); 
        // using ViewHolder to set text/images to views 
        viewHolder.nameText.setText(currentLocation.getName()); 
        viewHolder.descriptionText.setText(currentLocation.getDescription()); 
        viewHolder.image.setImageResource(currentLocation.getImageResourceId()); 
        return listItemView; 
    } 
}

列表项布局(location_list_item.xml)

<?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:id="@+id/linearLayout" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:orientation="vertical"> 

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

        <TextView 
            android:id="@+id/name_text_view" 
            android:layout_width="368dp" 
            android:layout_height="22dp" 
            android:layout_marginBottom="8dp" 
            android:layout_marginEnd="8dp" 
            android:layout_marginStart="8dp" 
            android:textSize="18sp" 
            android:textStyle="bold" 
            android:textColor="@android:color/white" 
            app:layout_constraintBottom_toBottomOf="@+id/image_view" 
            app:layout_constraintEnd_toEndOf="parent" 
            app:layout_constraintHorizontal_bias="0.0" 
            app:layout_constraintStart_toStartOf="parent" 
            tools:text="Oleviste church" 
            android:elevation="2dp" /> 

        <ImageView 
            android:id="@+id/image_view" 
            android:layout_width="match_parent" 
            android:layout_height="160dp" 
            android:layout_marginEnd="8dp" 
            android:layout_marginStart="8dp" 
            android:layout_marginTop="8dp" 
            android:contentDescription="@string/image_content_description" 
            app:layout_constraintEnd_toEndOf="parent" 
            app:layout_constraintStart_toStartOf="parent" 
            app:layout_constraintTop_toTopOf="parent" /> 

        <TextView 
            android:id="@+id/description_text_view" 
            android:layout_width="0dp" 
            android:layout_height="51dp" 
            android:layout_marginEnd="8dp" 
            android:layout_marginStart="8dp" 
            app:layout_constraintEnd_toEndOf="parent" 
            app:layout_constraintStart_toStartOf="parent" 
            app:layout_constraintTop_toBottomOf="@+id/image_view" 
            tools:text="This modern museum is located inside a former seaplane hangar and boasts a submarine and an ice breaker among its exhibits." /> 

    </android.support.constraint.ConstraintLayout> 

</LinearLayout>

ListView布局

<?xml version="1.0" encoding="utf-8"?> 
<ListView xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@+id/list_view" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:overScrollMode="never" 
    android:scrollingCache="false" 
    android:animationCache="false"/>

内容的提问来源于stack exchange,提问作者Igor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:11:24