I have ListView with Adapter, and also use holder. but later I read about recyclerView.ViewHolder and now confused, Is it different with the one I've been using right now? I mean for the optimization purpose, I want to know if using holder only is not good enough without using recyclerView.
public class NewsAdapter extends ArrayAdapter<News> {
Context context;
List<News> myList;
public NewsAdapter(Context context, int resource, List<News> objects) {
super(context, resource, objects);
this.context = context;
this.myList = objects;
}
#Override
public News getItem(int position) {
if(myList != null)
return myList.get(position);
return null;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
Holder holder;
if (convertView == null){
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.list_news, null);
holder = new NewsAdapter.Holder();
holder.title = (TextView)convertView.findViewById(R.id.textViewTitle);
holder.datePosted = (TextView)convertView.findViewById(R.id.textViewDate);
holder.imageView = (ImageView)convertView.findViewById(R.id.imageViewThumbnailpic);
convertView.setTag(holder);
}else{
holder = (Holder)convertView.getTag();
}
News news = getItem(position);
holder.title.setText(news.getTitle());
holder.datePosted.setText(news.getDatePost());
Picasso.with(context)
.load(news.getImgUrl())
.placeholder(R.drawable.coin25)
.error(R.drawable.hnbspic)
.into(holder.imageView);
return convertView;
}
private class Holder{
ImageView imageView;
TextView title;
TextView datePosted;
}
}
It's better to use Recyclerview because it has been optimized for various scenarios and not just for View holder pattern like it give the option for determining how your item should be laid out or like what should be the animation or custom drawing in each item.You can read more this medium post
Related
I am trying to create a custom adapter for a shop element in my Android app but every time I scroll down the list the images seem to change their position, I have had a look around on here already but suggestions that I have found and tried have been to no avail.
Each item has a title, an image (being loading via an AsyncTask or retrieved from the LRUCache) and a price of the item below is the code I use to generate the adapter.
public class ShopAdapter extends BaseAdapter {
public Context context;
public ArrayList<ShopItem> shopItemList;
public ImageCache imageCache;
String imageToLoad;
Bitmap shopImage;
public ShopAdapter(Context context, ArrayList<ShopItem> shopItemList) {
this.context = context;
this.shopItemList = shopItemList;
imageCache = new ImageCache(this.context);
}
#Override
public int getCount() {
return shopItemList.size();
}
#Override
public Object getItem(int position) {
return shopItemList.get(position);
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if(convertView == null){
LayoutInflater vi = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = vi.inflate(R.layout.shop_list, null);
holder = new ViewHolder();
holder.title = (TextView) convertView.findViewById(R.id.textView3);
holder.image =(ImageView) convertView.findViewById(R.id.imageView);
holder.price = (TextView) convertView.findViewById(R.id.textView4);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.title.setText(shopItemList.get(position).getTitle());
shopImage = imageCache.getBitmapFromMemoryCache(shopItemList.get(position).getImage());
if(shopImage != null) {
holder.image.setImageBitmap(shopImage);
} else {
new LoadImage(context, holder.image).execute(imageToLoad);
}
holder.price.setText(shopItemList.get(position).getPrice());
return convertView;
}
static class ViewHolder {
TextView title;
ImageView image;
TextView price;
}
}
Any help would be very much appreciated!
Sorry for my english. I use SlideMenu libruary and i want use new font for textview, it old version use:
((ListView) ((Activity) context).findViewById(R.id.sidemenulistobject)).setAdapter(
new ArrayAdapter<Object>(
context,
R.layout.sidemenu_item,
R.id.textSlide,
items
)
);
But this i cant get my textSlide and set new font. Now i add array adapter and set this in listView. This is my all code:
menu = new SlidingMenu(context);
menu.setMode(SlidingMenu.LEFT);
menu.setTouchModeAbove(SlidingMenu.TOUCHMODE_MARGIN);
menu.setShadowWidth(15);
menu.setFadeDegree(1.0f);
menu.setShadowWidthRes(R.dimen.shadow_width);
menu.setShadowDrawable(R.drawable.shadow);
menu.attachToActivity((Activity) context, SlidingMenu.SLIDING_WINDOW);
menu.setBehindWidth(400);
menu.setMenu(R.layout.sidemenu);
//add item in list view
ArrayList<String> itemsObj = new ArrayList<String>();
itemsObj.add("Новости");
itemsObj.add("События");
itemsObj.add("Наше меню");
itemsObj.add("Фотографии");
itemsObj.add("Видеозаписи");
itemsObj.add("Контакты");
itemsObj.add("Мой профиль");
//get sidemenulistobject
ListView lv = ((ListView) ((Activity) context).findViewById(R.id.sidemenulistobject));
//add adapter
SlideAdapter adapter = new SlideAdapter((Activity) context, R.layout.sidemenu_item, itemsObj);
lv.setAdapter(adapter);
This is my SlideAdapter
public class SlideAdapter extends ArrayAdapter<MenuCategoryObject>{
ArrayList<String> listItems;
int Resourse;
Context context;
LayoutInflater vi;
private ImageLoader imageLoader;
public SlideAdapter(Context context, int resource, ArrayList<String> listItems) {
super(context, resource);
this.listItems = listItems;
Resourse = resource;
this.context = context;
vi = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if(convertView == null) {
convertView = vi.inflate(Resourse, null);
holder = new ViewHolder();
Typeface face=Typeface.createFromAsset(context.getAssets(), "font/AvenirNext-Medium.ttf");
holder.textSlide = (TextView) convertView.findViewById(R.id.textSlide);
holder.textSlide.setTypeface(face);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.textSlide.setText(listItems.get(position));
return convertView;
}
static class ViewHolder {
public TextView textSlide;
}
}
But my item list view dont show. I dont know why. Please help
the problem is with your adapter. Since you are not providing the dataset to the super constructor, you have to override getCount and return its size. Add
public int getCount() {
return listItems.size();
}
to your adapter
I am using a ListView with images, also I have an image loader. The problem is that when there is a fast scrolling the images appears on wrong rows, after short time when the scrolling stops every thing gets back in place.
Here is my adapter:
public class MyAdapter extends ArrayAdapter<MyObj>
{
int layoutResourceId;
List<MyObj> data;
Activity activity;
int rowViewId;
public MyAdapter(Activity activity, int layoutResourceId, List<MyObj> data)
{
super(activity, layoutResourceId, data);
this.layoutResourceId = layoutResourceId;
this.activity = activity;
this.data = data;
rowViewId = R.layout.my_layout.xml;
}
public static class ViewHolder
{
public TextView title;
public ImageView image;
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
View row = convertView;
ViewHolder holder;
if (row == null)
{
LayoutInflater layoutInflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
row = layoutInflater.inflate(rowViewId, null);
holder = new ViewHolder();
holder.title = (TextView) row.findViewById(R.id.title);
holder.image = (ImageView) row.findViewById(R.id.image);
row.setTag(holder);
}
else
{
holder = (ViewHolder) row.getTag();
}
final MyObj obj = data.get(position);
if (event != null)
{
holder.title.setText(event.getTitle());
ImageLoader imageLoader = new ImageLoader();
imageLoader.loadImageFromAppServer(obj.getImageName(), holder.image);
}
return row;
}
}
Is there any thing I can do to prevent this unwanted behaviour?
Your image is being loaded asynchronously by loadImageFromAppServer. When it arrives from the server, your holder.image object is already reused by another row that doesn't represent the same data it did when you asked your ImageLoader object to download the image. You need to implement a way to tell the ImageLoader object to cancel the previous download request.
i've a custom adapter to populate an ListView, i have an ArrayList, when i execute the program the ListView is entire populated with the first object from arraylist.
I searched for an similar question here on stackoverflow and i didnt find a solution, plase, someone help me
Heres playerAdapter.java
public class playerAdapter extends ArrayAdapter<Player>{
private ArrayList<Player> entries;
private Activity activity;
public playerAdapter(Activity a, int textViewResourceId, ArrayList<Player> entries) {
super(a, textViewResourceId, entries);
this.entries = entries;
this.activity = a;
}
public static class ViewHolder{
public TextView item1;
public TextView item2;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
ViewHolder holder;
if (v == null) {
LayoutInflater vi =
(LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.itemplayer, null);
holder = new ViewHolder();
holder.item1 = (TextView) v.findViewById(R.id.nomecharlist);
//holder.item2 = (TextView) v.findViewById(R.id.small);
v.setTag(holder);
}
else
holder=(ViewHolder)v.getTag();
final Player custom = entries.get(position);
if (custom != null) {
holder.item1.setText(custom.getNomePlayer());
//holder.item2.setText(custom.getSecond());
}
return v;
}
}
I'm not sure what the exact problem is with your code, but I do know that's it's much too complicated. Try something like this:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater vi =
(LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = vi.inflate(R.layout.itemplayer, null);
}
final Player custom = entries.get(position);
if (custom != null)
((TextView) convertView.findViewById(R.id.nomecharlist)).setText(custom.getNomePlayer());
return convertView;
}
I have made my own CustomArrayAdapter to show list of Brazilian Restaurants. I have overidden the GetView method to make my own custom view.
private class MyAdapter extends ArrayAdapter<String> {
public MyAdapter(Context context, int resource, int textViewResourceId,
String[] strings) {
super(context, resource, textViewResourceId, strings);
// TODO Auto-generated constructor stub
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View row = inflater.inflate(R.layout.list_item, parent,false);
String [] items= getResources().getStringArray(R.array.locations_array);
TextView tv= (TextView) row.findViewById(R.id.textView1);
ImageView iv = (ImageView) row.findViewById(R.id.imageView1);
tv.setText(items[position]);
iv.setImageResource(R.drawable.brazil);
return row;
}
}
Currently this new GetView class is pulling in a text string from a resource xml file and putting it into the list item.
If I wanted to incorporate an array of extra data generated within the app, I assume that I don't do the array generating in the GetView class as this will be recreated each time a new row is made.
Where do I put the code to make the array, and how do I call this data into the GetView code above?
It's worth pointing out that for better performance you should be making use of the convertView variable passed into the getView() method.
The use of convertView allows you to re-use list item views instead of creating new ones which has a heavy performance hit. If you have a large data set or value performance in your app, you would do well to check out the documentation for getView()
Your code would then look something more like this:
...
//it's also worth moving these methods to your constructor so they aren't called every time getView() for better performance
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
String [] items= getResources().getStringArray(R.array.locations_array);
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null)
convertView = inflater.inflate(R.layout.list_item, parent, false);
TextView tv= (TextView) convertView.findViewById(R.id.textView1);
ImageView iv = (ImageView) convertView.findViewById(R.id.imageView1);
tv.setText(items[position]);
iv.setImageResource(R.drawable.brazil);
return row;
}
...
Building on #CodeDownZero's answer, I highly recommend you adopt the ViewHolder pattern, and definitely recycle your listviews (using convertview).
...
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = null;
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) getSystemService
(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(textViewResourceId, parent, false);
final ViewHolder viewHolder = new ViewHolder();
viewHolder.tv1 = (TextView) view.findViewById(R.id.textView1);
viewHolder.tv2 = (TextView) view.findViewById(R.id.textView2);
view.setTag(viewHolder);
} else {
view = convertView;
}
ViewHolder holder = (ViewHolder) view.getTag();
MyDataClass data = this.getItem(position);
holder.tv1.setText(data.street);
holder.tv2.setText(data.name);
return view;
}
...
private class ViewHolder {
private TextView tv1;
private TextView tv2;
}
You can base an ArrayAdapter on a custom class instead of string. Here is an example:
public class MyDataClass {
public String street;
public String name;
}
private class MyAdapter extends ArrayAdapter<MyDataClass> {
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View row = inflater.inflate(R.layout.list_item, parent,false);
TextView tv1= (TextView) row.findViewById(R.id.textView1);
TextView tv2= (TextView) row.findViewById(R.id.textView2);
MyDataClass data = this.getItem(position);
tv1.setText(data.street);
tv2.setText(data.name);
return row;
}
}
To populate the Adapter with data you can use this snippet in the OnCreate method of the Activity:
..
MyAdapter adapter = new MyAdapter();
MyDataClass lData = new MyDataClass(); // here was a mistake
lData.name = "MyName";
lData.street = "MyRoad";
adapter.Add(lData);
..
ListView.Adapter=adapter; // where Listview is the Listview