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.
Related
This might sound a bit convoluted.
I have roughly 15 Spinners in one activity and made a distinct method for each of these spinners. I then initiate the methods in the onCreate method.
Method example:
//Relative Position Spinner
public void relativePositionSpinner() {
Spinner relativePositionSpinner = (Spinner) findViewById(R.id.spinner_relativePosition);
ArrayAdapter relativePositionAdapter = ArrayAdapter.createFromResource(this, R.array.relativePosition, R.layout.spinner_item);
relativePositionSpinner.setAdapter(relativePositionAdapter);
//what happens when selected
relativePositionSpinner.setOnItemSelectedListener(this);
}
OnCreate Method:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_new_hand);
//initiate all Spinners
relativePositionSpinner();
absolutePositionSpinner();
etc.
Now what I want is to send the data of each Spinner to another Activity with the click of a Button. I know that I can do this with an intent and using putExtra in the Button method like this:
public void openHandSummary() {
//Find the Button that gives option to enter new hand
Button handInputButton = (Button) findViewById(R.id.hand_input_button);
//set a click listener on Hand Analyzer Button
handInputButton.setOnClickListener(new View.OnClickListener() {
//below code will be executed when the new Hand Button is clicked
#Override
public void onClick(View view) {
Intent handSummaryIntent = new Intent(NewHandActivity.this, HandSummaryActivity.class);
handSummaryIntent.putExtra("RelPosString", WHATTOENTERHERE??)
startActivity(handSummaryIntent);
}
});
}
However I do not know how to retrieve the value/variable out of my Spinners to put them into the Button/intent method? Because if I make a String in the Spinner method, then I can't access this in the Button method.
So I feel like I have too many methods? So is there a way to pass data from one method to another method, or do I have to cancel some methods? What would be the easiest way to set this up?
I also made an onItemSelected to make some toasts, which worked. Can I use OnItemSelected somehow to create variables or initiate a data transfer to another Activity?
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
TextView myText = (TextView) view;
switch (parent.getId()) {
case R.id.spinner_relativePosition:
makeText(NewHandActivity.this, "Relative Position is " + myText.getText(), Toast.LENGTH_SHORT).show();
break;
case R.id.spinner_absolutePosition:
makeText(NewHandActivity.this, "Absolute Position is " + myText.getText(), Toast.LENGTH_SHORT).show();
break;
I'm very new to coding, and I just can't figure out the logic how I get the Spinner methods, Button/iniate method and OnItemSelected method to work together and exchange variables. Would appreciate if someone can point me in the right direction. Have already browsed the internet a day or so to find an answer, with no success.
I like your idea of separating out the code to create each spinner into its own method. However, if you are copying and pasting the whole method and making a few changes, you should step back and think about how you can do it even more easily. Often the changes you make after copy and paste give a hint that you should add some parameters to the method. If you do it correctly, you can write just a single method and then copy and paste the method call instead of the entire method and then make appropriate changes to the arguments.
As for your actual question, you are making this much more complicated than necessary. Specifically, you do not need to setOnItemSelectedListener() on any of the Spinners. Instead, the OnClickListener for the button should just get the selected item from each spinner and send it to the new activity in the Intent.
I used to dynamically populate a Table Layout with different elements per row, among them a Spinner.
To handle the initialization of spinner values i used what most recommended: a boolean flag variable. The code went something like this
public class spinnerHandling implements OnItemSelectedListener{
public void dynamicallyPopulateTableLayout(int a){
...
//initialize spinner with default values
mySpinner.setSelection(a);
mySpinner.setTag(true);
mySpinner.setOnItemSelectedListener(this);
...
}
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position,
long id) {
if(!(Boolean)parent.getTag()){
//my code
...
}
parent.setTag(false);
}
}
This worked fine.
Later i decided that i should use a List View instead of a Table Layout. For that i have a custom ArrayAdapter that uses a row Layout, in which of course i have a Spinner, to populate the List View. As those of you who are familiar with List Views, understand its complex dynamic behavior, would know that the somehow static turn around of the boolean flag method wont work.
After three days of tremendous headache trying to find a way to resolve this issue, i just came across the spinner's isDirty() method that in contrast with isPressed(), isSelected(), etc. actually changes its boolean values when spinner item is selected by user.
the code is something like this:
public class spinnerHandling implements OnItemSelectedListener{
public void dynamicallyPopulateListView(int a){
...
//initialize spinner with default values
mySpinner.setSelection(a);
mySpinner.setOnItemSelectedListener(this);
...
}
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position,
long id) {
if(!parent.isDirty()){
//my code
...
}
}
}
This seems to work fine!
But i would like to make sure that isDirty() handles properly spinner initialization and user selection.
Can you please confirm or contradict this behavior?
Thank you.
Let me explain myself:
As you know, when you've a view which have to be inflated several times, but changing values, you use a GridView or a ListView. Those two Composite views, have some methods like onItemClick. This method is so useful, as it returns the position of the view clicked.
With this position you can perform some concrete tasks, like retreiving from an ArrayList, the information of that object. Here's an example:
ArrayList<DocumentInfo> documents;
And when you set a setOnItemClickListener() you can get the correct values:
gallery.setOnItemClickListener(new OnItemClickListener(){
#Override
public void onItemClick(AdapterView<?> arg0, View v, int pos, long arg3) {
getDocumentInfoOf(pos);
}
});
public void getDocumentInfoOf(int position){
DocumentInfo doc = documents.get(position);
}
However, when you aren't using a GridView or a ListView, you're in your own. You don't have a clear way (AFAIK) to know which layout inflated is the one clicked (I mean like the previous example, the "position" value).
What I am currently doing, is the following:
for (int i=0; i<10;i++){
RelativeLayout documentInflated = (RelativeLayout) this.mInflater.inflate(R.layout.open_document_per_inflar, null);
documentInflated.setContentDescription(""+i);
documentInflated.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View v) {
openDocument(v);
}
});
container.addView(documentInflated);
}
public void openDocument(View v){
int idDocument = Integer.parseInt(v.getContentDescription());
//idDocument is the view clicked
}
Do you guys think this is a clear way of doing this?
Thank you!!!
If I'm not mistaken you want to get some data from your created Relative Layout when you click on it. The best solution here is to use the method setTag(Object tag). After that you get the informatiom with the method getTag(). This method allows you to add extra information to your view. As it says in the documentation:
Tags
Unlike IDs, tags are not used to identify views. Tags are essentially an extra piece of information that can be associated with a view. They are most often used as a convenience to store data related to views in the views themselves rather than by putting them in a separate structure.
Also depending on your needs you can seperate every tag with a key -> value pair with the method setTag(int key, Object tag), after that you can retrieve this object with getTag(int key);
So in your case you will have
documentInflated.setTag(i)
in the onClick yo will then have:
int i = (int)v.getTag();
Unless I'm not understanding your desire...
#Override
public void onClick(View v) {
openDocument(v);
}
v IS the view being clicked. Your code looks like it should do what you're hoping it will do. What are you actually seeing happen?
I have an intent launched activity with a number of spinners on the page. I've just set up my spinner's ItemSelected listeners, following this guide. The problem is, the first item in each Spinner is basically "Please select", just so it's not a blank box: so my ItemSelected Listener detects the fact that Please select is in the spinner and seems to assume that it was selected rather than loaded by default. Ideally I want the listener to only detect when an actual choice is made. What is the best way to ignore the default selection?
Here's the relevant code:
ageSpinner = (Spinner) findViewById(R.id.ageSpinner);
ageAdapter = ArrayAdapter.createFromResource
(this, R.array.ageArray, android.R.layout.simple_spinner_item);
ageAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
ageSpinner.setAdapter(ageAdapter);
ageSpinner.setOnItemSelectedListener(new MyOnItemSelectedListener());
public class MyOnItemSelectedListener implements OnItemSelectedListener {
public void onItemSelected(AdapterView<?> parent,
View view, int pos, long id) {
Toast.makeText(parent.getContext(), "The planet is " +
parent.getItemAtPosition(pos).toString(), Toast.LENGTH_LONG).show();
}
public void onNothingSelected(AdapterView parent) {
// Do nothing.
}
}
The problem is that the selection is done on the first layout phase. So the first time your layout is computed, the Spinner raises onItemSelected. Its quite annoying, but its the way it works. You could try a couple different things, but given you already have Please Select Something items, the best you could do is ignore the event when the selected item is 0.
Set a boolean to false, check it in the onItemSelected and if false, set it to true and do nothing else. Next time, when it's true do as you normally would.
There are lots of approaches possible here. For example, you can override the normal behaviour of a Spinner using reflection, or simply switch to a Button imitating a Spinner, which on clicking on it pops up an AlertDialog.
However, other solutions can easier to implement. Barak has named one, while an alternative would be to simply check the selected position in the Spinner, assuming that "Please select" will always be the first item (and hence, at position 0) and you're not doing any sorting on the items.
I'm sure you'll find one of the possibilities suitable for your problem, but perhaps you can also a elaborate a bit more on why exactly it's problematic onItemSelected(...) gets fired for the initial selection?
Solution by OP.
public class MyOnItemSelectedListener implements OnItemSelectedListener {
public void onItemSelected(AdapterView<?> parent,
View view, int pos, long id) {
if (pos != 0) {
//this if makes sure it ignores 0, which is
//Please Select in the drop down
Toast.makeText(parent.getContext(), parent.getTag().toString() +
parent.getItemAtPosition(pos).toString(), Toast.LENGTH_LONG).show();
}
}
public void onNothingSelected(AdapterView parent) {
// Do nothing.
}
}
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;
}
}
}