Android ListView与RecyclerView

Android中,最复杂的原生控件就是ListView,在support-v7中,Google提供了一种功能更加丰富的控件来代替ListView,那就是RecyclerView,本篇文章,就来解析一下ListView与RecyclerView。

使用ListView控件需要为设置一个Adapter:

public class listadapter extends BaseAdapter {

    private Context context;

    private List<listitem> list;


    class ViewHolder {
        ImageView imageView;
        TextView textView;
    }

    public listadapter(Context context, List<listitem> list) {
        this.context = context;
        this.list = list;
    }

    public listitem getItem(int position) {
        return list.get(position);
    }

    public long getItemId(int postion) {
        return postion;
    }

    public int getCount() {
        return list.size();
    }

    public View getView(int position, View convertview, ViewGroup parent) {
        listitem listitem = getItem(position);
        
        View view;
        ViewHolder viewHolder;
        if(convertview == null) {
            int id = getItemViewType(position);
            viewHolder = new ViewHolder();
            if(id == 0) {
                view = LayoutInflater.from(context).inflate(R.layout.listitem_layout0, parent, false);
                viewHolder.imageView = (ImageView) view.findViewById(R.id.image0);
                viewHolder.textView = (TextView) view.findViewById(R.id.name0);
                view.setTag(viewHolder);
            } else {
                view = LayoutInflater.from(context).inflate(R.layout.listitem_layout1, parent, false);
                viewHolder.imageView = (ImageView) view.findViewById(R.id.image1);
                viewHolder.textView = (TextView) view.findViewById(R.id.name1);
                view.setTag(viewHolder);
            }
        } else {
            view = convertview;
            viewHolder = (ViewHolder) view.getTag();
        }
        viewHolder.imageView.setImageResource(listitem.getImageId());
        viewHolder.textView.setText(listitem.getName());
        return view;
    }

    @Override
    public int getItemViewType(int position) {
        if(position < 6) return 0;
        else return 1;
    }

    @Override
    public int getViewTypeCount() {
        return 2;
    }

}

上面的代码中,Adapter继承自BaseAdapter, 复写了六种方法。其中

public listitem getItem(int position) {
    return list.get(position);
}

public long getItemId(int postion) {
    return postion;
}

public int getCount() {
    return list.size();
}

public View getView(int position, View convertview, ViewGroup parent) {

}


四种方法最为基本(注意getItemId方法的返回值类型为long),getView在每次获取View时都会被调用,里面复写的逻辑尽可能简单。

此外,使用convertview与ViewHolder来对ListView进行优化。

@Override
public int getItemViewType(int position) {
    if(position < 6) return 0;
    else return 1;
}

@Override
public int getViewTypeCount() {
    return 2;
}
复写以上两种方法,可以实现ListView的多种布局。getViewTypeCount方法返回布局类型的数目,getItemViewType方法返回布局类型的序号,范围为0~n-1(n为布局类型的数目)。


ListView还可以直接与Android自带的轻量级数据库sqlite相关联。


相比于ListView,RecyclerView除了需要设置Adapter,还需要设置LayoutManager。

class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.MyViewHolder>
{

    private Set<BitmapWorkerTask> taskCollection;

    //private List<square> squareList = new ArrayList<>();

    private LruCache<String, Bitmap> bitmapLruCache;

    private RecyclerView recyclerView;

    private StaggeredGridLayoutManager layoutManager;

    private int firstVisibleItem, lastVisibleItem;

    private boolean isFirstEnter = true;

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
    {
        MyViewHolder holder = new MyViewHolder(LayoutInflater.from(
                parent.getContext()).inflate(R.layout.squareitem, parent,
                false));
        return holder;
    }

    @Override
    public void onBindViewHolder(MyViewHolder holder, int position)
    {
        final String url = image.imageThumbUrls[position];
        ImageView bg = holder.bg;
        bg.setTag(url);
        Bitmap bitmap = getBitmapFromMemoryCache(url);
        if (bitmap != null) {
            bg.setImageBitmap(bitmap);
        } else {
            bg.setImageResource(R.color.black_overlay);
        }
    }

    @Override
    public int getItemCount()
    {
        return image.imageThumbUrls.length;
    }

    class MyViewHolder extends RecyclerView.ViewHolder
    {

        ImageView bg;

        public MyViewHolder(View view)
        {
            super(view);
            bg = (ImageView) view.findViewById(R.id.bgimage);
        }
    }

    public RecyclerViewAdapter(RecyclerView recyclerView) {
        //this.squareList = squareList;
        this.recyclerView = recyclerView;
        layoutManager = (StaggeredGridLayoutManager)recyclerView.getLayoutManager();
        taskCollection = new HashSet<>();
        int maxMemory = (int) Runtime.getRuntime().maxMemory();
        int cacheSize = maxMemory / 8;
        bitmapLruCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key,  Bitmap bitmap) {
                return bitmap.getByteCount();
            }
        };
        System.out.println("CacheSize : " + cacheSize);
        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                if (newState == SCROLL_STATE_IDLE) {
                    loadBitmaps(firstVisibleItem, lastVisibleItem);
                } else {
                    cancelAllTasks();
                }
            }

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                firstVisibleItem =  layoutManager.findFirstVisibleItemPositions(null)[0];
                lastVisibleItem = layoutManager.findLastVisibleItemPositions(null)[1];

                System.out.println("firstvisibleitem : " + firstVisibleItem);
                System.out.println("lastvisibleitem : " + lastVisibleItem);
                if (isFirstEnter && lastVisibleItem - firstVisibleItem > 0) {
                    loadBitmaps(firstVisibleItem, lastVisibleItem);
                    isFirstEnter = false;
                }
            }
        });
    }

    private void loadBitmaps(int firstVisibleItem, int lastVisibleItem) {
        try {
            for (int i = firstVisibleItem; i <= lastVisibleItem; i ++) {
                String imageUrl = image.imageThumbUrls[i];
                Bitmap bitmap = getBitmapFromMemoryCache(imageUrl);
                if (bitmap == null) {
                    BitmapWorkerTask task = new BitmapWorkerTask();
                    taskCollection.add(task);
                    task.execute(imageUrl);
                    System.out.println("task is executing");
                } else {
                    ImageView imageView = (ImageView) recyclerView.findViewWithTag(imageUrl);
                    if (imageView != null && bitmap != null) {
                        imageView.setImageBitmap(bitmap);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("task failed");
        }
    }

    public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
        if (getBitmapFromMemoryCache(key) == null) {
            bitmapLruCache.put(key, bitmap);
        }
    }

    public Bitmap getBitmapFromMemoryCache(String key) {
        return bitmapLruCache.get(key);
    }

    public void cancelAllTasks() {
        if (taskCollection != null) {
            for (BitmapWorkerTask task : taskCollection) {
                task.cancel(false);
            }
        }
    }

    class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {

        private String imageUrl;

        @Override
        protected Bitmap doInBackground(String... params) {
            imageUrl = params[0];
            Bitmap bitmap = downloadBitmap(params[0]);
            if (bitmap != null) {
                addBitmapToMemoryCache(params[0], bitmap);
            }
            return bitmap;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            ImageView imageView  = (ImageView) recyclerView.findViewWithTag(imageUrl);
            if(imageView != null && bitmap != null) {
                imageView.setImageBitmap(bitmap);
            }
            taskCollection.remove(this);
        }

        private Bitmap downloadBitmap(String imageUrl) {
            Bitmap bitmap = null;
            HttpURLConnection con = null;
            try {
                URL url = new URL(imageUrl);
                con = (HttpURLConnection) url.openConnection();
                con.setConnectTimeout(5000);
                con.setReadTimeout(10000);
                bitmap = BitmapFactory.decodeStream(con.getInputStream());
            } catch (Exception e) {
                e.printStackTrace();;
            } finally {
                if (con != null) {
                    con.disconnect();
                }
            }
            return bitmap;
        }

    }

}
上面的代码中使用了LRUCache对图片进行缓存,同时对RecyclerView进行了滑动监听,仅在静止时加载图片。

RecyclerView使用的Adapter继承了RecyclerView.Adapter<T>,泛型T为RecyclerView.ViewHolder类型,需要在Adapter中自己设置,继承RecyclerView.ViewHolder,重写构造函数。其他需要重写的三个函数:

@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType)

@Override
public void onBindViewHolder(MyViewHolder holder, int position)

@Override
public int getItemCount()

onCreateViewHolder方法返回ViewHolder。
在onBindViewHolder方法中设置Item的View,相当于ListView中的getView方法,不过onBinderViewHolder无需返回View。
getItemCount方法返回Item的数量,相当于ListView中的getCount方法。

LayoutManager表示RecyclerView的布局格式。不同于ListView的线性的颇显单调的布局,RecyclerView提供了LinearLayourManager(线性布局),GridLayoutManager(方格布局)
和StaggeredGridLayoutManager(瀑布流布局)三种布局格式。一次性解决了ListView和GridView,还提供了瀑布流的布局格式。
此外,还可以同过setItemAnimatoraddItemDecoration方法来设置RecyclerView的增删Item的动画以及分割线的格式。这两个属性,我们下回再说。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章