android Listview repeat data while scroll - android

i have listview with BaseAdapter this is my adapter code
public class MessageAdapter1 extends BaseAdapter {
Context context;
private int auth;
private List<MessageList> mMessages;
private LayoutInflater mInflater;
public MessageAdapter1(Context context, List<MessageList> messages) {
this.mMessages = messages;
this.context = context;
this.session = new SessionManager(context);
mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
auth = session.getUserId();
}
#Override
public int getItemViewType(int position) {
int type = -1;
String m = mMessages.get(position).getType();
int user_idx = mMessages.get(position).getUser_id();
if(auth != user_idx && m.equals("message")) type = 1;
else if(auth == user_idx && m.equals("message")) type = 0;
else if(auth != user_idx && m.equals("image")) type = 3;
else if(auth == user_idx && m.equals("image")) type = 4;
//other else if and types .....
return type;
}
#Override
public int getViewTypeCount() {
return 19;
}
#Override
public int getCount() {
return mMessages.size();
}
#Override
public Object getItem(int i) {
return mMessages.get(i);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
int type = getItemViewType(position);
if (convertView == null) {
holder = new ViewHolder();
switch (type) {
case MessageList.TYPE_MESSAGE_RIGHT:
convertView = mInflater.inflate(R.layout.right_message, null);
holder.groupMessage = (LinearLayout)convertView.findViewById(R.id.messages);
break;
case MessageList.TYPE_MESSAGE_LEFT:
convertView = mInflater.inflate(R.layout.left_message, null);
holder.groupMessage = (LinearLayout)convertView.findViewById(R.id.messages);
break;
case MessageList.TYPE_ACTION:
convertView = mInflater.inflate(R.layout.typing, null);
holder.Indicator = (AVLoadingIndicatorView)convertView.findViewById(R.id.indicator);
break;
//other case break for other types ..
}
convertView.setTag(holder);
} else {
holder = (ViewHolder)convertView.getTag();
}
setMessage(holder,position);
return convertView;
}
private void setMessage(ViewHolder holder, int position){
MessageList m = mMessages.get(position);
//holder.groupMessage.add ... data
}
public static class ViewHolder {
private LinearLayout groupMessage;
private AVLoadingIndicatorView Indicator;
//more here other
}
}
I search in google and in stackoverflow all solution talk about Override
`getViewTypeCount` and `getItemViewType` and other solution not helped me
i see my code its correct but why its repeat or reorder item when scroll on
ListView

Because your item views appear to change based on the type of items in the list, you cannot cache your viewholder.
Try removing your if (convertView == null) { and accompanying else { statement.

Related

java.lang.NullPointerException: Attempt to read from field 'android.widget.TextView

Hello I realize there are multiple people with a similar issue but the reason for me adding this as a new question is because this is an adapter with two holders. I have a listview that has a header cell and the information cell. the error that I am encountering its
java.lang.NullPointerException: Attempt to read from field 'android.widget.TextView.
Which only happens when I scroll the list down and then go back up. I am assuming the holder is getting destroyed and when I call the line to add text the holder is null but I am not sure. Thank you for your help in advance!
Custom Adapter:
private class MyCustomAdapter extends ArrayAdapter<sources> {
private ArrayList<sources> sources_list = new ArrayList<sources>();
private TreeSet<Integer> sectionHeader = new TreeSet<Integer>();
private static final int TYPE_ITEM = 0;
private static final int TYPE_SEPARATOR = 1;
private LayoutInflater vi;
public MyCustomAdapter(Context context, int textViewResourceId, ArrayList<sources> sources_list) {
super(context, textViewResourceId, sources_list);
vi = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public void addItem(final sources item) {
sources_list.add(item);
notifyDataSetChanged();
}
public void addSectionHeaderItem(final sources item) {
sources_list.add(item);
sectionHeader.add(sources_list.size() - 1);
notifyDataSetChanged();
}
public int getItemViewType(int position) {
return sectionHeader.contains(position) ? TYPE_SEPARATOR : TYPE_ITEM;
}
public int getCount() {
return sources_list.size();
}
public int getViewTypeCount() {
return 2;
}
public sources getItem(int position) {
return sources_list.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
public class ViewHolder {
TextView code;
CheckBox name;
}
public class ViewHolder2 {
TextView separator;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
ViewHolder2 holder2 = null;
Log.v("ConvertView", String.valueOf(position));
int rowType = getItemViewType(position);
if (convertView == null) {
switch (rowType) {
case TYPE_ITEM:
convertView = vi.inflate(R.layout.source_cell, null);
holder = new ViewHolder();
holder.code = (TextView)
convertView.findViewById(R.id.code);
holder.name = (CheckBox)
convertView.findViewById(R.id.checkBox1);
break;
case TYPE_SEPARATOR:
holder2 = new ViewHolder2();
convertView = vi.inflate(R.layout.source_header, null);
holder2.separator = (TextView)
convertView.findViewById(R.id.separator);
break;
}
convertView.setTag(holder);
} else {
if (rowType == TYPE_ITEM) {
holder = (ViewHolder)
convertView.getTag(R.layout.source_cell);
} else {
holder2 = (ViewHolder2) convertView.getTag(R.layout.source_header);
}
}
if (rowType == TYPE_ITEM) {
sources n_source = sources_list.get(position);
holder.code.setText(n_source.getCode().toUpperCase());
holder.name.setTag(n_source);
} else {
sources n_source = sources_list.get(position);
holder2.separator.setText(n_source.getCode().toUpperCase());
}
return convertView;
}
}
Your setTag/getTag appears to be wrong. You need to use setTag(r.layout.blah, holder) to match your getTag(r.layout.blah).

is it possible to use three view types in recycler view Adapter?

public class StoreDetailAdapter extends RecyclerView.Adapter<StoreDetailAdapter.MyViewHolder> {
private final int VIEW_TYPE_STORE_HEAD = 0;
private final int VIEW_TYPE_CAT = 1;
private final int VIEW_TYPE_PROD = 2;
private List<Store.ProductType.ProductList> mPopularProducts = new ArrayList<>();
private List<Store.ProductType.ProductList> mPurchasedProducts = new ArrayList<>();
private CategoryListAdapter mCategoryAdapter;
private ProductCollectionAdapter mProductCollectionAdapter;
private Context mContext;
private List<Category> mCategories = new ArrayList<>();
private List<InStore> inStoreList = new ArrayList<>();
public class MyViewHolder extends RecyclerView.ViewHolder {
public TextView categoryName;
public TwoWayView items;
LinearLayout containerHeading;
FrameLayout containerHeaderImage;
ImageView imageView;
private Store mStore;
public MyViewHolder(View view) {
super(view);
items = (TwoWayView) view.findViewById(R.id.items_list);
categoryName = (TextView) view.findViewById(R.id.category_name);
containerHeading = (LinearLayout) view.findViewById(R.id.container_linear_layout_heading);
containerHeaderImage = (FrameLayout) view.findViewById(R.id.container_frame_layout_image);
imageView = (ImageView) view.findViewById(R.id.image_store_icon);
}
}
#Override
public int getItemViewType(int position) {
if(isFromMall){
if(position == 0)
return VIEW_TYPE_STORE_HEAD;
else if(position == 1)
return VIEW_TYPE_CAT;
else if(position == 2)
return VIEW_TYPE_PROD;
} else {
return position==0 ? VIEW_TYPE_CAT :VIEW_TYPE_PROD;
}
return 0;
}
public StoreDetailAdapter(Context context, List<Category> categories, List<Store.ProductType.ProductList> products, Store.ProductType popularType, Store store) {
mContext = context;
mCategoryAdapter = new CategoryListAdapter(mContext, R.layout.view_category_item);
mCategoryAdapter.clear();
mCategories = categories;
mCategoryAdapter.notifyDataSetChanged();
mPopularProducts = products;
notifyDataSetChanged();
mStore =store;
}
public StoreDetailAdapter(Context context, List<Category> categories, List<Store.ProductType.ProductList> popularProducts, List<Store.ProductType.ProductList> purchasedProducts, Store.ProductType popularType, Store.ProductType purchasedType) {
mContext = context;
mCategoryAdapter = new CategoryListAdapter(mContext, R.layout.view_category_item);
mCategoryAdapter.clear();
mCategories = categories;
mCategoryAdapter.notifyDataSetChanged();
mPopularProducts = popularProducts;
mPurchasedProducts = purchasedProducts;
notifyDataSetChanged();
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.view_home_list_item, parent, false);
return new MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
switch (getItemViewType(position)) {
case VIEW_TYPE_STORE_HEAD:
holder.containerHeaderImage.setVisibility(View.VISIBLE);
holder.containerHeading.setVisibility(View.GONE);
holder.items.setVisibility(View.GONE);
Picasso.with(getContext())
.load(mStore.getImageUriNew(Store.IMAGE_MD))
.into(holder.imageView);
break;
case VIEW_TYPE_CAT:
holder.containerHeaderImage.setVisibility(View.GONE);
holder.containerHeading.setVisibility(View.GONE);
mCategoryAdapter = new CategoryListAdapter(mContext, R.layout.view_category_item);
holder.items.setAdapter(mCategoryAdapter);
holder.items.setOnItemClickListener(mCategoryClickListener);
mCategoryAdapter.addAll(mCategories);
break;
case VIEW_TYPE_PROD:
if(mPopularProducts == null || mPopularProducts.isEmpty()){
holder.containerHeading.setVisibility(View.GONE);
break;
}
holder.containerHeaderImage.setVisibility(View.GONE);
int pos = isFromMall()?position-2:position-1;
if (pos < mPurchasedProducts.size()) {
holder.categoryName.setText(mPurchasedProducts.get(pos).getTitle());
break;
}
holder.categoryName.setText(mPopularProducts.get(pos).getTitle());
inStoreList = mPopularProducts.get(pos).getInStores();
mProductCollectionAdapter = new ProductCollectionAdapter(getContext(), R.layout.view_store_detail_product_list_item);
holder.items.setAdapter(mProductCollectionAdapter);
holder.items.setOnItemClickListener(mProductClickListener);
mProductCollectionAdapter.addAll(inStoreList);
mProductCollectionAdapter.notifyDataSetChanged();
break;
}
}
#Override
public int getItemCount() {
return isFromMall?2:1
+ mPopularProducts.size()
+ mPurchasedProducts.size();
}
private AdapterView.OnItemClickListener mCategoryClickListener = (parent, view, position, id) -> {
//click implementation goes here
}
};
private AdapterView.OnItemClickListener mProductClickListener = (parent, view, position, id) -> {
//todo click
};
}
Is there any error in the above code? case position == 2 is not executing in getItemViewType()?
I tried to debug, but position always coming as 0 or 1.
I am using a single layout here, single view holder. What I'm doing is show/hide a view if isFromMall condition is true.
You can have as many ViewTypes as you want. Just make sure that getItemViewType always returns a type for you. and that your logic can generate ViewHolders for those then. Remember that you will have to notify the adapter about a whole dataset changed as soon as you change your condition above.
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
#Override
public int getItemViewType(int position) {
// return your viewType here. make sure each position results in a viewType.
// otherwise you may end up in exceptions as no ViewHolder can be generated afterwards
return yourViewType;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//here you create the ViewHolders for the different viewTypes you have generated abvoe
switch (viewType) {
case 0: return new ViewHolder0(...);
case 2: return new ViewHolder2(...);
...
}
}
}
I have created a library which takes care about all those things, and force the correct usage. You can find it here: FastAdapter
try with this
#Override
public int getItemViewType(int position) {
if(isFromMall){
if(position == 0)
return 2;
else if(position == 1)
return 1;
else if(position == 2)
return 0;
} else {
return 0;
}
return 0;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = null;
if (viewType == 0) {
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.viewpager, parent, false);
return new CreateTwoViewHolder(view);
}
if (viewType == 1) {
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.expandcategoryview, parent, false);
return new CreateThreeeViewHolder(view);
}
if (viewType == 2) {
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.viewpager, parent, false);
return new CreatefourViewHolder(view);
}
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.categoryview, parent, false);
return new CreateOneViewHolder(view);
}
Fixed bye only two view type :)
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
switch (getItemViewType(position)) {
case VIEW_TYPE_CAT:
if (isFromMall) {
holder.containerHeaderImage.setVisibility(View.VISIBLE);
Picasso.with(getContext())
.load(store.getImageUriNew(Store.IMAGE_MD))
.into(holder.imageView);
} else {
holder.containerHeaderImage.setVisibility(View.GONE);
}
holder.containerHeading.setVisibility(View.GONE);
mCategoryAdapter = new CategoryListAdapter(mContext, R.layout.view_category_item);
holder.items.setAdapter(mCategoryAdapter);
holder.items.setOnItemClickListener(mCategoryClickListener);
mCategoryAdapter.addAll(mCategories);
break;
case VIEW_TYPE_PROD:
if (mPopularProducts == null || mPopularProducts.isEmpty()) {
holder.containerHeading.setVisibility(View.GONE);
break;
}
holder.containerHeaderImage.setVisibility(View.GONE);
int pos = position - 1;
if (pos < mPurchasedProducts.size()) {
holder.categoryName.setText(mPurchasedProducts.get(pos).getTitle());
break;
}
holder.categoryName.setText(mPopularProducts.get(pos).getTitle());
inStoreList = mPopularProducts.get(pos).getInStores();
mProductCollectionAdapter = new ProductCollectionAdapter(getContext(), R.layout.view_store_detail_product_list_item);
holder.items.setAdapter(mProductCollectionAdapter);
holder.items.setOnItemClickListener(mProductClickListener);
mProductCollectionAdapter.addAll(inStoreList);
mProductCollectionAdapter.notifyDataSetChanged();
break;
}
}

How to avoid default text issue in Custom Spinner Android?

i have done custom spinner with custom adapter in it but getting some default text everytime how to avoid this issue everytime it should only display 0th position text on spinner:
CourseAdapter courseAdapter = new CourseAdapter(mContext);
customViewHolder.spinner.setAdapter(courseAdapter);
while CourseAdapter.java
public class CourseAdapter extends BaseAdapter {
public static final String TAG=CourseAdapter.class.getName();
List<String> courses =new ArrayList<>(); // code to get the courses ArrayList
Context context;
private void fillCourses()
{
for(int i =0;i<10;i++)
{
if(i == 0)
{
courses.add("Choose");
}
else
{
courses.add("courses:"+i);
}
}
}
public CourseAdapter(Context context)
{
this.context = context;
fillCourses();
}
#Override
public int getCount() {
return courses.size();
}
#Override
public Object getItem(int position) {
return courses.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolderFilterItem viewHolder;
if (convertView == null) {
Log.d(TAG, "fillCourses size:" + courses.size());
convertView = LayoutInflater.from(context)
.inflate(R.layout.layout_customspinner, parent, false);
viewHolder = new ViewHolderFilterItem();
viewHolder.textView = (TextView) convertView.findViewById(R.id.txtViewFilterItem);
viewHolder.checkBox = (CheckBox)convertView.findViewById(R.id.checkboxFilterItem);
//store the holder with the view.
convertView.setTag(viewHolder);
return convertView;
}
else
{
Log.d(TAG, "convertview != null:" + courses.size());
viewHolder = (ViewHolderFilterItem) convertView.getTag();
}
if(courses != null && courses.size() > 0) {
String itemText = courses.get(position);
if (itemText != null) {
viewHolder.textView.setText(itemText);
viewHolder.checkBox.setChecked(true);
}
if(position == 0)
{
viewHolder.checkBox.setVisibility(View.GONE);
}
}
return convertView;
}
private class ViewHolderFilterItem{
TextView textView;
CheckBox checkBox;}
}[![[why Filter item text with checkbox appearing ?][1]][1]
you have to use courseAdapter.notifyDataSetChanged() after setting customViewHolder.spinner.setAdapter(courseAdapter);
Hope this helps.

StickyListHeadersListView MultiChoiceMode issue

I am using the StickyListHeadersListView android library from github for my application. It was working fine. After I implement MultiChoiceMode listener for copying and deleting elements, there is a problem in highlighting of elements.
Whenever I select an element and scroll up and down, some Section Headers are highlighted automatically like shown in the image below
How to avoid this behaviour. Is there any steps I'm missing? Need some hand in resolving the issue.
My Adapter which extends the StickyListHeadersAdapter is given below
public class MessageStickyAdapter extends BaseAdapter implements StickyListHeadersAdapter, SectionIndexer {
private final Context mContext;
private List<Msg> messages;
private int[] mSectionIndices;
private String[] mSectionDates;
private LayoutInflater mInflater;
public MessageStickyAdapter(Context context,List<Msg> listMessages) {
mContext = context;
mInflater = LayoutInflater.from(context);
messages = listMessages;
mSectionIndices = getSectionIndices();
mSectionDates = getSectionDates();
}
private int[] getSectionIndices() {
ArrayList<Integer> sectionIndices = new ArrayList<>();
String lastDate = messages.get(0)._msg_date;
sectionIndices.add(0);
for (int i = 1; i < messages.size(); i++) {
if (!messages.get(i)._msg_date.equalsIgnoreCase(lastDate)) {
Log.d("LastDate,Newdate",lastDate + ',' +messages.get(i)._msg_date);
lastDate = messages.get(i)._msg_date;
sectionIndices.add(i);
}
}
int[] sections = new int[sectionIndices.size()];
for (int i = 0; i < sectionIndices.size(); i++) {
sections[i] = sectionIndices.get(i);
}
Log.d("Sections",String.valueOf(sections.length));
return sections;
}
private String[] getSectionDates() {
String[] dates = new String[mSectionIndices.length];
for (int i = 0; i < mSectionIndices.length; i++) {
dates[i] = messages.get(i)._msg_date;
Log.d("Dates",dates[i]);
}
return dates;
}
#Override
public int getCount() {
return messages.size();
}
#Override
public Object getItem(int position) {
return messages.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder();
convertView = mInflater.inflate(R.layout.right, parent, false);
holder.text = (TextView) convertView.findViewById(R.id.msgr);
holder.time = (TextView) convertView.findViewById(R.id.tim);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
try {
holder.text.setText(URLDecoder.decode( messages.get(position)._msg_content, "UTF-8"));
holder.text.setTag(messages.get(position).getMsgID());
holder.time.setText(messages.get(position)._msg_time);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return convertView;
}
#Override
public View getHeaderView(int position, View convertView, ViewGroup parent) {
HeaderViewHolder holder;
if (convertView == null) {
holder = new HeaderViewHolder();
convertView = mInflater.inflate(R.layout.date_separator, parent, false);
holder.text = (TextView) convertView.findViewById(R.id.textSeparator);
convertView.setTag(holder);
} else {
holder = (HeaderViewHolder) convertView.getTag();
}
String headerText = messages.get(position)._msg_date;
holder.text.setText(headerText);
return convertView;
}
/**
* Remember that these have to be static, postion=1 should always return
* the same Id that is.
*/
#Override
public long getHeaderId(int position) {
// return the first character of the country as ID because this is what
// headers are based upon
return getSectionForPosition(position);
}
#Override
public int getPositionForSection(int section) {
if (mSectionIndices.length == 0) {
return 0;
}
if (section >= mSectionIndices.length) {
section = mSectionIndices.length - 1;
} else if (section < 0) {
section = 0;
}
return mSectionIndices[section];
}
#Override
public int getSectionForPosition(int position) {
for (int i = 0; i < mSectionIndices.length; i++) {
if (position < mSectionIndices[i]) {
return i - 1;
}
}
return mSectionIndices.length - 1;
}
#Override
public Object[] getSections() {
return mSectionDates;
}
public void clear() {
messages.clear();
mSectionIndices = new int[0];
mSectionDates = new String[0];
notifyDataSetChanged();
}
public void restore(List<Msg> newMessages)
{
messages.clear();
mSectionIndices = new int[0];
mSectionDates = new String[0];
messages = newMessages;
mSectionIndices = getSectionIndices();
mSectionDates = getSectionDates();
notifyDataSetChanged();
}
public void add(Msg newMessage)
{
messages.add(0,newMessage);
mSectionIndices = getSectionIndices();
mSectionDates = getSectionDates();
}
class HeaderViewHolder {
TextView text;
}
class ViewHolder {
TextView text;
TextView time;
}
}

Accelarate listview scrolling

I'm trying to scroll my simple listview, but unfortunately it's not scrolling smoothly.
I can't figure out what seem to be the problem. I'm not doing anything fancy.
public class ArticleRowAdapter extends BaseAdapter {
private static final int TYPE_MAINARTICLE = 0;
private static final int TYPE_ARTICLE = 1;
private static final int TYPE_MAX_COUNT = TYPE_ARTICLE + 1;
private Context context;
private ArrayList<ArticleRow> mData = new ArrayList<ArticleRow>();
private LayoutInflater mInflater;
public ArticleRowAdapter(Context context,ArrayList<ArticleRow> data) {
this.context = context;
this.mData = data;
mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
static class ArticleRowHolder
{
TextView tvTitle;
ImageView ivImage;
TextView tvSubTitle;
}
public void addItem(final ArticleRow item) {
mData.add(item);
notifyDataSetChanged();
}
#Override
public int getItemViewType(int position) {
return position == 0 ? TYPE_MAINARTICLE : TYPE_ARTICLE;
}
#Override
public int getViewTypeCount() {
return TYPE_MAX_COUNT;
}
#Override
public int getCount() {
return mData.size();
}
#Override
public ArticleRow getItem(int position) {
return mData.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ArticleRowHolder holder = null;
int type = getItemViewType(position);
if(convertView == null)
{
holder = new ArticleRowHolder();
switch(type)
{
case TYPE_MAINARTICLE:
convertView = mInflater.inflate(R.layout.mainarticle,parent, false);
break;
case TYPE_ARTICLE:
convertView = mInflater.inflate(R.layout.article,parent , false);
break;
}
holder.tvTitle = (TextView)convertView.findViewById(R.id.articleTitle);
holder.ivImage = (ImageView)convertView.findViewById(R.id.articleImage);
holder.tvSubTitle = (TextView)convertView.findViewById(R.id.articleSubTitle);
convertView.setTag(holder);
}
else
{
holder = (ArticleRowHolder)convertView.getTag();
}
ArticleRow articleRow = mData.get(position);
holder.tvTitle.setText((CharSequence)articleRow.title);
holder.tvSubTitle.setText(Html.fromHtml(articleRow.subTitle));
if(articleRow.imageURL == null)
holder.ivImage.setImageDrawable(this.context.getResources().getDrawable(R.drawable.dunk));
else
{
holder.ivImage.setImageDrawable(this.context.getResources().getDrawable(R.drawable.dunk));
}
return convertView;
}
I can say that the reason of lags are imageViews, thats for sure:
holder.ivImage = (ImageView)convertView.findViewById(R.id.articleImage);
try to not to fill imageViews with images and see what happens :). Also check google's BitmapFun and LazyList - both about images in list/grid views

Categories

Resources