gpt4 book ai didi

android - 快速上下滚动 ListView 会更改 ListView 项的顺序

转载 作者:行者123 更新时间:2023-11-29 17:49:15 25 4
gpt4 key购买 nike

我有一个带有自定义 ArrayAdapter 和自定义项的 ListView。当我在创建、恢复或使用 getView() 方法缓慢滚动时填充这些项目时,一切都很好。

但是当我快速上下滚动时,ListView 中的项目顺序会发生变化。

只有当我在 getView() 方法中使用“正确”的方式处理自定义 ArrayAdapter 时才会发生这种情况:

@Override
public View getView(int position, View convertView, ViewGroup parent){
View view = convertView;
MyViewHolder myViewHolder = null;
if(view == null){
view = inflater.inflate(layoutResourceId, parent, false);

// Get the View Elements

// Create a new ViewHolder and save the View-Elements in it
myViewHolder = new MyViewHolder(TextView txtView1, TextView txtView2, ...);

view.setTag(myViewHolder);
}
else
myViewHolder = (MyViewHolder) view.getTag();

// Do something with the Views like changing a text
myViewHolder.myTextView.setText("some text");
}

我确实在以下两个链接中找到了针对此快速滚动错误的解决方案:

这个解决方案是在getView()方法中每次进来都会给View充气并创建一个新的ViewHolder:

@Override
public View getView(int position, View convertView, ViewGroup parent){
View view = inflater.inflate(layoutResourceId, parent, false);

// Get the View Elements

view.setTag(new MyViewHolder(TextView txtView1, TextView txtView2, ...));

myViewHolder = (MyViewHolder) view.getTag();

// Do something with the Views like changing a text
myViewHolder.myTextView.setText("some text");
}

这确实解决了滚动错误,但它产生了一个新问题:当我快速上下滚动时,我遇到内存不足运行时错误和内存泄漏。这是 Eclipse 的 Logcat 中的信息:

06-17 07:15:30.728: I/dalvikvm-heap(2920): Forcing collection of SoftReferences for 111376-byte allocation
06-17 07:15:30.908: I/dalvikvm-heap(2920): Clamp target GC heap from 17.614MB to 16.000MB
06-17 07:15:30.908: D/dalvikvm(2920): GC_BEFORE_OOM freed 72K, 4% free 15843K/16360K, paused 180ms, total 181ms
06-17 07:15:30.908: E/dalvikvm-heap(2920): Out of memory on a 111376-byte allocation.
06-17 07:15:30.918: I/dalvikvm(2920): "main" prio=5 tid=1 RUNNABLE
06-17 07:15:30.918: I/dalvikvm(2920): | group="main" sCount=0 dsCount=0 obj=0xb4a9cca8 self=0xb730e398
06-17 07:15:30.918: I/dalvikvm(2920): | sysTid=2920 nice=0 sched=0/0 cgrp=apps handle=-1225232044
06-17 07:15:30.918: I/dalvikvm(2920): | state=R schedstat=( 20550000000 16710000000 17134 ) utm=1825 stm=230 core=0
06-17 07:15:30.918: I/dalvikvm(2920): at android.graphics.Bitmap.nativeCreate(Native Method)
06-17 07:15:30.918: I/dalvikvm(2920): at android.graphics.Bitmap.createBitmap(Bitmap.java:809)
06-17 07:15:30.928: I/dalvikvm(2920): at android.graphics.Bitmap.createBitmap(Bitmap.java:769)
06-17 07:15:30.928: I/dalvikvm(2920): at android.view.View.buildDrawingCache(View.java:13608)
06-17 07:15:30.928: I/dalvikvm(2920): at android.view.View.getDrawingCache(View.java:13463)
06-17 07:15:30.938: I/dalvikvm(2920): at android.view.View.draw(View.java:14156)
06-17 07:15:30.938: I/dalvikvm(2920): at android.view.ViewGroup.drawChild(ViewGroup.java:3103)
06-17 07:15:30.938: I/dalvikvm(2920): at android.widget.ListView.drawChild(ListView.java:3363)
06-17 07:15:30.948: I/dalvikvm(2920): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2940)
06-17 07:15:30.948: I/dalvikvm(2920): at android.widget.AbsListView.dispatchDraw(AbsListView.java:2458)
06-17 07:15:30.948: I/dalvikvm(2920): at android.widget.ListView.dispatchDraw(ListView.java:3358)
06-17 07:15:30.948: I/dalvikvm(2920): at android.view.View.draw(View.java:14468)
06-17 07:15:30.948: I/dalvikvm(2920): at android.widget.AbsListView.draw(AbsListView.java:3817)
06-17 07:15:30.958: I/dalvikvm(2920): at android.view.View.draw(View.java:14350)
06-17 07:15:30.958: I/dalvikvm(2920): at android.view.ViewGroup.drawChild(ViewGroup.java:3103)
06-17 07:15:30.958: I/dalvikvm(2920): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2940)
06-17 07:15:30.958: I/dalvikvm(2920): at android.view.View.draw(View.java:14348)
06-17 07:15:30.958: I/dalvikvm(2920): at android.view.ViewGroup.drawChild(ViewGroup.java:3103)
06-17 07:15:30.958: I/dalvikvm(2920): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2940)
06-17 07:15:30.958: I/dalvikvm(2920): at android.view.View.draw(View.java:14348)
06-17 07:15:30.958: I/dalvikvm(2920): at android.view.ViewGroup.drawChild(ViewGroup.java:3103)
06-17 07:15:30.958: I/dalvikvm(2920): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2940)
06-17 07:15:30.958: I/dalvikvm(2920): at android.view.View.draw(View.java:14468)
06-17 07:15:30.968: I/dalvikvm(2920): at com.android.internal.widget.ActionBarOverlayLayout.draw(ActionBarOverlayLayout.java:381)
06-17 07:15:30.968: I/dalvikvm(2920): at android.view.View.draw(View.java:14350)
06-17 07:15:30.968: I/dalvikvm(2920): at android.view.ViewGroup.drawChild(ViewGroup.java:3103)
06-17 07:15:30.968: I/dalvikvm(2920): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2940)
06-17 07:15:30.968: I/dalvikvm(2920): at android.view.View.draw(View.java:14468)
06-17 07:15:30.968: I/dalvikvm(2920): at android.widget.FrameLayout.draw(FrameLayout.java:472)
06-17 07:15:30.968: I/dalvikvm(2920): at com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java:2326)
06-17 07:15:30.968: I/dalvikvm(2920): at android.view.ViewRootImpl.drawSoftware(ViewRootImpl.java:2496)
06-17 07:15:30.968: I/dalvikvm(2920): at android.view.ViewRootImpl.draw(ViewRootImpl.java:2409)
06-17 07:15:30.968: I/dalvikvm(2920): at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2253)
06-17 07:15:30.968: I/dalvikvm(2920): at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1883)
06-17 07:15:30.968: I/dalvikvm(2920): at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1000)
06-17 07:15:30.968: I/dalvikvm(2920): at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5670)
06-17 07:15:30.968: I/dalvikvm(2920): at android.view.Choreographer$CallbackRecord.run(Choreographer.java:761)
06-17 07:15:30.968: I/dalvikvm(2920): at android.view.Choreographer.doCallbacks(Choreographer.java:574)
06-17 07:15:30.968: I/dalvikvm(2920): at android.view.Choreographer.doFrame(Choreographer.java:544)
06-17 07:15:30.968: I/dalvikvm(2920): at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:747)
06-17 07:15:30.968: I/dalvikvm(2920): at android.os.Handler.handleCallback(Handler.java:733)
06-17 07:15:30.968: I/dalvikvm(2920): at android.os.Handler.dispatchMessage(Handler.java:95)
06-17 07:15:30.968: I/dalvikvm(2920): at android.os.Looper.loop(Looper.java:136)
06-17 07:15:30.968: I/dalvikvm(2920): at android.app.ActivityThread.main(ActivityThread.java:5017)
06-17 07:15:30.968: I/dalvikvm(2920): at java.lang.reflect.Method.invokeNative(Native Method)
06-17 07:15:30.968: I/dalvikvm(2920): at java.lang.reflect.Method.invoke(Method.java:515)
06-17 07:15:30.968: I/dalvikvm(2920): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
06-17 07:15:30.968: I/dalvikvm(2920): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
06-17 07:15:30.968: I/dalvikvm(2920): at dalvik.system.NativeStart.main(Native Method)
06-17 07:15:30.998: I/Choreographer(2920): Skipped 294 frames! The application may be doing too much work on its main thread.

我上下滚动了大约 10 秒,在我的 Logcat 中得到了上面的整个 Stack-Trace,超过 50 次..

有谁知道解决快速滚动错误的解决方案,而不是像第二个代码那样每次都创建一个新 View ?

如果不是,什么更可取?忽略这个错误并让它改变顺序,这可能会导致用户提示或者修复这个错误但有内存泄漏和应用程序崩溃的风险?


解决方案:

根据使用缓存的建议,我首先尝试了 Vigneshwaran Thenraj 的 LazyList 建议。但是,我忘了说我只使用 /res/drawable 文件夹中的图片,不使用网络中的图片。因此,我使用了一个非常简单的自己创建的缓存。因为每次我重新膨胀 View 并重新创建 ViewHolder 时,当我非常快速地上下滚动时,它在模拟器上仍然有点慢,但在真实设备上却不是。

所以,我用来缓存图片的解决方案是:

// ImageLoader class to cache the Images from the Drawable folder
public class ImageLoader
{
// Logcat tag
private static final String TAG = "ImageLoader";

private Context context;

// The HashMap where we save the cached Bitmaps for the ImageViews
// TODO: Find a way to use a synchronized SparseArray to fix the warning below
private Map<Integer, Bitmap> cachedBitmaps = Collections.synchronizedMap(new HashMap<Integer, Bitmap>());

// Default Checkbox Image
private static final int DEFAULT_CHECKBOX = D.CHECKBOX_IMAGES[0];

public ImageLoader(Context c){
context = c;
// Add the default Resource-ID to the WeakHashMap
Bitmap defaultBitmap = BitmapFactory.decodeResource(context.getResources(), DEFAULT_CHECKBOX);
if(defaultBitmap != null){
cachedBitmaps.put(DEFAULT_CHECKBOX, defaultBitmap);
if(D.SHOW_INFO_LOGS)
L.Log(TAG, "Default Image added to the list: " + context.getResources().getResourceEntryName(DEFAULT_CHECKBOX), LogType.INFO);
}
else
L.Log(TAG, "The Default Resource-ID for the Drawable-Image is incorrect and we can't retrieve the Bitmap with the BitmapFactory!", LogType.ERROR);
}

// The method to save the Resource-ID key and it's current Bitmap value in the HashMap
// And also to retrieve the Bitmap with the given Resource-ID
public void imageForImageView(int id, ImageView iv){
// Get the Bitmap from the HashMap (or null if it doesn't exist yet)
Bitmap bitmap = cachedBitmaps.get(id);

// Does this Bitmap already exist in the HashMap?
if(bitmap != null)
// Then assign it to the ImageView
iv.setImageBitmap(bitmap);
// If it doesn't exists yet:
else{
// Create it locally using the BitmapFactory
bitmap = BitmapFactory.decodeResource(context.getResources(), id);
// Does the Drawable with this given Resource-ID exist?
if(bitmap != null){
// Put it in the HashMap
cachedBitmaps.put(id, bitmap);
// and assign it to the ImageView
iv.setImageBitmap(bitmap);
L.Log(TAG, "New Image added to the list: " + context.getResources().getResourceEntryName(id), LogType.INFO);
}
// If it doesn't exist:
else
// Use the default Resource-ID instead
iv.setImageBitmap(cachedBitmaps.get(DEFAULT_CHECKBOX));
}
}
}

将此 ImageLoader 添加到我的 MainActivity:

private static ImageLoader imageLoader;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

... // Do more stuff

imageLoader = new ImageLoader();
}

public static ImageLoader getImageLoader(){
return imageLoader;
}

并在我的 getView() 方法和我更改这些图像的其他地方使用此 ImageLoader:

MainActivity.getImageLoader().imageForImageView(D.CHECKBOX_IMAGES[currentItem.getCheckState()], imageView);

现在我不再遇到 Logcat 内存泄漏/应用程序崩溃了。

PS:我使用默认类来保存图像资源 ID:

// Class for all the Default values
public class D
{
... // Other defaults

public static final Integer[] CHECKBOX_IMAGES = {
R.drawable.checkbox_unchecked,
R.drawable.checkbox_checked,
R.drawable.checkbox_error,
R.drawable.checkbox_partly
};
}

PSS:还有一个 Logger 类:

import android.util.Log;

// Logger class to show Logcat-messages
public class L
{
// Show normal Logs based on the given LogType
public static void Log(String TAG, String message, LogType type){
if(D.SHOW_LOGS){
switch(type){
case INFO:
Log.i(TAG, message);
break;
case WARNING:
Log.w(TAG, message);
break;
case ERROR:
Log.e(TAG, message);
break;
}
}
}

// Show Exception Error Logs
public static void LogException(String TAG, String message, Throwable ex){
if(D.SHOW_LOGS)
Log.e(TAG, message, ex);
}
}

将 LogType 作为枚举,并提供三种可用的 LogType。

最佳答案

如果您在 viewholder 中有图像,最好将它们添加到缓存中,这样性能会更高并且只获取图像一次。这是您应该为每一行创建一个新的查看器的正确方法

关于android - 快速上下滚动 ListView 会更改 ListView 项的顺序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24264241/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com