Is there any faster alternative to Gallery widget in Android, because Gallery is so slow and lagging, i tryed to use view holder to reuse views, but without result. i used classic method
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.image, null);
// Creates a ViewHolder and store references to the two children views
// we want to bind data to.
holder = new ViewHolder();
holder.icon = (ImageView) convertView.findViewById(R.id.ImageView01);
convertView.setTag(holder);
} else {
// Get the ViewHolder back to get fast access to the TextView
// and the ImageView.
holder = (ViewHolder) convertView.getTag();
}
holder.icon.setImageDrawable(getPicture(items[position]));
return convertView;
}
This is a couple of various galleries created by other and uploaded to GitHub. You might need to checkout them to see how they are and how fast they are.
Related
I have an Adapter with ViewHolder. Let's say I want to set data only once to the ViewHoler inside the getView() method, what's actually happened is, its set the data every time I'm scrolling the view.
How can I check if the ViewHolder was already init or not?
public View getView(final int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder();
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
Deal deal = dealsArr.get(position);
holder.textView.setText(deal.getPrice);
// Here is my issue. how can I do it only once?
changePrice(deal, true);
}
I asssume you already have the basic understanding of Android Adapters & working of getCount() & getView() if not see this
Either adapter returns a null View or an Inflated View; findViewbyId(R.id.xmlID) is always executed;
ViewHolder is used to avoid frequent call and ensure the smooth scrolling of your listview.
Excellent Explanation here
First spend sometime of understand Listview's recycling mechanism!
The link I had shared also has a link to that.!
public View getView(final int position, View convertView, ViewGroup parent) {
//rest of the code goes here
}
focus on the parameters of this method,
the convertViewwhich is for recycling will be null initially because there is no recycling done yet
so this part of the code will be executed;
if (convertView == null) { //convertView is null when recycling isn't done
holder = new ViewHolder();
convertView.setTag(holder);
}
so we initialize the view-holder & refer all our Views(Buttons, TextViews etc) using findview by id.
the next time getView() is called; it executes this part of the code;
else {
holder = (ViewHolder) convertView.getTag();
}
I'd strongly recommend to first understand working of Android Adapters & List View Recyling Mechanism without using ViewHolders in your code. first you understand how & when the methods are invoked then use ViewHolder for more optimization
I have a huge listview , with more then 100,000 items. What I did now..
PostPaid accountItem = (PostPaid)arr.get(position);
ViewHolder holder;
if (convertView == null) {
LayoutInflater mInflater = (LayoutInflater)
context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
convertView = mInflater.inflate(R.layout.list_support_item20, null);
holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(R.id.accountType);
convertView.setTag(holder);
}
else {
holder = (ViewHolder) convertView.getTag();
}
holder.text.setText(accountItem.getTitle());
What I did originally
LayoutInflater mInflater = (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
convertView = mInflater.inflate(R.layout.list_support_item, null);
TextView accountType = (TextView) convertView.findViewById(R.id.accountType);
accountType.setText(accountItem.getTitle());
I want to ask about the differences between two, and which on preferable? And how can I improve performance even more. I am testing on kitat nexus and works perfectly in both cases, but am trying to figure out gingerbread issues
I want to ask about the differences between two, and which on preferable?
with a large of data to represent is always preferable to use the ViewHolder pattern. As you have surely realised, if you do not use this pattern, you have to call findViewById for every view that belongs to the layout you inflated. With this pattern you look for those only once.
Performance Tips for Android’s ListView
use the ViewHolder pattern.
Understand how listview recycling works
How ListView's recycling mechanism works
Quoting docs
Your code might call findViewById() frequently during the scrolling of ListView, which can slow down performance. Even when the Adapter returns an inflated view for recycling, you still need to look up the elements and update them. A way around repeated use of findViewById() is to use the "view holder" design pattern.
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) { // if convertView is null
convertView = mInflater.inflate(R.layout.mylayout,
parent, false);
holder = new ViewHolder();
// initialize views
convertView.setTag(holder); // set tag on view
} else {
holder = (ViewHolder) convertView.getTag();
// if not null get tag
// no need to initialize
}
//update views here
return convertView;
}
So using ViewHolder improves performance as you avoid initializing views everytime. You initialize only when view is null (if(convertView==null) {. Helps in smooth scrolling.
You missed the important part convertView.setTag(holder) and holder = (ViewHolder) ConvertView.getTag()
Android Doc of Smooth scrolling in lisview
In my listview i have image and text which can be updated from internet. Images is saved on external memory. When update is happening text is showed correct but image is still old one, the one which is in cache. On update i delete old image and create new one with same name. But unless im clearing app cache, listview will show old image. How could i solve this ?
I also tried to clear cache programmatically but thats causing some other problems.
On postExecute i try call MyListView.getMdataAdapter().notifyDataSetChanged();however it doesnt help.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
View view = convertView;
final ViewHolder holder;
Items2 i = items.get(position);
if(convertView==null){
holder = new ViewHolder();
view = inflater.inflate(R.layout.list_row, parent, false);
holder.text = (TextView)view.findViewById(R.id.tvRestList);
holder.image = (ImageView)view.findViewById(R.id.imgList);
view.setTag(holder);
view.setOnTouchListener(this);
}else{
holder = (ViewHolder)view.getTag();
}
holder.pos = position;
Items2 item = (Items2)i;
holder.text.setText(item.name);
imageLoader.displayImage("file:///"+context.getExternalFilesDir(null).toString()+File.separator+String.valueOf(position)+".jpg", holder.image, options);
return view;
}
ImageLoader is Universal Image Loader.
I am having an unclear issue concerning the recycling of views in a getView method of a custom array adapter.
I understand that elements are reused, but how do I know exact what to implement in the first part of the if statement, and what in the second?
Right now I am having following code. I came to this question due to dropping the code in the second part of the statement which results in a list of the first 9 elements, which are repeated numberous times instead of all elements. I didn't really know what is causing this exactly...
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
if (row == null) {
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
row = inflater.inflate(layoutResourceId, parent, false);
title = getItem(position).getTitle();
size = calculateFileSize(position);
txtTitle = (TextView) row.findViewById(R.id.txtTitle);
tvFileSize = (TextView) row.findViewById(R.id.tvFileSize);
txtTitle.setText(title);
tvFileSize.setText(size);
} else {
title = getItem(position).getTitle();
size = calculateFileSize(position);
txtTitle = (TextView) row.findViewById(R.id.txtTitle);
tvFileSize = (TextView) row.findViewById(R.id.tvFileSize);
txtTitle.setText(title);
tvFileSize.setText(size);
}
return row;
}
It's easy. The first time no row is created, so you have to inflate them. Afterwards, the Android os may decide to recycle the views that you already inflated and that are not visible anymore. Those are already inflated and passed into the convertView parameter, so all you have to do is to arrange it to show the new current item, for example placing the right values into the various text fields.
In short, in the first part you should perform the inflation AND fill the values, in the second if (if convertView != null) you should only overwrite the field because, given the view has been recycled, the textviews contain the values of the old item.
This post and this are good starting points
I understand that elements are reused, but how do I know exact what to implement in the first part of the if statement, and what in the second?
The organization is quite simple once you get the hang of it:
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
/* This is where you initialize new rows, by:
* - Inflating the layout,
* - Instantiating the ViewHolder,
* - And defining any characteristics that are consistent for every row */
} else {
/* Fetch data already in the row layout,
* primarily you only use this to get a copy of the ViewHolder */
}
/* Set the data that changes in each row, like `title` and `size`
* This is where you give rows there unique values. */
return convertView;
}
For detailed explanations of how ListView's RecycleBin works and why ViewHolders are important watch Turbo Charge your UI, a Google I/O presentation by Android's lead ListView programmers.
You want to create a ViewHolder class in your MainActivity. Something like
static class ViewHolder
{
TextView tv1;
TextView tv2;
}
then in your getView, the first time you get your Views from your xml in the if and reuse them after that in the else
View rowView = convertView;
if (rowView == null)
{
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
rowView = inflater.inflate(R.layout.layout_name_to_inflate, parent, false);
holder = new ViewHolder();
holder.tv1= (TextView) rowView.findViewById(R.id.textView1);
holder.tv2 = (RadioGroup) rowView.findViewById(R.id.textView2);
rowView.setTag(holder);
}
else
{
holder = (ViewHolder) rowView.getTag();
}
I would recommend that you use the View holder and convertview pattern to create your listView as it will be more efficient.Here is a good explanation of how it works with a re-use strategy. This will answer your question on how re-cycling works. If you want to refer to a code sample, I have it on GitHub.
Hope this helps.
The last part of the question I really couldn't grasp without a picture of the effect but for the first part "what to implement in the first part of the if statement, and what in the second" I think I've found the this implementation very common.
You would find the view references first and store them to a static class ViewHolder which then you attach to the tag of the new inflated view. As the listview recycles the views and a convertView is passed getView you get the ViewHolder from the convertView's tag so you don't have to find the references again (which greatly improves performance) and update the view data with that of your object at the position given.
Technically you don't care what position the view was since all you care for is the references to the views you need to update which are held within it's ViewHolder.
#Override
public View getView(int position, View convertView, ViewGroup container) {
ViewHolder holder;
Store store = getItem(position);
if (convertView == null) {
convertView = mLayoutInflater.inflate(R.layout.item_store, null);
// create a holder to store references
holder = new ViewHolder();
// find references and store in holder
ViewGroup logoPhoneLayout = (ViewGroup) convertView
.findViewById(R.id.logophonelayout);
ViewGroup addressLayout = (ViewGroup) convertView
.findViewById(R.id.addresslayout);
holder.image = (ImageView) logoPhoneLayout
.findViewById(R.id.image1);
holder.phone = (TextView) logoPhoneLayout
.findViewById(R.id.textview1);
holder.address = (TextView) addressLayout
.findViewById(R.id.textview1);
// store holder in views tag
convertView.setTag(holder);
} else {
// Retrieve holder from view
holder = (ViewHolder) convertView.getTag();
}
// fill in view with our store (at this position)
holder.phone.setText(store.phone);
holder.address.setText(store.getFullAddress());
UrlImageViewHelper.setUrlDrawable(holder.image, store.storeLogoURL,
R.drawable.no_image);
return convertView;
}
private static class ViewHolder {
ImageView image;
TextView phone;
TextView address;
}
i am using a viewholder to display from a dynamic arrayadapter.it works but the data displayed changes irregularly when i scroll the List.i want my List View to be populated only once ,Not all the time when i scroll my list. Any suggestion?
Here is my Code
public View getView(int position, View convertView, ViewGroup parent) {
// A ViewHolder keeps references to children views to avoid unneccessary calls
// to findViewById() on each row.
ViewHolder holder;
// When convertView is not null, we can reuse it directly, there is no need
// to reinflate it. We only inflate a new View when the convertView supplied
// by ListView is null.
if (convertView == null) {
convertView = mInflater.inflate(R.layout.sample, null);
// Creates a ViewHolder and store references to the two children views
// we want to bind data to.
holder = new ViewHolder();
holder.name = (TextView) convertView.findViewById(R.id.text);
holder.icon = (ImageView) convertView.findViewById(R.id.icon);
convertView.setTag(holder);
} else {
// Get the ViewHolder back to get fast access to the TextView
// and the ImageView.
holder = (ViewHolder) convertView.getTag();
}
// Bind the data efficiently with the holder.
if(_first==true)
{
if(id<myElements.size())
{
holder.name.setText(myElements.get(id));
holder.icon.setImageBitmap( mIcon1 );
id++;
}
else
{
_first=false;
}
}
//holder.icon.setImageBitmap(mIcon2);
/*try{
if(id<myElements.size())
id++;
else
{
id--;
}
}
catch(Exception e)
{
android.util.Log.i("callRestService",e.getMessage());
}*/
return convertView;
}
static class ViewHolder {
TextView name;
ImageView icon;
}
when the list is loaded it looks like this : http://i.stack.imgur.com/NrGhR.png after scrolling some data http://i.stack.imgur.com/sMbAD.png it looks like this, and again if i scroll to the beginning it looks http://i.stack.imgur.com/0KjMa.png
P.S : my list have to be in alphabetic order
Have you tried this?
public View getView(int position, View convertView, ViewGroup parent) {
// A ViewHolder keeps references to children views to avoid unneccessary calls
// to findViewById() on each row.
ViewHolder holder;
// When convertView is not null, we can reuse it directly, there is no need
// to reinflate it. We only inflate a new View when the convertView supplied
// by ListView is null.
if (convertView == null) {
convertView = mInflater.inflate(R.layout.sample, null);
// Creates a ViewHolder and store references to the two children views
// we want to bind data to.
holder = new ViewHolder();
holder.name = (TextView) convertView.findViewById(R.id.text);
holder.icon = (ImageView) convertView.findViewById(R.id.icon);
convertView.setTag(holder);
} else {
// Get the ViewHolder back to get fast access to the TextView
// and the ImageView.
holder = (ViewHolder) convertView.getTag();
}
// Bind the data efficiently with the holder.
holder.name.setText(myElements.get(id));
holder.icon.setImageBitmap( mIcon1 );
return convertView;
}
static class ViewHolder {
TextView name;
ImageView icon;
}
If yes, what's wrong with it?
I don't think loading all the rows at once is a good idea. You will end up having plenty of useless Views in memory that are going to slow the application down for nothing.
Views and operations on views (like inflate, findViewById, getChild..) are expensive, you should try to reuse them as much as possible. That's why we use ViewHolders.
You would need to write you own version of ListView to do that (which is bad). If the ListView doesn't work properly, it probably means that you are doing something wrong.
Where does the id element come from? You are getting the position in your getView() method, so you don't need to worry about exceeding list bounds. The position is linked to the element position in your list, so you can get the correct element like this:
myElements.get(position);
When the data in your list changes, you can call this:
yourAdapter.notifyDataSetChanged()
That will rebuild your list with new data (while keeping your scrolling and stuff).