如何在ArrayAdapter中用AsyncTask加载图片解决ListView滚动卡顿
Fixing ListView Scrolling Lag with AsyncTask for Image Loading
Hey there! Let's get that ListView scrolling smoothly again. The lag you're seeing comes from two main issues in your current adapter code:
- You're inflating a new layout every time
getView()is called (no view recycling) - You're loading images synchronously on the main UI thread, which blocks rendering during scrolls
Here's a refactored version of your adapter that uses the ViewHolder pattern for view reuse and AsyncTask to load images in the background:
Modified MainActivity.java (Focus on the Adapter)
package com.emmakade.okt; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.graphics.drawable.Drawable; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.os.AsyncTask; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import java.util.List; public class MainActivity extends AppCompatActivity { protected List<Course> data; private static final int DETAIL_REQUEST_CODE = 1; // Added since your onActivityResult references this @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); data = DataProvider.getData(); ArrayAdapter<Course> courseArrayAdapter = new CourseArrayAdapter(this, 0, data); ListView listView = (ListView) findViewById(android.R.id.list); listView.setAdapter(courseArrayAdapter); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Course course = data.get(position); displayDetail(course); } }); } // Make sure this method is defined (since you call it in onItemClick) private void displayDetail(Course course) { // Your existing detail activity logic here } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); // Don't forget this line if (requestCode == DETAIL_REQUEST_CODE) { if (resultCode == RESULT_OK) { String msg = data.getStringExtra("resultMessage"); Toast.makeText(this, msg, Toast.LENGTH_LONG).show(); } } } class CourseArrayAdapter extends ArrayAdapter<Course>{ Context context; List<Course> objects; public CourseArrayAdapter(Context context, int resource, List<Course> objects) { super(context, resource, objects); this.context = context; this.objects = objects; } // ViewHolder class to cache views for reuse static class ViewHolder { TextView titleTextView; ImageView courseImageView; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; Course course = objects.get(position); // Reuse existing view if available if (convertView == null) { LayoutInflater inflater = (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE); convertView = inflater.inflate(R.layout.course_item, parent, false); // Initialize ViewHolder and cache views holder = new ViewHolder(); holder.titleTextView = (TextView) convertView.findViewById(R.id.tvTitle); holder.courseImageView = (ImageView) convertView.findViewById(R.id.imageCourse); convertView.setTag(holder); } else { // Get existing ViewHolder from the reused view holder = (ViewHolder) convertView.getTag(); } // Set the course title immediately (this is fast) holder.titleTextView.setText(course.getTitle()); // Cancel any ongoing image load task for this view to prevent image misalignment ImageLoadTask existingTask = (ImageLoadTask) holder.courseImageView.getTag(); if (existingTask != null) { existingTask.cancel(true); } // Start a new AsyncTask to load the image in the background ImageLoadTask task = new ImageLoadTask(holder.courseImageView, course.getCourseNumber()); holder.courseImageView.setTag(task); task.execute(); return convertView; } // AsyncTask to load images in background thread private class ImageLoadTask extends AsyncTask<Void, Void, Drawable> { private ImageView imageView; private String courseNumber; public ImageLoadTask(ImageView imageView, String courseNumber) { this.imageView = imageView; this.courseNumber = courseNumber; } @Override protected Drawable doInBackground(Void... voids) { // Load the drawable in background thread int resId = context.getResources().getIdentifier( "image_" + courseNumber, "drawable", context.getPackageName() ); return context.getResources().getDrawable(resId); } @Override protected void onPostExecute(Drawable drawable) { // Only update the ImageView if the task wasn't cancelled and the view is still valid if (!isCancelled() && imageView != null && drawable != null) { imageView.setImageDrawable(drawable); } } } } }
Key Improvements Explained:
ViewHolder Pattern:
- Caches the
TextViewandImageViewfor each list item, so we avoid repeatedfindViewById()calls (a major source of UI thread overhead) - Reuses existing
convertViewinstead of inflating new layouts for every item, cutting down on memory usage and layout processing time
- Caches the
AsyncTask for Image Loading:
- Moves drawable loading to a background thread (
doInBackground()), so the main UI thread stays free to handle scrolling and rendering - Cancels ongoing tasks when a view is reused, preventing old images from appearing in the wrong list items
- Only updates the ImageView if the task completed successfully and wasn't cancelled, ensuring consistent UI state
- Moves drawable loading to a background thread (
Notes:
- Your layout files (
activity_main.xmlandcourse_item.xml) don't need any changes - I added a missing
DETAIL_REQUEST_CODEconstant anddisplayDetail()method since your original code references them but didn't define them - For even better performance, you could add an LRU image cache to avoid reloading the same drawable multiple times as the list scrolls
内容的提问来源于stack exchange,提问作者kayode emmanuel
相关产品推荐
相关产品推荐

