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的動畫以及分割線的格式。這兩個屬性,我們下回再說。

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