I have a list of around 2500 items in a ListView object. I would like for some of those items to be visible, but greyed out. An activity launches for the ones that aren't greyed out (in my case, this is an AlertDialog). I'd like (for example) the first 500 items to be active and to have the usual behaviour, whilst the other items are greyed out and clicking on them does nothing.
What I'm trying to create is a trial version of a ListView app, so that the user can only access some of the items in the list for demonstration purposes.
What would be the simplest way of going about this? I can only think of having two lists that are appended to each other (the first being active, the second not). Ideally I would actually have interdigitated lists, such that (for example) every third item is active and the others are not, but I'll settle for two separate groups that run on the same list if that would be too complicated.
Simply extend whatever adapter you are using and add this simple check in getView():
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
if(position < 500) {
view.setClickable(false);
view.setEnabled(true);
}
else {
view.setClickable(true);
view.setEnabled(false);
}
return view;
}
I did your simple check (if the row is in the first 500) then changed a couple characteristics.
setEnabled() turns the text grey when it is false.
setClickable() when true this prevents the ListView from receiving the TouchEvent.
Hope that helps!
Related
i use ListView to display a list of 30 products.
protected class ListProductAdapter extends ArrayAdapter<Product>
implements View.OnClickListener {
...
#Override
public View getView(int position, View convertView, ViewGroup parent) {
I noticed that when I display the activity, it first calls getView 6 times, then if I scroll down, getView will be called to display other products, but if then I scroll up, getView is called again on product for which getView had already been called. Isn't it possible to get it in memory to not have to recalculate everything I scroll down and up ?
The ListView recycles its Views precisely so you do not have to have a View for every item in memory all the time, but it passes an old View as convertView so it can be reused and doesn't have to be re-inflated every time. See here.
That's how ListView works. It manages a number of views and calls getView(int, View, ViewGroup) whenever it needs one view to be set up. It doesn't hold to many views though. You might be able to hold all in memory, but that's not its intent.
If convertView is null the list needs you to create the views (your first 6 calls to getView). When you start scrolling, the list will provide you with an (already initialized) convertView which you only need to refill with the data you want to display.
See the documentation of getView for an elaborate explanation.
In a custom adapter, how to know weither I need to reconfig the convertView or not?
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView == null) {
imageView = (ImageView) mInflater.inflate(R.layout.avatar, parent, false);
// Should this 2 lines of code be here ?
User user = mUserList.getUserAt(position);
user.setAvatar(imageView);
} else {
imageView = (ImageView) convertView;
}
// or here ?
User user = mUserList.getUserAt(position);
user.setAvatar(imageView);
return imageView;
}
I would think that if it is recycled, I would not need to reset the user's avatar but I often see the configuration happening outside of the if block. Why is that?
I always configure the view outside of if block. This convertView that you get in the getView method might (and most probably will) be set up for another user by some previous call to getView because of ListView's policy to reuse item views when they go offscreen. If you do not set up a proper avatar you will have wrong one for this item. Of course you won't need to reset properties that are independent of the concrete position like background.
For example CursorAdapter separates getView into two parts: newView, that performs inflate and (mostly) position-independent setup and bindView that assigns actual position-dependent data.
It may also happen that you will get exactly the same view that you used for this position earlier. Of course you can avoid resetting a view in this case, but you need to check if data in this view are valid. Setting and then checking View's tag comes to my mind as a most obvious solution.
It is not truly recycled, it just means that you need to fill a "recycled" view with new data according to the its new position. If you don't do it this row will be filled with old data that shouln't be visible on the screen anymore since you scrolled away its position.
So in short you have to reconfigure view with fresh data each time getView() called (outside of if block in your code).
Your issue is only with the understanding of listview.
So here I'll make you clear how it works??
Let's say listview has to contain 20 items but your current screen can accommodate(show on screen) only 8 items(list items, in your case imageview).
When the listview tried to get items for 1----8th it will return you convertView as null because no recycling of objects happened yet.
but, when you try to scroll, in our case(scroll up!).
the 1st element of the list will be recycled when go out of screen, and will be supplied as convertView for 9th item.
In this way listview has to manage only 8th(in our case) to show any number of items.
The opposite will happen if we will scroll down wards.
So, on the basis of convert view (null or not) you have to design your logic either to create and fill or to fill.
Hope this will help you.
I'd like to have the effect of clicking on a row in a ListView and offering buttons to do basic editing with what I'm displaying in said row.
Now I don't mean to change a textview into an editable-textview exactly, rather something that just offers buttons like "edit" and "delete" for example. This could be done with a popup but I'm trying to avoid this, I want these action buttons to be replacing the displayed content of that row.
At first I figured it could just have two layouts for inflating into rows. One "active" and one normal. On click it would just return the different layout for the row clicked and have a marker to indicate which row was the currently selected one. First ran into issues I didn't expect with the inflated views being recycled as a listview is scrolled up & down. Fine, ok, so I made an extension of BaseAdapter so I could do my own thing with getView(). Well I managed to get it to correctly allow recycling of views (and not reusing the "active" one where it wasn't suppose to be) however I see no way to make it refresh / reload the alternate layout except when scrolling said row off screen and then back on. Seems there's no way to force getView() to actually happen unless a row leaves the screen and comes back.
So it's a two headed question. One is there a way to make a single row swap out inflated views while it's being displayed? And two maybe my method of doing this is a backwards way to accomplish what I want and is there a better way?
Thank you for your time!
public View getView(int position, View convertView, ViewGroup parent) {
View v;
Boolean activeExists = false;
if(convertView!=null && convertView.getTag()==(String) "active"){
activeExists=true; }
if (((position==activeFlag && activeExists==true) || (position!=activeFlag && activeExists==false)) && convertView!=null) {
v = convertView;
} else if(position==activeFlag && activeExists==false){
v = inflater.inflate(rowlayoutActive, parent, false);
v.setTag((String) "active");
} else if (position!=activeFlag && activeExists==true) {
v = inflater.inflate(rowlayout, parent, false);
} else {
v = inflater.inflate(rowlayout, parent, false);
}
bindView(position, v);
return v;
}
(Outside of this getView I have (int) activeFlag to remember which one is the current "selected" and I have my own version of bindView which doesn't really differ that much from normal. The boolean is a quick mark for already inflated views to keep the wrong one from going to the wrong row when recycled.)
Great question, and part of the answer might be that you consider the MVC model. Strictly speaking, you should modify the underlying data to cause a change in the UI which suggests adding perhaps a boolean to your data "isEditing" or similar, set it with the click then calling notifyDataSetChanged. In getView, you would test that boolean to use the appropriate layout.
How can I color code individual rows in a ListView exactly like it is done in the native MESSAGING app? I do NOT want to simply do alternating rows, but I want to copy the format used in the MESSAGING app, where message rows have a different background color based on the username of the message.
FYI:
I am currently extending SimpleAdapter and overriding getView(int position, View convertView, ViewGroup parent). In this method, I am trying to calculate setting the background color based on the position as compared to a list of 'positions to highlight' that I am maintaining each time I update the list, but so far this is only working the first time the list is updated.
In class that overrides SimpleAdapter:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
if(highlightPositions.contains(new Integer(position))){
view.setBackgroundColor(highlightColor);
}
else{
view.setBackgroundColor(normalColor);
}
return view;
}
Thank you for any guidance you can offer!
I think you need to call notifyDataSetChanged() on your ListAdapter. This forces the list to refresh, calling your overridden getView() method.
Thanks for the help. Actually, the code I listed does in fact work. The problem turned out to be a small mistake in my keeping track of the list positions to highlight in the highlightPositions list. Once I corrected that, and in fact highlightPositions list did indeed contain the correct positions to highlight, then it worked properly.
I'm trying to put a checkbox into ExpandableListView. How do I do that? I extend BaseExpandableListAdapter and put the following into getGroupView():
#Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
ViewGroup parent) {
View view = convertView == null ?
context.getLayoutInflater().inflate(R.layout.onet_head, null) : convertView;
((TextView)view.findViewById(R.id.onetText)).setText(cats.get(groupPosition).value);
return view;
}
Notice that inflated layout? That's where I'm putting TextView and CheckBox.
I noticed that placing a checkbox into my group row layout disables default group row functionality when clicking on the row makes a secondary (child) list appear. CheckBox is functioning as expected but when I click outside of the it the
click is never detected by ether CheckBox or by OnGroupClickListener. I suspect that placing CheckBox into group row this way interferes with event detection/handling but thus far I'm not able to track it down
Can
someone help me to resolve this?
The CheckBox works fine though including detecting clicks when
clicking directly on the box
Anytime you place an item that is focusable in a list the list items no longer respond to clicks or anything like that. For every item you place in the list item that is focusable (buttons, checkboxes, etc), you need to set the android:focusable attribute to false.
I had a similar question and that was the answer for me.
Android custom ListView unable to click on items