Hello i have a fully working code for my list adapter:
public View getView(int position, View convertView, ViewGroup parent) {
View vi=convertView;
if(convertView==null)
vi = inflater.inflate(R.layout.overview_item, null);
//getting id's
TextView name =(TextView)vi.findViewById(R.id.userUsername);
TextView date =(TextView)vi.findViewById(R.id.imageDate);
ImageView image=(ImageView)vi.findViewById(R.id.userImage);
ImageView avatar=(ImageView)vi.findViewById(R.id.userAvatar);
//setting text
name.setText(dataNames.get(position));
date.setText(dataDates.get(position));
//set image
Log.d("test: ", "Adapter wants to get picture");
imageLoader.DisplayImage(dataImage.get(position), image);
imageLoader.DisplayImage(dataAvatars.get(position), avatar);
return vi;
}
This code works perfect but the problem is this function runs everytime when you scroll throught the listview so whenever the lis item is getting in sight. And that's not what i want. i want it to do this function just once for every list item. This is because when your scrolling fast trought the list it has to load all images again so the loading image is showing and it keeps jumping because the loading image is another size then the image wich is getting loaded. I hope thay tou understand my question and can help me. Already thanks and if i'm not clear please ask my anything in the comments.
So short:
How do i run this code just once for every list-item and not everytime when it's getting in sight?
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.
Check this links:
1 - http://www.javacodegeeks.com/2013/09/android-viewholder-pattern-example.html
No, you should not. This is the way ListView works. Beside, you should use ViewHolder pattern for better performance.
If you still want to do this, you could remove check NULL with convertView. It will solve your problem, but lead to performance, I think.
Related
I have to show some HTML with images from the internet in a ListView. The images are handled in a custom Html.ImageGetter. The problem is, I need the TextView available to download the images to inside. This leads to having to incorporate the HTML parsing inside the ArrayAdapter's getView method. But that method gets called very often by the Android system as it recycles and redraws elements.
How can I stop this recycling from happening? I assume I have to use a different flow. This is my current getView() method:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView view = new TextView(context);
CharSequence element = getItem(position);
CharSequence html = Html.fromHtml(element.toString(), new MessageImageGetter(context, view), null);
view.setText(html);
view.setPadding(12, 4, 12, 4);
return view;
}
Maybe a list-like behavior is possible without the use of ListView and ArrayAdapter, that doesn't recycle and redraw often.
How can I stop this recycling from happening?
You don't. You create a richer model object. Rather than having what appears to be a ListAdapter<String>, have a ListAdapter<Thing>, where:
Thing holds your existing string value (element)
Thing caches the Html.fromHtml() result
Your getView() gets the html CharSequence from the Thing and therefore can take advantage of caching
Feel free to substitute a more applicable noun than Thing, of course... :-)
Could you explain the getView() method of an ArrayAdapter.
I read the docs and it has three parameters:
position: The position of the item within the adapter's data set of
the item whose view we want.
convertView: The old view to reuse, if possible. Note: You should check that this view is non-null and of an appropriate type before using. If it is not possible to convert this view to display the correct data, this method can create a new view.
Heterogeneous lists can specify their number of view types, so that this View is always of the right type (see getViewTypeCount() and getItemViewType(int)).
parent: The parent that this view will eventually be attached to
I understood the position parameter. Like they said, it's imply the position of the item, who view has been requested.
Where does convertView come from. I've seen many examples where they check if convertView is null. If is is null, they inflate a new instance of the row layout, populate it and return it. I guess I've gotten my head around that too but one thing still baffles me. What is the layout that is passed in through the convertView parameter. Is if the resource parameter that is passed in when initialising the ArrayAdapter? Is is a cached copy of the last layout returned by getView()?
And lastly. What does the parent parameter do. I haven't seen too many examples utilising this. Most of them simply reuse/inflate a row layout and return it.
(I'm asking because I have an on-click animation in my ListView. Specifically this one that aims to replicate Spotify's dropdown quick action menu. My animation has been a little sluggish. After diagnosing this issue for a while, I've realized that this has been due to the fact that my getView() method takes a bit of time to complete as I'm inflating a fresh row layout in every iteration. Someone suggested caching the row layout in a ViewHolder while other examples point to reusing the convertView parameter i.e. only inflating a row layout if convertView is null.)
Is is a cached copy of the last layout returned by getView()?
The convertView is the view of a row that left the screen(so it isn't the last view returned by the getView method). For example, the list is first shown, in this case convertView is null, no row view was previously built and left the screen. If you scroll down, row 0 will leave the screen(will not be visible anymore), when that happens the ListView may choose to keep that view in a cache to later use it(this makes sense, as the rows of a ListView generally have the same layout with only the data being different). The reason to keep some views in a cache and later use them is because the getView method could be called a lot of times(each time the user scrolls up/down and new rows appear on the screen). If each time the row view would need to be recreated this would have resulted in a lot of objects being created which is something to avoid. In your getView method you would check convertView to see if it is null. If it's null then you must build a new row view and populate it with data, if it isn't null, the ListView has offered you a previous view. Having this previous view means you don't need to build a new row layout, instead you must populate it with the correct data, as that cached view has the old data still attached to it(you would see a lot of questions on stackoverflow where users ask why the rows of their ListView are duplicating when they scroll down).
What does the parent parameter do. I haven't seen too many examples
utilising this. Most of them simply reuse/inflate a row layout and
return it.
It should be used to get the correct LayoutParams for the newly inflated/built row. For example, if you inflate a layout which has a RelativeLayout as the root and you don't use the parent to get the LayoutParams you could have some problems with the row layout. To take the parent in consideration you would use :
convertView = getLayoutInflater().inflate(R.layout.row_layout, parent, false);
My understanding of convertView is that it's essentially views that have been recycled because they're not being used at the moment - for example, you scroll down the list, the ones at the top aren't on the screen, so they get passed into this parameter for use when you need a new view (so you don't have to create a whole new one while having unused ones sitting around idle). iOS has a similar method called dequeueReusableCellWithIdentifier. If each row of your listview has the same structure, it's safe to cast this to the appropriate type and just update the information in it - text, images, etc. It will be a View that was previously returned by a getView() call for the same list.
My best guess (and it is admittedly a guess) with parent is that it's the view that this adapter's list is a child of. It gives you a route back to the rendering system if you need a context, access to the resource system, to pass information to or receive information from the list's parent view.
I'm writing an app in which you enter a few letters and it creates a list of words that use those letters in that order (but not necessarily consecutively).
I would like my listview to only take up the screen underneath the text entry field and two buttons (the text field and buttons are in a linearLayout and on the top of the screen not taking up much room).
Also, I have an arrayList of all the words I want to show in the list but I don't know how to make it update with the new arrayList each time the search function is invoked.
Thanks for any help.
Also may you might want to check out adding a header to the list view... something like...
lv = getListView();
LayoutInflater inflater = getLayoutInflater();
ViewGroup header = (ViewGroup) inflater.inflate(R.layout.myheader,
lv, false);
lv.addHeaderView(header, null, false);
Maybe put your first LinearLayout (the small form) and the ListView in a second LinearLayout (vertical).
Then for your data, you can use an ArrayAdapter (or extend it but it seems like your list item layout should be simple so no need really) to link your array to the ListView, and you should be able to find adequate methods in that class among : notifyDataSetChanged(), getFilter(), various methods to update the inner adapter ArrayList etc.
I want to be able to access my ArrayAdapter's view:
public View getView(int position, View convertView, ViewGroup parent)
I'm not sure how I can access this:
View myView = myArrayAdapter.getView(myPosition, myView, ?);
I am not sure how I can get a ViewGroup parent?
Well as far as I understand you don't suppose to Adapter in general serves as a source to instance of AdapterView so getView contract is follows :
in params : inPosition,convertView,viewParent
result : View witch will be shown at position inPosition , you can use convertView to bind data to if it's not null, returned view will be attached to viewParent.
So if you want to acquire view that will be shown by adapter view at specific position, why don't just call
AdapterView<?> adapterView = getAdapterView();
View myView = adapterView.getChildAt(position);
If you want to change way of showing view at specific position you should use
Adapter myAdapter = getAdapter();
//change data inside adapter
myAdapter.notifyDataSetChanged();
Update:
Try adding a field to the data in the data set that holds the background resource/color to be used when returning the view, and in the getView do
holder.background = dataSet.get(position).getBackground()
If you don't know what I'm talking about with this holder thing, check How to load the Listview "smoothly" in android
Old:
I'd say the ListView containing the adapter is the parent ViewGroup.
Anyway, why don't you use the ListView containing the ArrayAdapter? The ListView is the container of the Views, rather than the adapter, that only holds the data collection and creates and returns Views on demand when the ListView asks for them (f.e, when scrolling). Even if you were able to fetch a view for the position X, I'd say that won't work, as the convertView you pass as a parameter is not the one the ListView is holding and showing.
Take into account that in a ListView there are at most 7-8 Views inflated at any given moment (I don't know the exact number), and what the ListView does is fill them in by calling the adapter's getview.
This said, to update a single row (a single view) you'll need to update the adapter's data collection. Then, speculating, try to get that View from the ListView and invalidate it, but I doubt this'll work.
I think these concepts are right, if they are wrong I'd be grateful if anyone corrects me.
Why do you want to update only one view at a time?
I've looked everywhere for a solution to this, but I can't figure out how to implement it. My OnItemClickListener was disabled somehow on my ListView rows, because I have an ImageButton in the row layout, which takes over the focus. There have been numerous questions I've found, but none of them have gotten me anywhere.
I've checked this question, but I couldn't really make heads or tails of it. I just need a way to get the rows clickable so that I can detect when a row is pressed. Long press and focus work fine.
Instead of an OnItemClickListener, add an OnClickListener to each of your views returned from your adapter. You'll need to use setItemsCanFocus setting up your list:
ListView list = (ListView) findViewById(R.id.myList);
list.setAdapter(new DoubleClickAdapter(this));
list.setItemsCanFocus(true);
and then in your Adapter's getView, this will yield a clickable row. The button is assumed to be in the inflated xml.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = View.inflate(context, R.layout.cell, null);
view.setClickable(true);
view.setFocusable(true);
view.setBackgroundResource(android.R.drawable.menuitem_background);
view.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
new AlertDialog.Builder(context).setTitle("touched").show();
}
});
return view;
}
set your ImageButton's attribute:
android:focusable="false"
Because AbsListView.onTouchEvent check child.hasFocusable().
I've tested the following solution on SDK levels 8 and 16.
In getView()
setFocusable(false);
setClickable(false);
rather than setting them true in the Adapter's getView() does what I think the original question wanted, and means that an OnItemClickListener gets called, provided that an OnClickListener is not set in getView().
I'm assuming that anything you can do in an View's OnClickListener you can do just as easily in a ListView's OnItemClickListener.
(setOnClickListener on a View implicitly sets the view to be clickable, which prevents the ListView's corresponding OnItemClickListener getting called, apparently.)
The behaviour is as one would expect, in terms of the ImageButton's visual state when the item is pressed or rolled over.
The solution is a slight illusion, in that it is the list item that's being pressed not the ImageButton itself, so if the button doesn't occupy whole list item, clicking somewhere else in the item will still make the button's drawable state reflect the click. Same for focus. That might be a price worth paying.
This will definitely work. Add this to the layout definition.
android:descendantFocusability="blocksDescendants"
Found the solution here
One alternative to setting an OnClickListener for every view is to NOT use an ImageButton - use an ImageView instead. The ImageView can still send events to an OnClickListener and won't take over the focus.
best way to do is this:
android:focusable="false"
android:focusableInTouchMode="false"
set these properties for that Imagebutton and try.
I
For my version of this problem, the issue was that I had set my TextView object to android:inputType="textMultiLine". When I removed this line the issue of the list not being clickable was gone. Looks like a nasty little bug.
Also, I'm still able to use the android:minLines/android:maxLines properties with no problem, so it's not a big issue. Just not the solution I expected.
The following line solved the issue in my project:
<TextView ... android:textIsSelectable="false" />
As an alternative solution which worked for me you can try to extend your adapter from BaseAdapter (iso implementing ListAdapter interface)
Put This code ImageView nextpage= (ImageView)findViewById(R.id.btnEdit); instead of ImageButton . now the list item is active
I have sub-classed ImageButton and setFocusable="false" in layout definition didn't work for me. It solved calling setFocusable(false) in constructor of subclass.
Using a ScrollView can prevent the onItemClickListener from receiving the input.
Hope this helps anyone.