How to override onClick on Android Spinner? - android

A normal android Spinner will pop up a list of choices when clicked. I want to override this onClick. When the user clicks the spinner during certain error states, I want to display an error message rather than pop up the list of choices. Currently, all I can do is set a OnClickListener, but its onClick method doesn't let me prevent the list of options from appearing.

Try setting a onTouchListener and in the onTouch method display your popup and return true to consume the event and stop it from propagating to the view (Spinner in this case).
spinner.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// display your error popup here
return true;
}
});
This should stop the "drop down list" from appearing.
Edit: forgot to mention you could do your error state check in the onTouch method as well, so you don't completely disable the spinner.

Extends from Spinner and override performClick() like this:
#Override
public boolean performClick() {
if(errorOccured) {
// show validation message
return true; // the event is handled by ourselves
}
else {
return super.performClick(); // show spinner dialog
}
}
See sources for more details. Hope this helps.

I would try to create a MySpinnerAdapter class, that implements SpinnerAdapter. In your MySpinnerAdapter, take a look at the getDropDownView method. I believe that's where the pop-up view is created. You could check your error state, and decide to return a different view there (allthough it might be in another function -> check the documentation)...
Anyway, this answer is not a copy-paste one. Just trying to spit some new ideas
public class yourActivity extends Activity
{
private class MySpinnerAdapter implements SpinnerAdapter
{
// There are quite a lot of methods you need to implement...
#Override
public View getDropDownView(int position, View convertView, ViewGroup parent)
{
// Check your error states here
if(<we have some kind of error>)
return specialErrorView // you get the idea
// return the default view with options
return normalView;
}
}
}

Related

OnItemLongClickListener() doesn't result in log tap gesture in ListFragment

Long tap gesture not working in ListFragment in combination with AdapterView.OnItemLongClickListener. I'm trying to achieve the GMail like long-tap selection and continuous selection on upcoming single taps.
The ListFragment declared as below:
public class VacationListFragment extends ListFragment implements View.OnClickListener,
ActionMode.Callback, AdapterView.OnItemClickListener,
AdapterView.OnItemLongClickListener, AbsListView.MultiChoiceModeListener, {
ListFragment loads the data from DB with SimpleCursorAdapter as below:
mAdapter = new SimpleCursorAdapter(getActivity(), R.layout.vacationlist_row_item, null,
fromColumns, toView, 0);
mVacationListView.setAdapter(mAdapter);
Current scenario:
on single the CAB shows up with provided menu(delete) and the action is working. Screenshot pasted below:
Screenshot here
I believe we're very close to solution and any input would be appreciated.
Feel free to ping if I'm not pedagogical. Thanks.
Sample :
textView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
/*do action */
return false;
}
});
Put mVacationListView.setOnLongClickListener(this) into your code. Then you will be able to handle clicks using:
#Override
public boolean onLongClick(View v) {
return false;
}
I could solve the issue with below code sample. It's Good example for Multi-select list for ListFragment in conjunction with LoaderCallbacks and MultiChoiceModeListener.
https://github.com/springbyexample/spring-by-example/blob/master/android/android-client/src/main/java/ua/com/springbyexample/fragment/MainListFragment.java
I think you want list item click in your fragment. Please try below code.
Add this code in your adapter.
mVacationListView.setOnLongClickListener(your fragment instance);
Handle override method in your fragment.
#Override
public boolean onLongClick(View v) {
return false;
}
If you do like this then your list item long click listener will handle at your fragment.

Is there a way to hide specific listview items from the main activity?

Just wondering if what I'm trying to do is possible. So i have a custom adapter for a listview. It contains a textview and two buttons. I would like one of the listview buttons to remain hidden unless a specific button is pressed on the main activity.
So far I have the listview buttons performing their intended function but I have no idea how I would even begin to get what I'm wanting.
Sorry, for clarification, I have one button completely separate from the listview that is just always there. When I press this button I would like to toggle the visibility of a button that is on each listview item all together. The best example of this that I can think of is having a list of items and a button that can toggle off the 1. 2. 3. 4. that comes in front of each item.
Create a method in your adapter for knowing you have clicked the button from your main activity like this
public void buttonIsClicked(){ //in your adapter
buttonhide.setVisibility(visibility?View.VISIBLE:View.GONE);
}
And call this method from your activity on btnclick.
like
yourAdapter.buttonIsClicked();
and call this method for notifying the adapter about the change.
yourAdapter.notifyDatasetChanged().
or
You can use an interface for listening to the clicks in main activity and implement that listener in your adapter
Set visibility gone to the button you want to hide by calling code
buttonhide.setVisibilty(VIEW.GONE);
hide it in oncreate() of your activity and make it shown on the button click event by calling code buttonhide.setVisibilty(VIEW.VISIBLE);
Below is the code
btnView.setVisibility(View.GONE);
yes it is possible to do that.
First have a Model class to back the listview data and keep a flag in that model which indicates whether to show the button in that row's data model. On certain condition change that model's flag and call notifyDataSetChanged() on adapter.
Ex:
class Model{
String label;
boolean showBtn;
}
in adapter's getView()
Model model = list.get(position)
if(model.showBtn){
btn.setVisibility(View.VISIBLE);
}else{
btn.setVisibility(View.GONE);
}
in Activity
disableButton(){
modelList.get(0).setShowBtn(false);
adapter.notifyDataSetChanged();
}
This code will hide button in first row
Add a Boolean value in your dataset which represent the Visibility state of the button.
public class Dataset {
private boolean visible;
public boolean isVisible() {
return this.visible;
}
public void setVisible(boolean visible) {
this.visible = visible;
}
//..more items
}
Then in your getView method of the Adapter check this Boolean value to show/hide the button.
boolean visibility = yourDataset.get(position).isVisible();
yourButton.setVisibility(visibility?View.VISIBLE:View.GONE);
And when the Button outside of your listview is clicked Update your dataset. And call yourAdapter.notifyDatasetChanged().
What you are attempting is: manipulating the visibility of the button declared in the Adapter from the containing activity. Simple, put a controlling variable in the activity and pass it a parameter to adapter.
Boolean mShowButton; //a controlling variable
void onCreate(Bundle savedInstanceState) {
...
mAdapter=new MyAdapter(...,mShowButton);
mButton.setOnClickListener(actionShow );
}
OnClickListener actionShow = new OnClickListener() {
#Override
public void onClick(View button) {
mShowButton=true;
mAdapter.notifyDataSetChanged();
mListView.invalidateViews();
}
};
And do this in your adapter,
Boolean showButton;
public MyAdapter(Context context, List<String> myList, Boolean showButton) {
...
this.showButton=showButton;
}
public View getView(int position, View rowView, ViewGroup parent) {
...
if(showButton){
mButtonTwo.setVisibility(View.GONE);
}else{
mButtonTwo.setVisibility(View.VISIBLE);
}
}

Android spinner onItemSelected called multiple times after screen rotation

I have a layout with three spinners. They differ in the option presented in the drop-down.
In my onCreateView I have a method to setup the spinners. Inside that method I have something like this:
mySpinner = (Spinner) view.findViewById(R.id.my_spinner);
ArrayAdapter<String> mySpinner =
new ArrayAdapter<String>(getActivity(), R.layout.background,
new ArrayList<String>(Arrays.asList(getResources().getStringArray(R.array.spinner_one_data))));
mySpinner.setDropDownViewResource(R.layout.spinner_text);
mySpinner.setAdapter(mySpinner);
mySpinner.setOnItemSelectedListener(this);
As I said, my other two spinners are almost the same but with different options.
I know that onItemSelected is called once for every spinner in a "first setup" so I have a flag to prevent this problem. With this flag solution, my spinners are working as expected.
The problem is when I select in each spinner an option and then rotate the screen. Now, onItemSelected is called 6 times instead the 3 times that I was expecting (I've set a flag to manage this situation of the 3 times calling).
Why Is it happening and hoe should I handle this?
In general, I've found that there are many different events that can trigger the onItemSelected method, and it is difficult to keep track of all of them. Instead, I found it simpler to use an OnTouchListener to only respond to user-initiated changes.
Create your listener for the spinner:
public class SpinnerInteractionListener implements AdapterView.OnItemSelectedListener, View.OnTouchListener {
boolean userSelect = false;
#Override
public boolean onTouch(View v, MotionEvent event) {
userSelect = true;
return false;
}
#Override
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
if (userSelect) {
// Your selection handling code here
userSelect = false;
}
}
}
Add the listener to the spinner as both an OnItemSelectedListener and an OnTouchListener:
SpinnerInteractionListener listener = new SpinnerInteractionListener();
mSpinnerView.setOnTouchListener(listener);
mSpinnerView.setOnItemSelectedListener(listener);
I've found a solution that is working for me.
I have the 3 spinners so onItemSelected is called 3 times at the initial spinner setup. To avoid onItemSelected from firing a method in the initial setup I've created a counter so onItemSelected only fires the method accordingly the counter value.
I've realized that in my situation, if a rotated the screen, onItemSelected is fired again the 3 times, plus a time for each spinner that is not in the position 0.
An example:
I have the 3 spinners and the user changes 2 of them to one of the available option other then position 0 so he ends up with a situation like this:
First spinner - > Item 2 selected
Second spinner -> Item 0 selected (no changes)
Third spinner -> Item 1 selected
Now, wen I rotate the screen, onItemSelected will be fired 3 times for the initial spinner setup plus 2 times for the spinners that aren't at position 0.
#Override
public void onSaveInstanceState(Bundle outState) {
int changedSpinners = 0;
if (spinner1.getSelectedItemPosition() != 0) {
changedSpinners += 1;
}
if (spinner2.getSelectedItemPosition() != 0) {
changedSpinners += 1;
}
if (spinner3.getSelectedItemPosition() != 0) {
changedSpinners += 1;
}
outState.putInt("changedSpinners", changedSpinners);
}
I've saved the state in onSaveInstanceState and then, in onCreateView I checked if savedInstanceState != null and if so, extracted changedSpinners from the bundle and updated my counter to act accordingly.
To expand on Andres Q.'s answer... If you are using Java 8 you can do this with fewer lines of code by making use of lambda expressions. This method also forgoes the need to create a separate class in order to implement onTouchListener
Boolean spinnerTouched; //declare this as an instance or class variable
spinnerTouched = false;
yourSpinner.setOnTouchListener((v,me) -> {spinnerTouched = true; v.performClick(); return false;});
yourSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if(spinnerTouched){
//do your stuff here
}
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
//nothing selected
}
});
What about just to check if fragment is in resumed state? Somethink like this:
private AdapterView.OnItemSelectedListener mFilterListener = new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (isResumed()) {
//your code
}
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
};
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
//set mFilterListener
}
It eliminates the rotation problem and also the first setup problem. No flags etc. I was having the same problem with TextWatchers and found this answer with comment, which inspired me for this solution.

How to prevent a double-tap on a ListView?

Is there anyway to prevent double tap on ListView in Android? I found this when i accidentally tapped item on ListView and it opened up two new window. is there any way to prevent it from opening a same window twice.
Just add listView.setEnabled(false); on select of listview and after select when response will come or back button press just write---- listView.setEnabled(true);
You should restrict the target activity (one that opens when an item is clicked) to have only one instance at any point of time.
Answer to this SO question should help you in achieving that. That way if you accidentally double click, you will still see just one new screen.
Have a try with introducing and Override of isEnabled method
#Override
public boolean isEnabled(int position) {
return false;
}
for the listview.
introduce a boolean for flag and an int to maintain last clicked position
int recentlyClickedPoisition;
boolean flagRecentClickedPoisition;
override the isEnabled method as follows
#Override
public boolean isEnabled(int position) {
if (flagRecentClickedPoisition && recentlyClickedPoisition == position) {
return false;
} else {
return true;
}
}
than set the last clicked position from your click listener as follows
public void setLastClickedPoisition(int recentlyClickedPoisition) {
flagRecentClickedPoisition = true;
this.recentlyClickedPoisition = recentlyClickedPoisition;
}
Hope this will work for you to prevent from double tap, enhance accordinly.
If you are using just single item like TextView in list then just create a class implements OnItemClickListener in this call and then in to onItemClick() method initialize myListView.setOnItemClickListenet(null);
then use Handler.postDelayed method to again set onItemClickListener.
like
myListView.setOnItemClickListener(new MyClickListenerClass());
This is working for all time in my case.
In your XAML view page place isEnable property as a bindable and two way mode.
<ListView ItemsSource="{Binding items}"
x:Name="listview"
HasUnevenRows="True"
IsEnabled="{Binding IsEnable, Mode=TwoWay}"
RowHeight="10"
SelectionMode="Single"
VerticalScrollBarVisibility="Never">
In viewmodel of your xaml page :
private bool _isEnable = true;
public bool IsEnable
{
get => _isEnable;
set
{
_isEnable = value; OnPropertyChanged(nameof(IsEnable));
}
}
public ICommand TapCommand => new Command<//Model>(async (obj) =>
{
IsEnable = false;
//your stuff
IsEnable = true;
});
I have a two pane layout, a listview that controls a detail view. First I thought a delayed handler is the worst idea, but after testing it is the simplest. Otherwise I would have to communicate between activity and fragment to enable the listview item once another detail was loaded. Error prone and complex.
/*example with Handler():*/
final boolean[] allowClick = {true};
myview.setOnClickListener(v -> {
//exit if not allowed
if(!allowClick[0])
return;
//do stuff
//we clicked, block another click
allowClick[0] =false;
//wait 0.7 seconds and allow another click
new Handler().postDelayed(() -> allowClick[0] =true, 700);
});
This solution is implemented on a ListFragment. If the tap dismissed the ListFragment to show a detail view (which it normally would), the next time the ListFragment appears, the tap counter is reset in OnResume():
public class MyListFragment extends ListFragment {
int mTapCounter;
#Override
public void onResume() {
super.onResume();
//Set-Reset ListView clickListener
mTapCounter = 0;
}
//ListView item tap event handler
#Override
public void onListItemClick(#NonNull ListView l, #NonNull View v, int position, long id) {
//Disable click listener to prevent double-tap
mTapCounter++;
//Only process single-tap
if(mTapCounter == 1) {
/* YOUR TAP HANDLER CODE HERE */
}
super.onListItemClick(l, v, position, id);
}
}

How to attach GestureDetector to a ListPreference?

The challenge of attaching a GestureDetector to a ListPreference is 2-fold:
Getting a handle to a ListPreference that's only defined in a preferences.xml (i.e. not instantiated in Java code).
ListPreference is neither a View nor Activity subclass.
Is it possible at all to attach a GestureDetector to a ListPreference?
If so, how would one go about this? Where would I write the code to instantiate GestureDetector and implement the listener?
Unless I didn't quite catch the question correctly, the answer is probably simpler than you might think. The source code for ListPreferece teaches that it's little more than a wrapper around an AlertDialog that displays its various options in a ListView. Now, AlertDialog actually allows you to get a handle on the ListView it wraps, which is probably all you need.
In one of the comments you indicated that, at this stage, all you're interested in is detecting a long-press on any item in the list. So rather than answering that by attaching a GestureDetector, I'll simply use an OnItemLongClickListener.
public class ListPreferenceActivity extends PreferenceActivity implements OnPreferenceClickListener {
private ListPreference mListPreference;
#Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.list_prefs);
mListPreference = (ListPreference) findPreference("pref_list");
mListPreference.setOnPreferenceClickListener(this);
}
#Override
public boolean onPreferenceClick(Preference preference) {
AlertDialog dialog = (AlertDialog) mListPreference.getDialog();
dialog.getListView().setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override public boolean onItemLongClick(AdapterView<?> parent, View v, int position, long id) {
Toast.makeText(getApplicationContext(), "Long click on index " + position + ": "
+ parent.getItemAtPosition(position).toString(), Toast.LENGTH_SHORT).show();
return false;
}
});
return false;
}
}
The result (which the toast in the long-click displaying):
With a reference to the ListView, you could also attach an OnTouchListener, GestureDetector etc. Up to you to go from here.
As #TronicZomB suggested, this isn't directly possible.
You can work around this by creating your own ListPreference derived class, getting its view in the inherited onBindDialogView().
Remember however that the latter is tricky because onBindDialogView() is only called if onCreateDialogView() doesn't return null, and this can happen only if you create your own custom view for YourListPreference.
The recommended way to do this is to build a custom Preference.
Once you have done that, you have a reference to YourListPreference's view, which is mandatory for attaching GestureDetector because one of the steps requires setOnTouchListener() on the view.
I have set a GestrueDetector to a ScrollView using setOnTouchListener previously and searched for a similar method for ListPreference, however since the ListPreference does not contain such a method, I do not believe this will be possible.

Categories

Resources