Spinner with custom adapter doesn't disappear on selection - android

I'm using a Spinner with a custom adapter class called AlgorithmAdapter and in principle everything's working fine, meaning the spinner popup appears and all containing views are properly inflated. However, what I'm not able to find out is how I "tell" the spinner when the selection is made. Of course I know about setSelection(), but that doesn't dispose the popup and return focus to the Activity.
So here's some of the relevant code:
In my activity, I create my adapter like this:
SpinnerAdapter spinnerAdapter = new AlgorithmAdapter(
this,
algorithmSpinner,
R.layout.algo_spinner_item_detail,
res.getStringArray(R.array.anaglyph_algorithm_titles),
res.getStringArray(R.array.anaglyph_algorithm_descriptions),
res.obtainTypedArray(R.array.anaglyph_algorithm_icons),
algorithmSpinner.getSelectedItemPosition()
);
algorithmSpinner.setAdapter(spinnerAdapter);
The constructor of AgorithmAdapter has the following signature:
public AlgorithmAdapter(Context context, Spinner spinner, int viewResourceId, String[] titles,
String[] descriptions, TypedArray icons, int selectedPosition) {
The getDropDownView() method of AlgorithmAdapter:
#Override
public View getDropDownView(final int position, View convertView, ViewGroup parent) {
if(convertView == null) {
LayoutInflater inflater = LayoutInflater.from(context);
convertView = inflater.inflate(viewResourceId, parent, false);
}
final ImageView icon = (ImageView) convertView.findViewById(R.id.algoItemIcon);
final TextView title = (TextView) convertView.findViewById(R.id.algoItemTitle);
final TextView description = (TextView) convertView.findViewById(R.id.algoItemDescription);
final RadioButton radio = (RadioButton) convertView.findViewById(R.id.algoItemRadio);
icon.setImageDrawable(icons.getDrawable(position));
title.setText(titles[position]);
description.setText(descriptions[position]);
radioGroup.add(radio);
/* it's essential to uncheck in case the positions do not match, because
* the system reuses views that could still have a checked radio button */
radio.setChecked(position == selectedPosition);
convertView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
// uncheck every other radio button
for(RadioButton each : radioGroup)
each.setChecked(false);
// inform adapter and user that this button is selected now
radio.setChecked(true);
selectedPosition = position;
spinner.setSelection(position);
}
});
return convertView;
}
where radioGroup is a list of all RadioButtons from all drop down views in order to manage which one is currently checked and which ones have to be unchecked therefore.
That all works very well except for the fact that the popup doesn't close on setSelection() (which itself is working though).
I also tried to attach an OnItemClickListener to the Spinner and manage the selection stuff in there, but that throws an exception saying setOnItemClickListener cannot be used with a spinner, which is really annoying since it feels as if the framework stands in my way instead of helping me at this point. But anyway, that's the reason I have to pass the Spinner through to the adapter which I would be glad I had a different solution for…
So the only thing I'm missing is how I "close" or "dispose" the Spinners drop down. Could anybody help me with that? Is this even anywhere near the way do to it? It cannot be that difficult since writing custom adapters for Spinners is a pretty basic task.
EDIT: No idea if this helps, but maybe someone finds an error in the layout file of my drop down view item. algo_spinner_item_detail.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"
android:background="#android:drawable/list_selector_background" >
<ImageView
android:id="#+id/algoItemIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:contentDescription="#string/algoIconDescription"
android:layout_marginRight="8dp" />
<RadioButton
android:id="#+id/algoItemRadio"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:layout_marginLeft="10dp"
android:layout_centerVertical="false" />
<TextView
android:id="#+id/algoItemTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="#id/algoItemIcon"
android:layout_alignTop="#id/algoItemIcon"
android:textStyle="bold"
android:layout_marginBottom="1dp" />
<TextView
android:id="#+id/algoItemDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/algoItemTitle"
android:layout_alignLeft="#id/algoItemTitle"
android:layout_toLeftOf="#id/algoItemRadio" />
</RelativeLayout>
How the heck do you close/dispose the f***ing Spinner drop down after you selected an item?

Here's what did the trick for me (WARNING: don't show this code to your kids, you'd certainly go to hell for it):
In the onClick method of my convertView I put this line of code:
((View) parent.getParent().getParent().getParent().getParent()).setVisibility(View.GONE);
which not only gets rid of the LinearLayout in which the convertViews are contained, but also makes the com.android.internal.policy.impl.PhoneWindow$DecorView invisible, that was there to darken the Activity screen while a dialog/drop down is shown.
I'm still after a better solution.

Related

What view(s) can I use in order to create something like the about screen of the phone?

I want to write an activity that is similar to the about screen of android phones. I want it to display some information in the style of the about screen of android phones.
Like this
title1
info
-----------------
title2
info
-----------------
etc.
Is there a special view that I can use or is it just a result of multiple views placed in a specific way? Or is there an activity template in android studio that I can use?
Use ListView. You can create a custom layout for cells and then use an array or a cursor to fill the data.
ListView: A view that shows items in a vertically scrolling list. The
items come from the ListAdapter associated with this view.
ListAdapter can receive data as input. The adapter would inflate the layout for each cell in its getView() method and assign the data to the individual views in the cell.
Read more about ListView here: http://developer.android.com/reference/android/widget/ListView.html
See PreferenceActivity or PreferenceFragment. They are special list views populated either from code or from a xml file. There are many different preference types to choose from (checkbox, switch, list etc)
An example preference fragment:
You can use ListView and a custom ArrayAdapter to create a screen like that. If you need any help about how to create a custom ArrayAdapter check this useful tutorial here.
If you want to create a simple list, then ListView is probably the simplest option. You may also want to look into ListActivity and/or ListFragment as well to further simplify the process.
If you intend to use complex animations, or have the list update dynamically with animations, you may be better served with RecyclerView, although using it is more complex.
An straightforward implementation of ListActivity could look something like this:
public class MainActivity extends ListActivity {
String[] titles = { "title one", "title two" };
String[] descriptions = { "desc 1", "desc 2" };
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setListAdapter(new ListAdapter() {
leave everything the same, except for getCount() and getView()
#Override
public int getCount() {
return titles.length;
}
This will ensure you list is always the correct length as your array.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) parent.getContext().
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.row, parent, false);
} else {
view = convertView;
}
TextView title = (TextView) view.findViewById(R.id.title);
TextView description = (TextView) view.findViewById(R.id.description);
title.setText(titles[position]);
description.setText(descriptions[position]);
return view;
}
And row.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/title"
android:textSize="24sp"
android:textStyle="bold"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/description"
android:textSize="20sp"/>
</LinearLayout>

why my ListView never calls onItemClick?

I have really searched a lot on google, stack overflow , and found this : OnItemCLickListener not working in listview ANDROID. But seems sunshine's answer not works for my case. Other answers are all similar ones.
I have tried the following approaches:
add android:focusable="false" to my list item xml
add TextView.setFocusable(false) and TextView.setClickable(false) in ViewHolder
using the xml as described in the above link.
But none of them work.
Here's my xml and java code:
list_item.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:baselineAligned="false"
android:descendantFocusability="blocksDescendants"
android:focusable="false"
android:paddingTop="2dp"
android:gravity="center_vertical" >
<TextView
android:id="#+id/ninegrid_number_list_choice"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="#dimen/ninegrid_number_listchoice_text_size"
android:gravity="center"
>
</TextView>
</LinearLayout>
getView int list adapter.java:
#Override
public View getView(int position, View convertView, final ViewGroup parent) {
ViewHolder holder;
if (convertView == null ) {
convertView = mInflater.inflate(R.layout.list_item, null);
holder = new ViewHolder();
holder.mTextView = (TextView)convertView.findViewById(R.id.ninegrid_number_list_choice);
holder.mTextView.setFocusable(false);
holder.mTextView.setClickable(false);
convertView.setTag(holder);
} else {
holder = (ViewHolder)convertView.getTag();
}
holder.mTextView.setText(mList.get(position));
holder.mTextView.setTextColor(mTextColor);
holder.mTextView.setFocusable(false);
holder.mTextView.setClickable(false);
return convertView;
}
Edit:
in my activity:
listchoice.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
listChoice.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
Log.v(tag, "sdf");
}
});
On method getView() of the adapter, you can set the onclick event of convertView before returning it.
That event handler can be treated as onItemClick :)
The listview click listener not working when we use buttons,imagebutton etc. As you use only textview so there will not be problem like that...
As you used linearlayout no need to use android:focusable="false" in your linear layout.
We used this code only during usage of buttons. Also no need to use code holder.mTextView.setClickable(false);
As text is not a button so it takes no any focus. When u clicked it will click on the list cell not on the textview...
So Simply inflate and then after setting custom adapter to the listview...
setonitemclicklistner to the list view....
UPDATED ANSWER
convertView.setOnClickListener(new OnItemClickListener( position ));
This will surely works...
I just ran into the same problem, and tried all suggested solutions but none worked for me. After trying out different things, I found out there was another reason for my problem. I was wrapping my list_item inside a ScrollView, which was causing my onItemClick to not be called. I hope this helps someone.
One thing that seems to override the row clicks is if you have textviews, make sure they don't have an android:inputtype associated with them
remove the inputtype and you can click the row
This is in addition of course to the above answers on

Multiple clickable views inside listview item

I am trying to create an item in a ListView that has multiple options; view and edit. I would like to create it in exactly the same way as android's contact system - see below:
I have added the red boxes to illustrate the behaviour I want. If you press within the left red-box, you call the contact. If you press within the right red-box, you send a text message to the contact. I have already created a similar layout in XML, but I am having trouble implementing this functionality in code.
I have tried to create custom android:onClick function calls for the separate layouts within the item, but calling an onClick method only allows you to pass in the View as a parameter, but not the position. Needing the position to use listview.getItemAtPosition function, I tried to use listview.getPositionForView to return the position but found this was extremely unstable and was very easy to return incorrect positioning due to recycling of views.
I then tried to set the item's position as the 'tag' in the getView method of my adapter, like so: convertView.setTag(position). But on the onClick method of my activity, I try and use getTag and cast it back to an integer, and it always returns null, which I find puzzling.
What is the best way of implementing a list populated by items with multiple buttons/layouts on each item?
You can create an onClick event on each views in your row like this :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<TextView
android:id="#+id/text_id"
android:layout_width="0sp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center_vertical"
android:onClick="textOnClickEvent"/>
<ImageButton
android:id="#+id/button_id"
android:layout_width="#dimen/width_button"
android:layout_height="match_parent"
android:onClick="imageOnClickEvent"
android:src="#android:drawable/ic_menu_delete" />
</LinearLayout>
Or even, add onClick listeners on each views in the getView method...
more info on this here.
In the list view when you define getview method, this is where you provide all the details of the single list item. There you can mention onlick event of each of the views.
in adapter class, add View.OnClickListener to the getView method:
#Override
public View getView(int i, View view, ViewGroup viewGroup) {
if(view == null) {
final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
view = layoutInflater.inflate(R.layout.grid_vendor_item, null);
}
final TextView textName = (TextView) view.findViewById(R.id.text_id);
final ImageButton imageProfil = (TextView) view.findViewById(R.id.button_id);
textName.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// add your edit codes
}
});
imageProfil.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// add your open prodil codes
}
});
return view;
}

How to put a button inside group item in an ExpandableListView?

I'm developing an application for Android.
How could I put a button in a group of a ExpandableListView?
By clicking the button a dialog will be displayed instead of open or close the group. Click outside the button, the group should behave normally opening and closing.
The image below shows where I would like to insert the button.
http://img193.imageshack.us/img193/2060/expandablelistviewbutto.png
Android ExpandableListView can have any buttons in Group or child.
Make sure that the button is not focusable like below in adapter.
editButton.setFocusable(false);
this will help to click on Group and Button inside group.parent seperately
You need to inflate the groupView with a custom XML file containing a button, like this one ( e.g inflate_xml_groupview.xml ) :
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/FrameLayoutGroupView"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<Button
android:id="#+id/myButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ButtonOfMyExpandableListGroupView"
android:visibility="visible" />
</FrameLayout>
Then you have to create a custom ExpandableListAdapter that extends BaseExpandableListAdapter and get the Button on the getGroupView() method, like this :
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = inflater.inflate(R.layout.inflate_xml_groupview, null);
holder = new ViewHolder();
holder.Button = (Button) convertView.findViewById(R.id.myButton);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.position = ListOfItems.get(groupPosition).getPosition();
Button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
Toast.makeText(getApplicationContext(), "Button " + groupPosition + " is clicked !", Toast.LENGTH_SHORT).show();
// DO STUFF
}
});
}
Hope this helps.
To offer an XML based solution, you simply need to add the following line to the control.
android:focusable="false"
Example:
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="false"/>
I created my own ExpandableListView. I used layouts in XML and classes to build the component.
And surprisingly it was very easy to do.
It was much easier to understand than the standard ExpandableListView because I created a class and a layout for each element of the list (for the list itself, for the group and for the items). There was no need messing with lists of lists of maps, which in my opinion decreases the expressivity and the readability of the code.
Additionally, the list becomes extremely flexible and customizable. I can easily add and remove groups and items at runtime. Now I can freely modify the appearance and internal components of the list.
The ExpandableListView I created can do the same as the standard and more. Just can not tell if the performance was impaired, but did not notice any visible problem.

adding CheckBox to list row loses my onItemClick events?

I have a ListView with an ArrayList adapter. The rows are not very complex (an Image on the left, a LinearLayout with TextViews inside, and a CheckBox on the right ... the layout is copied in below.) The goal is to have a QuickAction bar come up if the user clicks on the image or text, and have the CheckBox change state if the user clicks on the CheckBox. I have each part working independently, but not when they're together in the layout - somehow, I'm losing the onItemClick event.
The "QuickAction" bar is activated by OnItemClickListener(), and it works fine - unless I have the CheckBox in the layout, in which case the CheckBox works fine (using onClick()) but the onItemClickListener is never fired if the user clicks in the row but outside the CheckBox. The layout (minus some style stuff) is:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/vw1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView android:id="#+id/img"
android:layout_alignParentLeft="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<CheckBox android:id="#+id/ckb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_gravity="right"
android:checked="false" />
<!-- stack text in middle section of the row -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<TextView android:id="#+id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content""/>
<TextView android:id="#+id/text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</RelativeLayout>
The code behind it isn't complicated:
public class ListGroupsActivity extends BaseActivityForList {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list);
// add QuickAction bar
ListView lv = getListView();
lv.setOnItemClickListener(new QAListener(this));
}
}
//inner classes of the ListGroupsActivity
class CbOnClickListener implements OnClickListener {
// ...
// test the checkbox isChecked() and keep state in 'selectedItems' array
class GroupListAdapter extends ArrayAdapter<Group> {
super(/*...*/)
CbOnClickListener cblistener = null; // common listener for all the CheckBoxes
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// here do normal testing for convertView being null etc
// heres the view inflation from the layout into the ViewHolder
holder.tv1 = (TextView) convertView.findViewById(R.id.text1);
holder.tv2 = (TextView) convertView.findViewById(R.id.text2);
holder.img = (ImageView) convertView.findViewById(R.id.img);
holder.ckb = (CheckBox) convertView.findViewById(R.id.ckb);
Group g = groups.get(position); // this is the data obj for the row
holder.tv1.setText(g.getName());
holder.tv2.setText("child groups");
Bitmap bm = BitmapFactory.decodeFile(g.getIconUri());
holder.img.setImageBitmap(bm);
Integer key = (Integer) g.getId();
holder.ckb.setChecked(selectedItems.contains(key));
holder.ckb.setOnClickListener(cblistener); // hook onClick to cblistener
return convertView;
}
The QAListener is in a superclass for all my list activities:
public class BaseActivityForList extends
OrmLiteBaseListActivity<DatabaseHelper> {
// QAListener inner class, constructs new quick action bar when item clicked
class QAListener implements OnItemClickListener {
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
// has three ActionItem instances, 'copyAction', 'delAction', 'editAction'
ActionItem copyAction = new ActionItem();
copyAction.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// ... does some stuff
mQuickAction.dismiss();
}
}); // end setOnClickListener
} // end onItemClick
} // end QAListener
} end BaseActivityForList
So, how do I arrange for the Checkbox to pick up the onClick, and the rest of the list item to pick up the onItemClick()?
After few hours of debugging and research. I got it working by
setting following properties on the checkbox:
android:focusable="false"
android:focusableInTouchMode="false"
Also ImageView may need to be configured as described in http://blog.sachin.name/?p=62 (Thanks to that person)
On my side, I discovered that item view MUST NOT be clickable and text views should not be clickable, either. It took me a while to realize that I called setClickable(true) on my item view somewhere in the code.
Just for complementing this accepted answer: in case of ImageButton instead of CheckBox it is not enough to set in xml focusable and focusableInTouchMode to false but during runtime focusable need to be set to false. That means to make sure onItemClick will be detected one must do smth like:
button.setFocusable(false);
so things work completely.. Original credits to author here . hope it will help to someone.
Cheers ;)
Edit: There is even more elegant solution try to add android:descendantFocusability="blocksDescendants" in root layout of list element. That will make clicks onListItem possible and separately u can handle Button or ImageButton clicks

Categories

Resources