I am developing an application in which I have to show ratings depending on the values that I am receiving after parsing the XML response in listview. I have implemented it using the Custom Adapter and showing the images in the getView() method like :
String rating = Constants.menuRatingList.get(position);
if (rating.equals("1")) {
rateImg1.setImageResource(R.drawable.stary);
}
The problem is when I scroll the down to the last item and again move upwards, it is redrawing the list row.
Someone please suggest me approach to stop the redrawing the list row and set the image value permanently.
How do you create the rateImg1 variable?
Maybe you should get it from the view that is passed to the getView() method?
Something like this:
public View getView(int position, View view, ViewGroup parent) {
if (view == null) {
view = LayoutInflater.from(ctx).inflate(R.layout.my_layout, parent, false);
}
((ImageView) view.findViewById(R.id.my_image)).setImageResource(R.drawable.some_drawable);
return view;
}
Also you can implement getViewTypeCount() and getItemViewType() and check them in the above method not to redraw the same image if it is already set.
Related
I'm trying to implement a custom listview. Everything works fine until I use a if () statement inside the getView() method
Without the if() condition a single item gets selected when I select an item but when I add the if() condition, the views are displayed properly but two items (non-adjacent) get selected (1st and last 1st or and second-last, any such combination).
View getView(...){
....
if (!item.getPriceTo().equals(""))
priceToTV.setText(item.getPriceTo());
else
priceToTV.setText(item.getPriceFrom());
return view;
}
Also I'm using saving the previous view to show the selection so the current selection has a red_border and when it is selected a black_border is set to it.:
subItemsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Log.d("New Order", "........");
if (previousViewOfSubItems != null && previousViewOfSubItems != view) {
previousViewOfSubItems.setBackgroundResource(R.drawable.black_border);
if (quantity.getText().toString().equals("xx") || quantity.getText().toString().equals("0")) {
viewForVisibility.setVisibility(View.GONE);
layoutForQuantity.setVisibility(View.GONE);
}
}
if (previousViewOfSubItems == view)
return;
previousViewOfSubItems = view;
previousViewOfSubItems.setBackgroundResource(R.drawable.red_border);
viewForVisibility = previousViewOfSubItems.findViewById(R.id.viewForVisibility);
viewForVisibility.setVisibility(View.VISIBLE);
layoutForQuantity = (LinearLayout) previousViewOfSubItems.findViewById(R.id.layoutForQuantity);
layoutForQuantity.setVisibility(View.VISIBLE);
quantity = (TextView) previousViewOfSubItems.findViewById(R.id.subTypeQuantity);
}
});
previousViewOfSubItems = view; seems to be causing the problem,
In Listviews with adapter you should avoid saving view instances, because views are reused by adapters so view can be same for two rows so rather than saving view instance's reference use ViewHolder Design pattern and use view tagging
You need to use ViewHolder Pattern and view tagging to properly identify every view in different position. ListView always recycle the view instead of re-inflating the view again and again.
You can refer to Android Training documentation on how to implement ViewHolder pattern.
ListViews recycle the views in the list. so as you scroll, top views are reused and content replaced using the methods.
Where you set background to Red etc, use Else statements to set it back to your default black.
its beacause it listview reuses view to display items, once the first view is scrolled out the the same view is reused to display the view at bottom of the listview. instead of comparing the view try compairing the position of the view clicked
I have explained about this abnormal behavior in my blog on recyclerview you refer that to solve this problem.
use pojo class to get the status and update the view accordingly
In my android app my Main Activity extends List Activity. I store there elements called Items, their layout is defined by itemLayout xml file and I use custom adapter (called ItemAdapter) to transfer data to List View in Main Activity. In itemLayout there's an ImageView and my aim is to change its image when user clicks on the particular item in list view. In my opinion the easiest way to achieve that is to get access to particular item's (the one that was clicked) layout, there find the ImageView and call method setImageBitmap. How can I "find" this layout of clicked item? I tried many things in method:
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
}
but nothing worked. Do I have to add anything to my ItemAdapter? Or is it possible to do in onListItemClick(…) but I can't find out how?
You're thinking about it in slightly the wrong way. Adapter's are a way to map data to views. So if you want to change how a particular view looks for a given position, you need to change it's correlating data so that the rendered View then changes. Attempting to modify a view directly kinda goes against how adapters are meant to be used.
So in your case, when a user clicks on an item. Find that item in your adapter via the position number. Then update the item and ensure notifydataset is called. Meanwhile, your adapter's getView() will then handle displaying the appropriate image.
As I understood you need to modify clicked item layout, right? Use argument from onListItemClicked:
v.findViewById(<your_img_view_id>)
For better performance use view holder.
onListItemClick is fired when you press on an element of the ListView. If you want to retrieve the element in the dataset, you can simply invoke
l.getItemAtPosition(position)
the returned value has to be casted to the specific object
Yes It is possible in your custom adapter class in the getView() mtheod u can change imageview bitmap by clicking on it
see this code
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View rowView = convertView;
rowView = inflater.inflate(R.layout.playlist_item, null);
final ImageView im = (ImageView) rowView.findViewById(R.id.favorite);
im.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// Do your stuff here
}
});
return rowView;
}
I have a multiple choice list view and for its adapter I have designed a layout file. I have an image view in the layout and when it's clicked, I want to know what child of the list it is. I mean the child id for the its parent which is the list to further use the method list.getChildAt(???). can anyone tell me how to get that?
The image is independent of the list view and for its onClick attribute I've written a different method that changes image view resource... How can I know which child that a particular imageView is when I click on it?
In the XML layout I have this:
<ImageView
android:id="#+id/choice_image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="deleteSelected" />
And here is a part of deleteSelected method:
public void deleteSelected(View view) {
icon = (ImageView)view.findViewById(R.id.choice_image);
list.getChildAt(???); \\ To know which child the view is
}
I have set the adapter as follows:
list.setAdapter(new ArrayAdapter<String>(this, R.layout.list_choice_multiple,
R.id.choice_text, terms) {
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = super.getView(position, convertView, parent);
icon = (ImageView)v.findViewById(R.id.choice_image);
icon.setImageResource(R.drawable.delete_off);
icon.setTag(R.drawable.delete_off);
return v;
}
});
How can I set the id for each image view so I can access it by getID() method within deleteSelected()?
ListView has a getFirstVisiblePosition method, using that you can calculate the child position based on its position in the list view (which is a parameter passed in to the onItemClicked method of the listener).
int childIndex = listView.getFirstVisiblePosition() - position;
If you're clicking on a child of the row view (View used in the ListView, created by the adapter), then you need to know which position the row belongs too - simplest way is to store the position in the tag of the child when you set the onClickListener
Simple answer: You are writing that the click happens on the ImageView. The ImageView is not associated with the ListView.
So there is no relationship between your click or any element in the ListView.
When clicking on the ImageView, though, the onClick receives a reference to the ListView itself. So what you are trying to achieve? Please be more precise.
Basically first you will have to set an onClickListener for your list-item's ImageView in getView() of the Adaptor.
Now with onClickListener you get the view that is clicked as function parameter to onClick(). Set a different background for this view (ImageView) object.
Hope this is clear. Let me know.
Android GridView is quite interesting, it reuses the child views. The ones scrolled up comes back from bottom. So there is no method from GridView to get the child view by its position. But I really need to get view by its position and do some work on it. So to do that, I created an SparseArray and put views by their position in it from getView of BaseAdapter.
SparseArray<View> ViewArray = new SparseArray<View>();
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
if (view == null)
view = li.inflate(layoutID, null);
ViewArray.put(position, view);
}
Now, I can get all visible views by their position. Everything works perfect as it should but in some devices, the first child view(position 0) is not same as the one in array. I logged the getView and found that for position 0, getView got called many times and each time array was set with different view. I have no idea why GridView is calling getView for position 0 many times and that happens only on few devices. Any solution ?
After reading source of getPositionForView, I have wrote this method in own GridView, works perfect by API 18
public View GetViewByPosition(int position) {
int firstPosition = this.getFirstVisiblePosition();
int lastPosition = this.getLastVisiblePosition();
if ((position < firstPosition) || (position > lastPosition))
return null;
return this.getChildAt(position - firstPosition);
}
You can't reach the views directly because of the recycling. The view at position 0 may be re-used for the position 10, so you can't be sure of the data present in a specific view.
The way to go is to use the underlying data. If you need to modify data at position 10, then do it in the List or array under your adapter and call notifyDataSetChanged() on the adapter.
if you need to have different views for different data subtypes, you can override the two following method in your adapter: getItemViewType() and getViewTypeCount()
Then, in getView() you can 1) decide which layout to inflate 2) know the type of view recycled using getItemViewType()
You can find an example here:
https://stackoverflow.com/a/5301093/990616
There was an issue reported for this. Here is the link. This issue has been closed as WorkingAsIntended. Wish means we can expect the GridView to call getView() on pos 0 multiple times.
My work around is as follow:
public class GridAdapter extends BaseAdapter {
...
int previouslyDisplayedPosition = -1;
...
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if(previouslyDisplayedPosition != position) {
....
previouslyDisplayedPosition = position;
}
return convertedView;
}
What I am trying to do here is returning the same 'convertView' if same pos is called again and again. There by preventing any logic within getView() (eg setting image view etc)to be executed again and again.
I have a strange problem. I am setting the background color of the items of a listview like so:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
...
if (status == true) {
row.setBackgroundColor(Color.argb(255,0,85,187));
}
else {
if (morestuff) {
row.setBackgroundColor(Color.argb(128,255,0,0));
}
}
...
}
This seems to work. However, when i scroll on the listview, and then back, some of the rows have acquired a color from another row without being set by this code. I suspect the listview is recycling the views as an optimization.
How can I fix this?
The getView will be called all the time when the listview is drawn. Simply taking it will be called when we do a small change example do a small scroll
If you want to set color to a specific row, Just do it by checking the position (First argument of getview).
The list view is definately recycling views as an optimization. You should look at the efficient list view example for ideas.