zoukankan      html  css  js  c++  java
  • Android ListView 全面优化

    结合昨天学习的多线程,今天又继续对ListView进行了优化,包括异步加载图片,滑动时暂停加载,滑动停止后再加载显示界面中的item。

    综合ListView在使用时参考的多篇博客,这里对ListView的使用进行全面的优化总结。

    1. ListView 主界面

    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

    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() + "/APP/";
            }
        }
    
        @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异步加载图片

    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);
    
        }
    }
  • 相关阅读:
    16 js动态添加样式
    15 document对象
    89 多线程(十...)——线程池
    14 window对象
    13 js事件2——选择合适的事件
    12 js事件
    11 js的常用类和方法
    10 js自定义对象
    09 js自定义类与prototype关键字
    88 maven配置库,dom4j
  • 原文地址:https://www.cnblogs.com/fansen/p/5213625.html
Copyright © 2011-2022 走看看