Android ListPopupWindow's method isShowing() does not work - android

I've decided to create my own custom spinner by extending a TextView and composing a ListPopupWindow. I want to emulate the following functionality of the original Spinner: when the spinner is clicked the drop down list is shown, the second time the spinner is clicked the drop down list is dismissed. But I'm having some trouble, the ListPopupWindow.isShowing() seems to always return false (I've debugged it):
public class CustomSpinner extends TextView {
...
private ListPopupWindow dropDownPopup;
...
public CustomSpinner(Context context, AttributeSet attrs) {
super(context, attrs);
...
dropDownPopup = new ListPopupWindow(context, attrs);
dropDownPopup.setAnchorView(this);
dropDownPopup.setWidth(WindowManager.LayoutParams.WRAP_CONTENT);
dropDownPopup.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
dropDownPopup.dismiss();
...
}
});
this.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (dropDownPopup.isShowing()) {
dropDownPopup.dismiss();
} else {
dropDownPopup.show();
}
}
});
}
So, each time I click on the spinner the drop down list is shown. It is dismissed when I click on one of the items in the list. The problem seems to be that dropDownPopup.isShowing() always returns false.

By setting dropDownPopup.setModal(true), everything works.

By adding dropDownPopup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NEEDED);beforedropDownPopup.show();, it works for me.

Related

Android-Prevent dismissal of dropdown in AutoCompleteTextView after item selection

Even though I'm setting the setOnItemClickListener on the AutoCompleteTextView and performing some custom operations in it, once that method is done, the list dismisses and prints out the object.toString in the editbox.
I want to prevent dismissal of the dropdown on item select and would also like it to not replace the edit box. How can I achieve this ?
I also want to implement the same i used below code to implement it.
Create a custom class and extend AutoCompleteTextView.
Override dismissDropDown() method and remove the super call from it.
Will work for you.
public class CustomAutoComplete extends AutoCompleteTextView {
public NoSelectionAutoComplete(Context context) {
super(context);
}
public NoSelectionAutoComplete(Context context, AttributeSet attrs) {
super(context, attrs);
}
public NoSelectionAutoComplete(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void replaceText(CharSequence text) {
}
#Override
public void dismissDropDown() {
}
}
I added an onClickListener to the entire custom row layout that I was using for the dropdown adapter. This way whenever the row is clicked, my row onClickListener is invoked and the default one for the dropdown is not.
First Question - Prevent dropdown dismissal:
Solved below.
Second Question - Prevent text replacement: (For others interested)
You can extend AutoCompleteTextView and override
protected void replaceText(CharSequence text) {}
to do nothing.
As others mentioned, overriding performCompletion() won't help here.
well at least it seems like they are planning to add this in near future.
/**
* Sets whether the drop-down should remain visible as long as there is there is
* {#link #enoughToFilter()}. This is useful if an unknown number of results are expected
* to show up in the adapter sometime in the future.
*
* The drop-down will occupy the entire screen below {#link #getDropDownAnchor} regardless
* of the size or content of the list. {#link #getDropDownBackground()} will fill any space
* that is not used by the list.
*
* #param dropDownAlwaysVisible Whether to keep the drop-down visible.
*
* #hide Pending API council approval
*/
public void setDropDownAlwaysVisible(boolean dropDownAlwaysVisible) {
mPopup.setDropDownAlwaysVisible(dropDownAlwaysVisible);
}
edit,new answer:
this worked for me but it closes for a sec,and opens again.
class task extends TimerTask {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
autoComplete.showDropDown();
}
});
}
};
autoComplete.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
new Timer().schedule(new task(),0, 10);
}
});
Overriding replaceText without calling super works fine (prevents entering suggested text into AutoCompleteTextView), but overriding dismissDropDown causes not-dismissing dropdown not only when item clicked, but also when onBackPressed, touched outside dialog etc...
I've ended with NOT using setOnItemClickListener method from AutoCompleteTextView at all. I'm creating custom onClick in my custom ArrayAdapter and set it for all Views returned by getView method
View.OnClickListener onClick=null;
public void setOnItemClickListener(View.OnClickListener onClick) {
this.onClick=onClick;
/*this.onClick=new View.OnClickListener(){
#Override
public void onClick(View v) {
if(v.getTag()==null)
return;
Integer position = (Integer) v.getTag();
Toast.makeText(v.getContext(), "position: "+postion, Toast.LENGTH_SHORT).show();
}
});*/
}
#NonNull
#Override
public View getView(int position, View convertView, #NonNull ViewGroup parent) {
... call super/inflate convertView and do your stuff here
setCustomOnClick(convertView, position);
return convertView;
}
private void setCustomOnClick(final View view, final int position){
view.setTag(position);
view.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
if(onClick==null)
return;
// inside called onClick method v.getTag() will return pressed position
onClick.onClick(v);
}
});
}
in fact setting onClick for whole view will cover "original" always-dismissing and always-replacing-text onClick (not called at all then). Remember about adding custom graphic representation, when pressed (ripple/selector are shown when "original" onClick called only)
I've needed also always-visible functionality, because my autocomplete must always show first position (functional), even when there is no suggestions (if present then shown below on positions 1+)
public class AlwaysVisibleAutoCompleteTextView extends AppCompatAutoCompleteTextView {
private boolean showAlways=true;
public AlwaysVisibleAutoCompleteTextView(Context context) {
super(context);
}
public AlwaysVisibleAutoCompleteTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AlwaysVisibleAutoCompleteTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setShowAlways(boolean showAlways) {
this.showAlways = showAlways;
}
#Override
public boolean enoughToFilter() {
return showAlways || super.enoughToFilter();
}
#Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
super.onFocusChanged(focused, direction, previouslyFocusedRect);
showDropDownIfFocused();
}
private void showDropDownIfFocused() {
if (enoughToFilter() && isFocused() && getWindowVisibility() == View.VISIBLE)
showDropDown();
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
showDropDownIfFocused();
}
}
when AlwaysVisibleAutoCompleteTextView is focused, but dropdown dismissed and user press view again, then dropdown is not showing, because focus state not changing (onFocusChanged not called), so
autoComplete.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP)
autoComplete.showDropDown();
return false;
}
});
If you aren't planning using setOnTouchListener for your AutoCompleteTextView for any other purpose, then OnTouchListener may be set inside AlwaysVisibleAutoCompleteTextView class (in every constructor)

Disabling rows in ListPreference

I am creating a settings menu for a free version of my app. I have a ListPreference displaying many different options. However, only some of these options are to be made available in the free version (I would like all options to be visible - but disabled, so the user knows what they are missing!).
I'm struggling to disable certain rows of my ListPreference. Does anybody know how this can be achieved?
Solved it.
I made a custom class extending ListPreference. I then used a custom ArrayAdapter and used methods areAllItemsEnabled() and isEnabled(int position).
public class CustomListPreference extends ListPreference {
public CustomListPreference (Context context, AttributeSet attrs) {
super(context, attrs);
}
protected void onPrepareDialogBuilder(Builder builder) {
ListAdapter listAdapter = new CustomArrayAdapter(getContext(), R.layout.listitem, getEntries(), resourceIds, index);
builder.setAdapter(listAdapter, this);
super.onPrepareDialogBuilder(builder);
}
}
and
public class CustomArrayAdapter extends ArrayAdapter<CharSequence> {
public CustomArrayAdapter(Context context, int textViewResourceId,
CharSequence[] objects, int[] ids, int i) {
super(context, textViewResourceId, objects);
}
public boolean areAllItemsEnabled() {
return false;
}
public boolean isEnabled(int position) {
if(position >= 2)
return false;
else
return true;
}
public View getView(int position, View convertView, ViewGroup parent) {
...
return row;
}
I searched through and through all over the web, and couldn't find a way to achieve this. The answer above did not help me. I found the entire "ArrayAdapter" method very unintuitive , unhelpful, and hard to implement.
Finally, I actually had to look inside the source code for "ListPreference", to see what they did there, and figure out how to override the default behavior cleanly and efficiently.
I'm sharing my solution below. I made the class "SelectiveListPreference" to inherit the behavior of "ListPreference", but add a positive button, and prevent closing when an option is pressed. There is also a new xml attribute to specify which options are available in the free version.
My trick is not to call ListPreference's version of onPrepareDialogBuilder, but instead implement my own, with a custom click handler. I did not have to write my own code for persisting the selected value, since I used ListPreference's code (that's why I extended "ListPreference" and not "Preference").
The handler looks for the boolean resource "free_version" and if it's true, it only allows the options specified in "entry_values_free" xml attribute. If "free_version" is false, all options are allowed. There's also an empty method for inheritors, if something should happen when an option is chosen.
Enjoy,
Tal
public class SelectiveListPreference extends ListPreference
{
private int mSelectedIndex;
private Collection<CharSequence> mEntryValuesFree;
private Boolean mFreeVersion;
public SelectiveListPreference(Context context)
{
super(context);
}
//CTOR: load members - mEntryValuesFree & mFreeVersion
public SelectiveListPreference(Context context, AttributeSet attrs)
{
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.SelectiveListPreference);
try
{
CharSequence[] entryValuesFree = a
.getTextArray(R.styleable.SelectiveListPreference_entryValuesFree);
mEntryValuesFree = new ArrayList<CharSequence>(
Arrays.asList(entryValuesFree));
}
finally
{
a.recycle();
}
Resources resources = context.getResources();
mFreeVersion = resources.getBoolean(R.bool.free_version);
}
//override ListPreference's implementation - make our own dialog with custom click handler, keep the original selected index
#Override
protected void onPrepareDialogBuilder(android.app.AlertDialog.Builder builder)
{
CharSequence[] values = this.getEntries();
mSelectedIndex = this.findIndexOfValue(this.getValue());
builder.setSingleChoiceItems(values, mSelectedIndex, mClickListener)
.setPositiveButton(android.R.string.ok, mClickListener)
.setNegativeButton(android.R.string.cancel, mClickListener);
};
//empty method for inheritors
protected void onChoiceClick(String clickedValue)
{
}
//our click handler
OnClickListener mClickListener = new OnClickListener()
{
public void onClick(DialogInterface dialog, int which)
{
if (which >= 0)//if which is zero or greater, one of the options was clicked
{
String clickedValue = (String) SelectiveListPreference.this
.getEntryValues()[which]; //get the value
onChoiceClick(clickedValue);
Boolean isEnabled;
if (mFreeVersion) //free version - disable some of the options
{
isEnabled = (mEntryValuesFree != null && mEntryValuesFree
.contains(clickedValue));
}
else //paid version - all options are open
{
isEnabled = true;
}
AlertDialog alertDialog = (AlertDialog) dialog;
Button positiveButton = alertDialog
.getButton(AlertDialog.BUTTON_POSITIVE);
positiveButton.setEnabled(isEnabled);
mSelectedIndex = which;//update current selected index
}
else //if which is a negative number, one of the buttons (positive or negative) was pressed.
{
if (which == DialogInterface.BUTTON_POSITIVE) //if the positive button was pressed, persist the value.
{
SelectiveListPreference.this.setValueIndex(mSelectedIndex);
SelectiveListPreference.this.onClick(dialog,
DialogInterface.BUTTON_POSITIVE);
}
dialog.dismiss(); //close the dialog
}
}
};
}
EDIT: we also need to override the implemented onDialogClosed from ListPreference (and do nothing), otherwise, things valued do not get persisted. Add:
protected void onDialogClosed(boolean positiveResult) {}
Maybe you can do it by overrding default getView:
Steps:
Extend ListPreference
Override onPrepareDialogBuilder and replace mBuilder in DialogPreference with ProxyBuilder
Handle getView in ProxyBuilder->AlertDialog->onShow->getListView->Adapter
Code samples are in custom row in a listPreference?
Having the same problem I found a solution (maybe "hack" is more appropriate). We can register an OnPreferenceClickListener for the ListPreference. Inside this listener we can get the dialog (since the preference was clicked we are pretty safe that it is not null). Having the dialog we can set a OnHierarchyChangeListener on the ListView of the dialog where we are notified when a new child view is added. With the child view at hand we can disable it.
Assuming that the ListView entries are created in the same order as the entry values of the ListPreference we can even get the entry value.
I hope somebody finds this helpful.
public class SettingsFragment extends PreferenceFragment {
private ListPreference devicePreference;
private boolean hasNfc;
#Override
public void onCreate(android.os.Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// load preferences
addPreferencesFromResource(R.xml.preferences);
hasNfc = getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC);
devicePreference = (ListPreference) getPreferenceScreen().findPreference(getString(R.string.pref_device));
// hack to disable selection of internal NFC device when not available
devicePreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
final ListPreference listPref = (ListPreference) preference;
ListView listView = ((AlertDialog)listPref.getDialog()).getListView();
listView.setOnHierarchyChangeListener(new OnHierarchyChangeListener() {
// assuming list entries are created in the order of the entry values
int counter = 0;
public void onChildViewRemoved(View parent, View child) {}
public void onChildViewAdded(View parent, View child) {
String key = listPref.getEntryValues()[counter].toString();
if (key.equals("nfc") && !hasNfc) {
child.setEnabled(false);
}
counter++;
}
});
return false;
}
});
}
}

Checkboxes not changing checked status in UI?

I have a ListView that I am trying to use with a checkable item list. I am calling the toggle() method on my list item class in the ArrayAdaptor, but the checkbox is not being ticked in the UI. However, I can confirm that the correct items are being selected in the UI, and that the "isChecked()" status reports back correctly--just the UI doesn't change at all. Are there any special methods I need to call to update the checkbox graphic for the UI?
To put it another way--how do I programmatically tell the UI that a checkbox should show up as "checked"? It seems this should be a very simple process, but I've been having a lot of trouble finding this information.
Code is as follows:
For the item data class in the ArrayAdaptor:
public class SelectedItemData extends CheckedTextView {
public String _item_name;
public String getItemName()
{
return _item_name;
}
public void setItemName(String in_name)
{
_item_name = in_name;
}
// methods
public SelectedItemData(Context context) {
super(context);
init();
}
public void init()
{
this._item_name = "UNSET";
this.setChecked(false);
}
#Override
public String toString()
{
return _item_name;
}
}
In the Activity class (located within the onCreate method):
_selectedItemsListView = (ListView) findViewById(R.id.selected_items_listview);
_selectedItemsListView.setItemsCanFocus(false);
_selectedItemsListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
_selectedItemsListView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> listview, View view, int position, long id) {
#SuppressWarnings("unchecked")
ArrayAdapter<SelectedItemData> itemsAdapter = (ArrayAdapter<SelectedItemData>)_selectedItemsListView.getAdapter();
SelectedItemData selectedItem = itemsAdapter.getItem(position);
selectedItem.toggle();
Toast.makeText(view.getContext(), "Item " + selectedItem.getItemName() + " Selected!", Toast.LENGTH_SHORT).show();
Log.d(TAG, "Is Item Checked? " + selectedItem.isChecked());
_selectedItemsListView.setAdapter(itemsAdapter);
}
});
Any guidance on how to enable the UI to properly display that one of the items have been selected/checked would be great. Thanks!
You have to update your adapter with the new item and set it to the listview. (itemsAdapter.setItem(item,position))

Android Spinner - onItemSelected / setOnItemSelectedListener not triggering

This is driving me nuts since it's something I've done before but can't figure out why it isn't working now...
I've got a menu button, implemented in the usual way via a menu.xml file and the onOptionsItemSelected method with a switch in it, that creates and displays a spinner.
I've added the setOnItemSelectedListener, but it never seems to trigger. The spinner appears, I pick an option or back out, neither onItemSelected or onNothingSelected are called.
Here is all the code between the "case" and "return true" of the menu-button-handling switch statement. (topThis is a variable referring to the context of the activity - works fine for all other toasts in the app)
String[] widgetModes = {"Mode 1", "Mode2"};
ArrayAdapter<String> widgetModeAdapter = new ArrayAdapter<String> (this, android.R.layout.simple_spinner_item, widgetModes);
widgetModeAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
Spinner widgetModeSpinner = new Spinner(this);
widgetModeSpinner.setAdapter(widgetModeAdapter);
widgetModeSpinner.setPrompt("Choose Widget Mode");
widgetModeSpinner.setOnItemSelectedListener(new OnItemSelectedListener()
{
#Override
public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id)
{
Toast.makeText(topThis, "derp", Toast.LENGTH_LONG).show();
}
#Override
public void onNothingSelected(AdapterView<?> parentView)
{
Toast.makeText(topThis, "herf", Toast.LENGTH_LONG).show();
}
});
widgetModeSpinner.performClick();
Any ideas? I vaguely suspect that the fact I'm creating the Spinner programmatically is the problem...
I had the similar problem when I was implementing a spinner, I resolved it by getting the parent View and set Adapter-
spinner1 =(Spinner)findViewById(R.id.spinner1);
spinner1.setAdapter(BindSpinner("ProgramMaster",cols,null,true,""));
spinner1.setOnItemSelectedListener(new OnItemSelectedListener()
{
protected Adapter initializedAdapter=null;
#Override
public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id)
{
if(initializedAdapter !=parentView.getAdapter() ) {
initializedAdapter = parentView.getAdapter();
return;
}
String selected = parentView.getItemAtPosition(position).toString();
if(abc.equals("Select") && !selected.equals("Select"))
{
do something
}
else
{
Do something
}
textQualification=selected;
SearchUpdated("Qualification");
}
#Override
public void onNothingSelected(AdapterView<?> parentView) {
// your code here
}
});
Remember that you can't re-select the same spinner item, it always sets the first item as selected if you are not adding some custom code to handle the spinner selection.
For the Toast not showing, I would suggest to always use the "MyActivity.this" as your context when creating a Toast inside a listener interface like this:
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
/**
* Called when a new item is selected (in the Spinner)
*/
public void onItemSelected(AdapterView<?> parent, View view,
int pos, long id) {
// An spinnerItem was selected. You can retrieve the selected item using
// parent.getItemAtPosition(pos)
Toast.makeText(MyActivity.this, "Hello Toast",Toast.LENGTH_SHORT).show();
}
public void onNothingSelected(AdapterView<?> parent) {
// Do nothing, just another required interface callback
}
}); // (optional)
And the .show() at the end is easy to forget sometimes;)
Actually, if your spinner visibility is set to gone then it will trigger the click of it when you call performclick() method but it will not trigger its setOnItemSelectedListener
so you need to change the visibility then it will work
I know the question is a bit old, but in case you are waiting for a AsyncTask callback, make sure that you let your adapter know of the data changes by calling notifyDataSetChanged() on the callback thread!
#Override
public void onPostExecute(String result) {
///do something with your data
spinnerArrayAdapter.notifyDataSetChanged();
}

Issue Invoking a context menu when an onClickListener is applied to an activity

Please find the code sample below:
public class Abc extends Activity implements OnClickListener{
private ListView displayList;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mainlayout);
displayList =(ListView)findViewById(R.id.addressbooklistview);
addressbookAdapter = new CustomListAdapter(this,addressbookList);
displayList.setAdapter(addressbookAdapter);
registerForContextMenu(displayList);
}
#Override
public void onCreateContextMenu(ContextMenu menu, View v,ContextMenuInfo menuInfo)
{
Log.e("", "Entered Context Menu");
}
public void onClick(View v) {
Log.e("", "Click Detected");
}
}
I am not able to invoke the context menu on long press.
Please let me know any solution for the same.
I had this problem. Originally the "show context menu" functionality was working, but when I added normal "click" functionality, the context menu no longer worked.
My problem was that I actually had the onClick() code on a TextView within the Listview item, not on the ListView itself. Presumably, it was stealing the click from the listview. To fix this, I removed that code, and in my Activity's onCreate method, I call setOnItemClickListener() for the ListView. So now I have this:
// This creates the context menu functionality.
registerForContextMenu(findViewById(R.id.list_item));
// This creates the click functionality for the listview item.
ListView listView = (ListView) findViewById(R.id.list_item);
listView.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
// code here
}
});
What worked for me is stating explicitly that the longClick was not handled by adding a OnLongClickHandler to the view as well:
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(final View v) {
// do something
}
});
view.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
return false; // ignore and bubble up
}
});

Categories

Resources