I have a UI that consists of a Gridview loaded with a custom adapter. A ButtonAdapter, in this case. So the grid loads fine, the button clicks function like I want them to, but now I have to indicate on the button that it the "active" selection.
I thought I'd do this by just keeping track and changing the background. As it turns out, and based on a couple posts here on SO, the buttons don't actually exist when they are off screen...and even immediately after a scroll. I'll often get a NullPointerException when trying to change a button background after scrolling.
I've tried changing the views in the adapter to RadioButtons and ToggleButtons, but they all offer similar limitations.
The problem seems mostly to do with the getChildAt() that I use on the grid to "unselect" one button, or whatever, when another one is selected.
Is there a workaround for this, or perhaps another suggestion of similar functionality. A vertically scrollable, grid-like format, etc...
Thanks for the help.
EDIT:
Thanks Craigy...I did forget to put a platform on there o.0...i'll add android.
Have you considered using a Selector? In ignorance of how your buttonAdapter works or what you're pulling it from, you can set the background drawable of any View to change according to its state using selectors.
If your selector is defined like so (assuming the presence of appropriate drawables, of course):
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:state_focused="true"
android:drawable="#drawable/list_item_pressed" />
<item android:state_pressed="true"
android:drawable="#drawable/list_item_pressed" />
<item android:state_selected="true"
android:state_activated="true"
android:drawable="#drawable/list_item_selected" />
<item android:state_activated="true"
android:drawable="#drawable/list_item_selected" />
<item android:state_selected="true"
android:drawable="#android:color/black" />
<item android:drawable="#android:color/transparent" />
</selector>
And then set your GridView's choice mode to single Choice:
<GridView
...
android:choiceMode="singleChoice" />
This lets the OS take care of everything for you in terms of remembering which position is selected in the list and then clearing it out for you when you've clicked another one
Set a tag on each of the views that the adapter creates in getView(). Later search for the view with that tag by gridView.findViewByTag() or get the view's tag by view.getTag().
Well, this may not perfectly answer your question but I had done something sort of similar. Basically, I added items to a table and needed to have a remove button associated with the item. When the remove button was clicked, it needed to remove only that item from the table. This could be adapted to your needs so rather than removing the item clicked, it finds the previous item, unclicks it, and then highlights then newly clicked one.
So what I did was give a tag to the buttons themselves (obviously they need to be unique). When a button is clicked, save its tag in something like a sharedPreference for later reference. Then when a new button is clicked, simply find the button with the previously clicked tag and unmark the row that it is in and then mark the row for the newly clicked button. Here is the code I used (sorry the variable names are terrible, I actually had this working in a test app that was never released so I didn't bother giving them better names):
//Previous button clicked
String id = <get this from wherever you choose to store it>
// create a new TableRow
TableRow row = new TableRow(getApplicationContext());
TextView t = new TextView(getApplicationContext());
t.setTextColor(Color.BLACK);
t.setText(unique);
Button b = new Button(getApplicationContext());
b.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v)
{
for(int i = 0; i < table.getChildCount(); i++)
{
TableRow row = (TableRow) table.getChildAt(i);
Button bt = (Button) row.getChildAt(1);
TextView view = (TextView)row.getChildAt(0);
if( id.equals(v.getTag())) //they match, so this is the button that was previously clicked
{
//Put your code here to unclick the previous button and mark the new one as clicked.
}
}
}
});
b.setText(R.string.removeButtonText);
b.setTag(t.getText().toString());
/***BE SURE TO SAVE THE NEW BUTTON TAG (t.getText().toString()) SOMEWHERE LIKE A SHARED PREFERENCE****/
//saving the tag
//add the row to the table
row.addView(t);
row.addView(b);
// add the TableRow to the TableLayout
table.addView(row,new TableLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
Again, I don't suspect this is the exact answer you are looking for, but maybe it will give you an idea to try out. Hopefully this makes some sense, if not please feel free to ask for clarification. Sorry if it is way off base as well.
Related
I have a list with elements that use a custom layout which consists of a text view and an image view. For this, I follow ViewHolder pattern like explained here. The image views display one of two icons and I want to change the icon of the clicked image view.
So my first approach was to define the on click listener of the ImageViews in the overridden getView function of my adapter class. The problem is that when the icon of the first ImageView changes and I scroll down to the last its icon changed as well. This question here was not helpful.
Here I found that it's not the best way to handle the click in the getView function but it's better to do it in the listView.setOnItemClickListener. I tried it but I am not able to find out whether an ImageView was clicked or not as the parent object holds the list item and the view parameter the LinearLayout in which the ImageView is contained (even when I click directly in the ImageView). Setting android:focusable="false" of the outer LinearLayout as it is suggested here did not help.
I'm sure someone must have had this issue / use case but I'm not able to find a solution. So, what's the best way to handle the click of the ImageView in my custon list item view?
Try adding the following attributes to the clickable ImageView instead of its ViewGroup, your LinearLayout:
android:focusable="false"
android:focusableInTouchMode="false"
... and as far as updating UI in a AdapterView/ListView/RecyclerView goes, you should make good use of an int flag that tracks the position of the list item that was clicked, and then set it as a conditional statement in getView() before invoking notifyDataSetChanged(), since "every other" row will be updated, especially in lists of say, 100 rows.
I actually answered this in a similar question here and here.
If you have two icons, you can simply put a custom checkbox instead of an imageview :
<CheckBox
android:id="#+id/customchechecbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/checkbox_selector"
android:button="#android:color/transparent" />
checkbox_selector :
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:drawable="#drawable/checkbox"
android:state_checked="false"/>
<item android:drawable="#drawable/checkboxselected"
android:state_checked="true"/>
<item android:drawable="#drawable/checkbox"/>
</selector>
I'm a Xamarin Android beginner. In an app that I'm writing, I created an Activity with a RadioGroup, and a button below it.
All was fine, until my radio buttons contained so much text that the radio group ran off the phone's screen, and the button was hidden.
So, I searched on the internet, and discovered that I could make the RadioGroup scrollable, by making it a ListView, and setting the ListView to be above the button.
Furthermore, I discovered that Xamarin Android offers the BuiltInView SimpleListItemSingleChoice which is a ready-made radio group as a ListView.
So, I implemented this, and all was fine, except that the text fields in each Item of the BuiltInView get cut short (i.e my radio button options to the user).
I want to apply the property
android:layout_height="wrap_content"
so that my long text labels for the radio buttons won't get cut short.
My question is, how do I apply this to each item of the BuiltInView?
I've tried to define my own custom view, but have run into problems trying to make it checkable, so I wondered if there is a simpler way to solve the problem by using the already provided BuiltInView.
In MyListAdapter GetView, I have
view = (context.LayoutInflater.Inflate(Android.Resource.Layout.SimpleListItemSingleChoice, parent, false));
and in my Activity, I have
myListAdapter = new Adapters.MyListAdapter(this, myStrings, false);
myListView.Adapter = myListAdapter;
As the BuiltInView does not offer an xml file (or I don't know where to access it in Xamarin), I added the following code to the custom Adapter's GetView method:
var textLabel = view.FindViewById<TextView>(Android.Resource.Id.Text1);
//I added these 2 lines to set the WrapContent property on each element of the BuiltInView
AbsListView.LayoutParams layoutParams = new AbsListView.LayoutParams(AbsListView.LayoutParams.MatchParent,
AbsListView.LayoutParams.WrapContent);
textLabel.LayoutParameters = layoutParams;
i have a android listview. i want to change listview item background when i click one listview item.
and then previous selected item must go back to default background. this means only one item has to be selected.
i have searched it for a long time. i can change background of selected item using onItemClick()
but i can't change previous selected item. for example, if i select second item, it was changed. and then i select third item. oh my god! it is changed too! what can i do for this. how can i get the previous position?
here is my android code.
private class ListViewItemClickListener implements
AdapterView.OnItemClickListener {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
TextView title = (TextView) view.findViewById(R.id.title);
title.setBackgroundResource(R.drawable.list_shape);
}
}
You should use the built in methods of selecting items in a listview. Manually changing the background is prone to error as you have found.
Add this attribute to the root view in your listview item xml
android:background="?android:attr/activatedBackgroundIndicator"
then call setItemChecked(x, true) on your ListView where x is the position of the item you want to be selected.
Ensure your listview has a ChoiceMode set that allows selection (such as "SingleChoice")
When I have this in a similar example I have a global field named:
selectedListItem;
This would be updated in your onitemClickListener and the previous item would then have it's background returned to the default.
So to update your code:
private class ListViewItemClickListener implements
AdapterView.OnItemClickListener {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
//First update the previously selected item if one has been set
if(selectedListItem!=null){
TextView previousTitle = (TextView) selectedListItem.findViewById(R.id.title);
previousTitle.setBackgroundResource(R.drawable.list_default_background);
}
//Then update the new one
TextView title = (TextView) view.findViewById(R.id.title);
title.setBackgroundResource(R.drawable.list_shape);
selectedListItem = view;
}
}
So simply initalise selectedListItem as a field in your adapter with the onClickListener as an inner class and have your default background drawable instead of list_default_background .
Alternatively you can track the position numbers instead of the actual view.
EDIT:
To use this method for your list you will also have to keep track of an ID or object instance for your specific list item. In my own solution, in my ListAdapter's getView method I make sure only the list item that matches the ID/instance of the correct item is updated. With your code the way it is you will also find that when you scroll down the view at the same position in this list of visible items is also updated. This is because list view's refer to the list in sets of items, where each set corresponds to the items visible on the screen at any one time.
To update a singular, specific item you would be better suited to using a selector background or indicator as mentioned in the other answers.
HTH
You can change the ListView item colour on clicking it like below. Follow these steps.
(Remember, This is for Custom List View)
Create an XML file in Drawable Folder as Below:
<item android:drawable="#color/orange" android:state_focused="true"/>
<item android:drawable="#color/orange" android:state_pressed="true"/>
<item android:drawable="#drawable/listview"></item>
Choose your own resources.
While implementing Custom ListVIew, you'll have additional layout for Custom List Item design. Below is such an Example.
<ImageView
android:id="#+id/imageView1"
android:layout_width="60dp"
android:layout_height="60dp" />
<TextView
android:id="#+id/textView1"
android:layout_width="fill_parent"
android:layout_height="60dp"
android:layout_toRightOf="#+id/imageView1"
android:background="#drawable/listselect_picture"
android:gravity="center"
android:text="TextView"
android:textColor="#drawable/select_txtcolor"
android:textSize="16sp" />
In Above code I have put the set the XML file from Step 1 as "background" attribute. This will work as you want to.
Additionally if you want to change the text colour on ListItem selection as well, use below XML code and set that XML file as "TextColor" attribute.
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:color="#android:color/white"/>
<item android:state_focused="true" android:color="#android:color/white"/>
<item android:state_pressed="true" android:color="#android:color/white"/>
<item android:color="#android:color/black"/>
</selector>
The code above will change the text color to while on selection and revert to original when unclicked.
I have a class (A_Main.java) extending ArrayAdapter. I set my ListView to use A_Main as it's ListAdapter. Inside A_Main.getView() I inflate the view to get at the ListView widgets for each row. Each row contains a TextView, CheckBox and an ImageButton. When the ImageButton is clicked, I play the song associated with the TextView. I don't want to use onItemClickListener() on the ListView as it's too easy to fumble up a scroll and start playing a new song.
When I click an ImageButton in a new row, I need to un-hilite the ImageButton of the currently playing song, and hilite the new one. I'm thinking the way to do that would be to inflate the view in the ImageButton's onClickListener() and un-hilite every button in the List, then, hi-lite the one which is playing. I'm not sure the best way to go about this. Can I keep a member list in A_Main of each ImageButton ID as getView() iterates over them and reference the ID directly from onClickListener() without causing memory leaks? Do those IDs disappear as soon as getView() is done with them? Any thoughts on alternative approaches?
Edit:
Solution is probably simple Take boolean array globally like this
private final boolean[] selectedstates;
And initialize it with the size of your list in your constructor
selectedstates= new boolean[yourlist.size()];
And in out side of onclick listener set like this
yourbutton.setSelected(selectedstates[position]);
I hope this will help you
Try this
Take a custom selector with two different state images for selection and non selection
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/pause_button"
android:state_selected="true" />
<item android:drawable="#drawable/play_button" />
</selector>
1.Create a global variable
Imageview previous;
in your Custom Adapter and Initialize it in the constructor where you'll get the content
previous=new ImageView(context);
Add in your adapter getView() method you will probably have a onclickListener for your Imageview
do like this
imagPlay.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ImageView current=((ImageView)v);
current.setSelected(true);
previous.setSelected(false);
previous=current;
}
});
This will work, I was confident because I have used it in my app. I hope this will help you
Should look at this video. http://www.youtube.com/watch?v=wDBM6wVEO70. Especially the viewholder part to reuse views and avoid memory leaks. To highlight a listview row button check the position of the item on which you click and highlight the button by setting a background for the button.
Hello i'm working on an app where i'm basically just making a custom multi select listview i did something like this LeftAL is just an array list and this code below is in my onClick
if(LeftAL.contains(position)){
ImageView imageView = (ImageView) lView.getChildAt(position).findViewById(R.id.checkbox);
imageView.setImageResource(R.drawable.checkboxoff);
int i = position;
Integer intObj = new Integer(i);
LeftAL.remove(intObj);
}else{
ImageView imageView = (ImageView) lView.getChildAt(position).findViewById(R.id.checkbox);
imageView.setImageResource(R.drawable.checkbox);
LeftAL.add(position);
}
and then checkboxoff looks like this
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:drawable="#drawable/checkbox_pressed_off" />
<item android:drawable="#drawable/checkbox_off" />
</selector>
and checkbox on is the same thing basically just different images
Now that all works fine as it should and i didn't actually notice this for a little while but when i check say item 3 in my list. It checks and adds to the array list as it should however if i scroll down to the next group of list items i will see that number 3 of the second set is also checked for instance lets just say for example only 3 rows show on my phone.
Cat
Dog
Egg
-------off screen-----
Fox
Gerbil
Hog
I check Dog then i scroll gerbil would also be checked i guess this is just android like recycling or something but it's definately an issue how can i fix this so that only 3 in the list checks not 3 in the next set as well.
Thank you for any help
Android absolutely recycles the Views in a ListView. What you need to do is ensure that you are storing the checked / unchecked state in a separate list and set the state of the check box accordingly each time it's shown.