Android ListView 全面优化
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android ListView 全面优化相关的知识,希望对你有一定的参考价值。
结合昨天学习的多线程,今天又继续对ListView进行了优化,包括异步加载图片,滑动时暂停加载,滑动停止后再加载显示界面中的item。
综合ListView在使用时参考的多篇博客,这里对ListView的使用进行全面的优化总结。
1. ListView 主界面
package com.panasonic.imagelight; import com.panasonic.imagelight.adapter.AsyncImageLoader; import com.panasonic.imagelight.adapter.MovieListAdapter; import com.panasonic.imagelight.data.MovieList; import com.panasonic.imagelight.utils.MoviePlayerActivity; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.v4.app.Fragment; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView; import android.widget.ListView; import android.widget.TextView; public class MainListFragment extends Fragment implements OnItemClickListener, AbsListView.OnScrollListener { private final String TAG = this.getClass().getName().toString(); private View view; private TextView noMovieTextView; private ListView movieListView; private MovieListAdapter mAdapter; private AsyncImageLoader asyncImageLoader = new AsyncImageLoader(); int start; int end; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { view = inflater.inflate(R.layout.main_list_fragment, container, false); return view; } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); noMovieTextView = (TextView) view.findViewById(R.id.movie_list_no_movie_tv); movieListView = (ListView) view.findViewById(R.id.movie_list_fragment_lv); if (MovieList.getInstance().getArrayList().size() == 0) { noMovieTextView.setVisibility(View.VISIBLE); } else { setListView(); } } private void setListView() { mAdapter = new MovieListAdapter(getActivity(), movieListHandler, asyncImageLoader, start, end); movieListView.setAdapter(mAdapter); movieListView.setOnItemClickListener(this); movieListView.setOnScrollListener(this); } @Override public void onScrollStateChanged(AbsListView view, int onScroll) { switch (onScroll) { case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL: // 手指触屏拉动准备滚动,只触发一次 顺序: 1 asyncImageLoader.lock(); break; case AbsListView.OnScrollListener.SCROLL_STATE_FLING: // 持续滚动开始,只触发一次 顺序: 2 asyncImageLoader.lock(); break; case AbsListView.OnScrollListener.SCROLL_STATE_IDLE: // 整个滚动事件结束,只触发一次 顺序: 4 asyncImageLoader.unlock(); asyncImageLoader.setStartEnd(movieListView.getFirstVisiblePosition(), movieListView.getLastVisiblePosition()); break; default: break; } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { // 一直在滚动中,多次触发 顺序: 3 } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { String file_path = MovieList.getInstance().getArrayList().get(position).file_path; Intent intent = new Intent(getActivity(), MoviePlayerActivity.class); intent.putExtra("file_path", file_path); startActivity(intent); Log.i(TAG, "onItemClick"); } public Handler movieListHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); refreshListView(); } }; public void refreshListView() { if (mAdapter != null) { mAdapter.notifyDataSetChanged(); } else { noMovieTextView.setVisibility(View.GONE); setListView(); } } @Override public void onDestroyView() { super.onDestroyView(); ((ViewGroup) view.getParent()).removeView(view); } }
2. BaseAdapter
package com.panasonic.imagelight.adapter; import com.panasonic.imagelight.R; import com.panasonic.imagelight.data.MovieList; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.Drawable; import android.os.Environment; import android.os.Handler; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; import java.io.File; public class MovieListAdapter extends BaseAdapter { private Context context; private String record_image_path; private Handler movieListHandler; int start; int end; private AsyncImageLoader asyncImageLoader; public MovieListAdapter(Context context, Handler movieListHandler, AsyncImageLoader asyncImageLoader, int start, int end) { this.context = context; this.movieListHandler = movieListHandler; this.asyncImageLoader = asyncImageLoader; this.start = start; this.end = end; if (record_image_path == null) { record_image_path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Panasonic/"; } } @Override public int getCount() { return MovieList.getInstance().getArrayList().size(); } @Override public Object getItem(int position) { return null; } @Override public long getItemId(int position) { return position; } @Override public View getView(final int position, View convertView, ViewGroup parent) { final ViewHolder holder; if (convertView == null) { convertView = LayoutInflater.from(context).inflate(R.layout.movie_list_item, parent, false); holder = new ViewHolder(); holder.movie_image_iv = (ImageView) convertView.findViewById(R.id.movie_list_item_image_iv); holder.movie_name_tv = (TextView) convertView.findViewById(R.id.movie_list_item_name_tv); holder.total_time_tv = (TextView) convertView.findViewById(R.id.movie_list_item_total_time_tv); holder.movie_size_tv = (TextView) convertView.findViewById(R.id.movie_list_item_size_tv); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } String imagePath = record_image_path + MovieList.getInstance().getArrayList().get(position).movie_name + ".png"; File file = new File(imagePath); if (file.exists()) { // 1. Load Bitmap from SDCard // holder.movie_image_iv.setImageBitmap(BitmapFactory.decodeFile(imagePath)); // 2. Load Bitmap from threadPool holder.movie_image_iv.setTag(position); loadImage(convertView, imagePath, position); } else { holder.movie_image_iv.setImageResource(R.drawable.loading); movieListHandler.sendEmptyMessage(0); } holder.movie_name_tv.setText(MovieList.getInstance().getArrayList().get(position).display_name); int sec = Integer.valueOf(MovieList.getInstance().getArrayList().get(position).total_time) / 1000 % 60; int min = Integer.valueOf(MovieList.getInstance().getArrayList().get(position).total_time) / 1000 / 60; holder.total_time_tv.setText(String.format("%02d:%02d", min, sec)); String movieSize = String.valueOf(Integer.valueOf(MovieList.getInstance().getArrayList().get(position).movie_size) / 1000 / 1000) + context.getResources().getString(R.string.movie_list_fragment_size); holder.movie_size_tv.setText(movieSize); return convertView; } private void loadImage(final View convertView, String imagePath, final int position) { Bitmap cacheImage = asyncImageLoader.loadBitmap(imagePath, position, new AsyncImageLoader.ImageCallback() { @Override public void imageLoaded(Bitmap imageBitmap) { if ((convertView.findViewWithTag(position)) != null) ((ImageView) convertView.findViewWithTag(position)).setImageBitmap(imageBitmap); // ((ImageView) convertView.findViewById(id)).setImageBitmap(imageBitmap); Log.i("test", "1count: " + position); } }); if (cacheImage != null) { ((ImageView) convertView.findViewWithTag(position)).setImageBitmap(cacheImage); // ((ImageView) convertView.findViewById(id)).setImageBitmap(cacheImage); Log.i("test", "2count: " + position); } else { ((ImageView) convertView.findViewWithTag(position)).setImageResource(R.drawable.loading); // ((ImageView) convertView.findViewById(id)).setImageResource(R.drawable.loading); } } private class ViewHolder { public ImageView movie_image_iv; public TextView movie_name_tv; public TextView total_time_tv; public TextView movie_size_tv; } }
3. AsyncImageLoader异步加载图片
package com.panasonic.imagelight.adapter; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Handler; import android.os.SystemClock; import android.util.Log; import java.lang.ref.SoftReference; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * Created by fansen on 2016/02/23. */ public class AsyncImageLoader { // 为了加快速度,在内存中开启缓存(主要应用于重复图片较多时,或者同一个图片要多次被访问,比如在ListView时来回滚动) public Map<String, SoftReference<Bitmap>> imageCache = new HashMap<>(); private ExecutorService executorService = Executors.newFixedThreadPool(5); private Handler handler = new Handler(); private boolean firstLoad = true; private boolean allow = true; private int startLoad; private int endLoad; public void setStartEnd(int start, int end){ startLoad = start; endLoad = end; Log.i("test", "startLoad = " + startLoad); Log.i("test", "endLoad = " + endLoad); } public void lock() { allow = false; firstLoad = false; } public void unlock() { Log.i("test", "allow = true"); allow = true; } /** * @param imagePath 图像路径 * @param callback 回调接口 * @return 返回内存中缓存的图像,第一次加载返回null */ public Bitmap loadBitmap(final String imagePath, final int position, final ImageCallback callback) { // 如果缓存过就从缓存中取出数据 if (imageCache.containsKey(imagePath)) { SoftReference<Bitmap> softReference = imageCache.get(imagePath); if (softReference.get() != null) { Log.i("test", "AsyncImageLoader: ----2----"); return softReference.get(); } } // 缓存中没有图像,则从存储卡中取出数据,并将取出的数据缓存到内存中 executorService.submit(new Runnable() { @Override public void run() { while (!allow){ try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } if (firstLoad || (position >= startLoad && position <= endLoad)) { // 测试时,模拟网络延时,实际时这行代码不能有 SystemClock.sleep(2000); final Bitmap bitmap = BitmapFactory.decodeFile(imagePath); imageCache.put(imagePath, new SoftReference<>(bitmap)); handler.post(new Runnable() { @Override public void run() { Log.i("test", "AsyncImageLoader: ----1----"); callback.imageLoaded(bitmap); } }); } } }); return null; } //对外界开放的回调接口 public interface ImageCallback { //注意 此方法是用来设置目标对象的图像资源 void imageLoaded(Bitmap imageBitmap); } }
以上是关于Android ListView 全面优化的主要内容,如果未能解决你的问题,请参考以下文章
Android App 在片段中创建 ListView 引用时关闭