I want to hide spinner prompt popup on outside click. If prompt popup is open and user press home key activity will minimize so when user again open application that prompt popup should disappear.
Is there any way to achieve this.
Thank You
Edit:-- Prompt popup is not customized. So I can't hide them in onPause or onResume methods.
Based on Andro's answer, you may prefer reflection to be able to call the protected method onDetachedFromWindow. Then you don't have to subclass Spinner, adapt the layout, etc.
/**
* Hides a spinner's drop down.
*/
public static void hideSpinnerDropDown(Spinner spinner) {
try {
Method method = Spinner.class.getDeclaredMethod("onDetachedFromWindow");
method.setAccessible(true);
method.invoke(spinner);
} catch (Exception e) {
e.printStackTrace();
}
}
Well its a little complicated than I thought.
I am adding the step by step details here. Try to follow it. I was able to achieve this in api level 10.
And this solution assumes that you are supposed to close the prompt dialog programatically when the user clicks on Home Button or If you had to move to next activity without user interaction
The first step is to create a Custom Spinner by extending Spinner Class.
Let's say, I have created a class called CustomSpinner in the package com.bts.sampleapp
My CustomSpinner class looks like this,
package com.bts.sampleapp;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.Spinner;
public class CustomSpinner extends Spinner{
Context context=null;
public CustomSpinner(Context context) {
super(context);
this.context=context;
}
public CustomSpinner(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public CustomSpinner(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
}
}
Now in your Xml file, replace Spinner element by this custom spinner,
<com.bts.sampleapp.CustomSpinner
android:id="#+id/spin"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
The next step is to initialize and set adapter to this spinner in your Activity class,
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
CustomSpinner spin=null;
spin=(CustomSpinner)findViewById(R.id.spin);
spin.setAdapter(spinnerAdapter); //you can set your adapter here.
}
The final step is to close the dialog when the user clicks on HomeButton or When the Activity moves to background. To do this, we override the onPause() like this,
#Override
protected void onPause() {
Log.i("Life Cycle", "onPause");
spin.onDetachedFromWindow();
super.onPause();
}
Now within the onPause() call the method spin.onDetachedFromWindow(); which does the job of closing the prompt dialog for you.
Also calling spin.onDetachedFromWindow(); from anywhere within the Acitivity should close the Spinner prompt dialog if it is open.
You can popup Activity as Dialog Theme.
Override onPause method.
protected void onPause (){
super.onPause ();
this.finish();
}
spinner.clearFocus();
this is simple line to close spinner programitically
Related
I have 36 spinners that I have initialized with some values. I have used onItemSelectedListener with them. As usual, the user can interact with these spinners, firing the onItemSeected function.
One problem is that the call is made during init, but I found solutions to it here and avoided that using a global variable "count" and checking if count > 36 before executing code inside onItemSelected.
My problem is this:
The user has the option to click on a button called "Previous", upon which I have to reset SOME of the spinner values.
I tried changing the value of count to 0 before resetting the spinners, and then changing it back to 37 after resetting, but I have come to understand that the onItemSelected is called only after every other function is done executing, so it is called AFTER count is changed back to 37 even though the spinner values are set as soon as they are selected by user.
I need to repeatedly refresh some spinners WITHOUT firing off the onItemSelected function. Can anyone please help me find a solution? Thanks.
I found a simple and, I think, elegant solution.
Using tags.
I first created a new XML file called 'tags' and put in the following code:
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<item name="pos" type="id" />
</resources>
Whenever I myself use spin.setSelection(pos), I also do spin.setTag(R.id.pos, pos), so I am setting the current position as a tag.
Then, in onItemSelected, I am executing code only if(spin.getTag(R.id.pos) != position), where position is the position variable supplied by the function.
In this way, my code is executed only when the user is making a selection.
Since the user has made a selection, the tag has not been updated, so after the processing is done, I update the tag as spin.setTag(R.id.pos, position).
NOTE: It is important to use the same adapter throughout, or the "position" variable might point to different elements.
EDIT: As kaciula pointed out, if you're not using multiple tags, you can use the simpler version, that is spin.setTag(pos) and spin.getTag() WITHOUT the need for an XML file.
When Spinner.setSelection(position) is used, it always activates setOnItemSelectedListener()
To avoid firing the code twice I use this solution:
private Boolean mIsSpinnerFirstCall = true;
...
Spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
//If a new value is selected (avoid activating on setSelection())
if(!mIsSpinnerFirstCall) {
// Your code goes gere
}
mIsSpinnerFirstCall = false;
}
public void onNothingSelected(AdapterView<?> arg0) {
}
});
I don't know if this solution is as foolproof as the chosen one here, but it works well for me and seems even simpler:
boolean executeOnItemSelected = false;
spinner.setSelection(pos)
And then in the OnItemSelectedListener
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if(executeOnItemSelected){
//Perform desired action
} else {
executeOnItemSelected = true;
}
}
The way I solved this is by saving the OnItemSelectedListener first. Then set the OnItemSelectedListener of the Spinner to the null value. After setting the item in the Spinner by code, restore the OnItemSelectedListener again. This worked for me.
See code below:
// disable the onItemClickListener before changing the selection by code. Set it back again afterwards
AdapterView.OnItemSelectedListener onItemSelectedListener = historyPeriodSpinner.getOnItemSelectedListener();
historyPeriodSpinner.setOnItemSelectedListener(null);
historyPeriodSpinner.setSelection(0);
historyPeriodSpinner.setOnItemSelectedListener(onItemSelectedListener);
Here's my solution to this problem. I extend AppCompatSpinner and add a method pgmSetSelection(int pos) that allows programmatic selection setting without triggering a selection callback. I've coded this with RxJava so that the selection events are delivered via an Observable.
package com.controlj.view;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.AdapterView;
import io.reactivex.Observable;
/**
* Created by clyde on 22/11/17.
*/
public class FilteredSpinner extends android.support.v7.widget.AppCompatSpinner {
private int lastSelection = INVALID_POSITION;
public void pgmSetSelection(int i) {
lastSelection = i;
setSelection(i);
}
/**
* Observe item selections within this spinner. Events will not be delivered if they were triggered
* by a call to setSelection(). Selection of nothing will return an event equal to INVALID_POSITION
*
* #return an Observable delivering selection events
*/
public Observable<Integer> observeSelections() {
return Observable.create(emitter -> {
setOnItemSelectedListener(new OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
if(i != lastSelection) {
lastSelection = i;
emitter.onNext(i);
}
}
#Override
public void onNothingSelected(AdapterView<?> adapterView) {
onItemSelected(adapterView, null, INVALID_POSITION, 0);
}
});
});
}
public FilteredSpinner(Context context) {
super(context);
}
public FilteredSpinner(Context context, int mode) {
super(context, mode);
}
public FilteredSpinner(Context context, AttributeSet attrs) {
super(context, attrs);
}
public FilteredSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public FilteredSpinner(Context context, AttributeSet attrs, int defStyleAttr, int mode) {
super(context, attrs, defStyleAttr, mode);
}
}
An example of its usage, called in onCreateView() in a Fragment for example:
mySpinner = view.findViewById(R.id.history);
mySpinner.observeSelections()
.subscribe(this::setSelection);
where setSelection() is a method in the enclosing view that looks like this, and which is called both from user selection events via the Observable and also elsewhere programmatically, so the logic for handling selections is common to both selection methods.
private void setSelection(int position) {
if(adapter.isEmpty())
position = INVALID_POSITION;
else if(position >= adapter.getCount())
position = adapter.getCount() - 1;
MyData result = null;
mySpinner.pgmSetSelection(position);
if(position != INVALID_POSITION) {
result = adapter.getItem(position);
}
display(result); // show the selected item somewhere
}
I'm developing an application in which I have a dashboard with 4 buttons. Each button starts a new Intent to a different Activity. Each Activity inflates the same layout that consists of the custom view. In the custom view is a ViewPager. In the ViewPager I want to display a strings that resides in the arrays in the mentioned above activities. Should I pass those arrays to the custom view and display it in the ViewPager? If so how can I pass them? How the Custom View would know from which activity they come from? I know that the 'sender' activity will be the one that is currently running but how can I check it? Or should I just make the arrays static and easily access them? In this case I would also need to know which activity to access. Please advise or maybe there is a better way of implementing it. Thank you.
Intents let you start other activities, also allows you to pass primitives, primitive arrays/Lists as well as custom Parcelable objects to other activities.
You can pass array data, starter activity name etc here. Or you can use getCallingActivity() to know the sender activity.
UPDATE:
You can pass data to custom views in onCreate() , if You have setter methods in Custom View class.
For more complex communication, Interface your activity to its child view as shown:
Interface:
public interface CustomViewParent {
//----add as many communication methods you want---
public String[] getData();
public void doSomething();
}
Activity:
public class MyActivity extends Activity implements CustomViewParent {
// ------------------------ INTERFACE METHODS ------------------------
// --------------------- Interface CustomViewParent ---------------------
#Override
public String[] getData() {
return new String[]{"data1","data2"};
}
#Override
public void doSomething() {
Toast.makeText(this,"Custom view called me !",Toast.LENGTH_SHORT).show();
}
// -------------------------- OTHER METHODS --------------------------
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//----here we set the parent--
((CustomView)findViewById(R.id.my_custom_view)).setParent(this);
}
}
Accept and use this interface in Custom View:
public class CustomView extends ViewPager {
// ------------------------------ FIELDS ------------------------------
private CustomViewParent parent;
// --------------------------- CONSTRUCTORS ---------------------------
public CustomView(Context context) {
super(context);
init();
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
// --------------------- GETTER / SETTER METHODS ---------------------
public void setParent(CustomViewParent parent) {
this.parent = parent;
}
// ---------------------------- INITIALIZE-----------------------------
private void init() {
//-----initialize/inflate custom views-----
//-----calls to parent, !! do not forget the null check !! ----
if(parent != null){
String[] data = parent.getData();
parent.doSomething();
}
//----add data from parent to Views etc---
}
}
In my application I want to fill the sliding draw only when data is not null.
So , when the user presses on the sliding drawer handler it should give a dialog "Data is empty" and sliding drawer should not open.
I have tried Handler.setOnTouchListener() but what happens is anywhere a touch event is called that dialog box appear.
Any suggestions?
You should take a look at the method setOnDrawerOpenListener.
Create your own OnDrawerOpenListener and use it to check if the data is null - if it is, close the drawer again:
private class OnDrawerOpened implements OnDrawerOpenListener {
#Override
public void onDrawerOpened() {
if( data == null )
mySlidingDrawer.close();
}
}
You would need a reference to your SlidingDrawer and you would need OnDrawerOpened to be an inner class, so you can access the reference to your SlidingDrawer.
Alternative
You could also create your own subclass by extending SlidingDrawer and then override the animateOpen() and open() methods (you might need to override animateToggle() and toggle() too):
public class MySlidingDrawer extends SlidingDrawer {
public MySlidingDrawer( Context context, AttributeSet attrs ) {
super( context, attrs );
}
#Override
public void animateOpen() {
if( data != null )
super.animateOpen();
else
//Show dialog
}
#Override
public void open() {
if( data != null )
super.open();
else
//Show dialog
}
}
If you do this, you would need to reference your own implementation of the SlidingDrawer in your XML:
<com.myPackage.MySlidingDrawer>
...
</com.myPackage.MySlidingDrawer>
Depeding on how and when you get the data to check for null, you might want to either pass it to MySlidingDrawer in its constructor or add a method through which you can set the data.
I have an activity with a spinner, and I was wondering if it is possible to close the spinner programmatically, if the user has opened it.
The whole story is that in the background I am running a process on a separate thread. When the process has finished, I invoke a Handler on the main activity and, depending on the outcome, I perform some tasks. It is then that I want to close the spinner, it the user has opened it.
The spinner is in the main.xml layout:
<Spinner android:id="#+id/birthPlaceSpinner" android:layout_weight="1"
android:layout_height="wrap_content" android:prompt="#string/select"
android:layout_width="fill_parent" />
and this is the Handler:
private class BirthplaceChangedHandler extends Handler {
#Override
public void handleMessage(Message msg) {
String placeFilterStr = birthPlaceFilterText.getText().toString();
if ("".equals(placeFilterStr) || placeFilterStr == null || validNewAddresses.isEmpty()) {
birthPlaceSpinner.setEnabled(false);
hideGeoLocationInformation();
} else {
birthPlaceSpinner.setEnabled(true);
}
adapter = new ArrayAdapter<String>(getApplicationContext(), R.layout.multiline_spinner_dropdown_item, validNewAddressesStr)
birthPlaceSpinner.setAdapter(adapter);
}
}
Cheers!
public static void hideSpinnerDropDown(Spinner spinner) {
try {
Method method = Spinner.class.getDeclaredMethod("onDetachedFromWindow");
method.setAccessible(true);
method.invoke(spinner);
} catch (Exception e) {
e.printStackTrace();
}
}
This works for me:
class SomeAdapter extends BaseAdapter implements SpinnerAdapter {
//......
public View getDropDownView(int position, View convertView, ViewGroup parent) {
View view = convertView;
if (view == null) {
//......
}
view.setOnClickListener(new ItemOnClickListener(parent));
return view;
}
//.....
}
and the click listener:
class ItemOnClickListener implements View.OnClickListener {
private View _parent;
public ItemOnClickListener(ViewGroup parent) {
_parent = parent;
}
#Override
public void onClick(View view) {
//.......
// close the dropdown
View root = _parent.getRootView();
root.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK));
root.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK));
}
}
Well its a little complicated than I thought.
I am adding the step by step details here. Try to follow it. I was able to achieve this in api level 10.
And this solution assumes that you are supposed to close the prompt dialog programatically when the user clicks on Home Button or If you had to move to next activity without user interaction
The first step is to create a Custom Spinner by extending Spinner Class.
Let's say, I have created a class called CustomSpinner in the package com.bts.sampleapp
My CustomSpinner class looks like this,
package com.bts.sampleapp;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.Spinner;
public class CustomSpinner extends Spinner{
Context context=null;
public CustomSpinner(Context context) {
super(context);
this.context=context;
}
public CustomSpinner(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public CustomSpinner(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
}
}
Now in your Xml file, replace Spinner element by this custom spinner,
<com.bts.sampleapp.CustomSpinner
android:id="#+id/spin"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
The next step is to initialize and set adapter to this spinner in your Activity class,
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
CustomSpinner spin=null;
spin=(CustomSpinner)findViewById(R.id.spin);
spin.setAdapter(spinnerAdapter); //you can set your adapter here.
}
The final step is to close the dialog when the user clicks on HomeButton or When the Activity moves to background. To do this, we override the onPause() like this,
#Override
protected void onPause() {
Log.i("Life Cycle", "onPause");
spin.onDetachedFromWindow();
super.onPause();
}
Now within the onPause() call the method spin.onDetachedFromWindow(); which does the job of closing the prompt dialog for you.
Now that being said, calling spin.onDetachedFromWindow(); from anywhere in your Activity should help you to close the spinner programatically. So if this is what you want, then remove the onpause().
I don't see a way to accomplish that -- there is no method on Spinner to close it. The "open" part of a Spinner is an AlertDialog on Android 1.x and 2.x, and I'm not completely sure how it is implemented on Honeycomb when using the holographic theme.
The only workaround would be to clone the source for Spinner and add in some code yourself to dismiss the dialog. But, again, it would not work on Honeycomb or higher until you can see and clone that code as well.
Beyond that, I would think that what you want is poor UX. If the user opened the Spinner, they are most likely actively examining the Spinner's contents and making a selection. Yanking that out from under their finger will confuse them, at best. Please consider an alternative approach.
Also, don't use getApplicationContext() unless you know why you are using getApplicationContext(). You do not need or even want getApplicationContext() when creating an ArrayAdapter.
I think you should scrap your use of Spinner and instead use an ImageView with a Frame Animation (i.e. <animation-list>) to create your own spinner. You just set the ImageView's background to be your Frame Animation drawable.
Then you can easily do something like this to start and stop it.
You would like to close your spinners from anywhere. Key injection for BACK pressed is the good solution but, here you are closing all the views at once.
How about setPressed(false)?
Link:
Close Spinners dropdown when two among all in a groupview are clicked simultaneously
Otherwise:
Try to make the Spinner focusable and focusableInTouchMode, and use clearFocus()
on it. Try to focus on the view below it using requestFocus() method.
Check if the spinner drop-down closes
Use the clearFocus() to close the spinner programitically
spinner.clearFocus();
Add clearfocus() in code.
Spinner spinner = (Spinner) findViewById(R.id.spinner);
spinner.clearFocus();
Use transparent background in xml
android:background="#android:color/transparent
<Spinner
android:id="#+id/spinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#android:color/transparent"
android:clickable="false"
android:focusable="?android:attr/windowOverscan"
android:focusableInTouchMode="false"
android:pointerIcon="arrow"
android:spinnerMode="dialog"
android:theme="#style/ThemeOverlay.AppCompat.Light" />
Koltin Reflection
fun AppCompatSpinner.dismiss() {
val popup = AppCompatSpinner::class.java.getDeclaredField("mPopup")
popup.isAccessible = true
val listPopupWindow = popup.get(this) as ListPopupWindow
listPopupWindow.dismiss()
}
I don't manage to set a neither a title nor a (title) icon to my custom alert dialog.
My code:
public class AddingFavoriteDialog extends AlertDialog {
private OnAddingFavoriteListener onAddingFavoriteListener;
private Context context;
private GeocodingManager geocodingManager;
private FavoritesActivity favoritesActivity;
public AddingFavoriteDialog(Context context, OnAddingFavoriteListener onAddingFavoriteListener) {
super(context, android.R.style.Theme_Dialog);
this.context = context;
this.onAddingFavoriteListener = onAddingFavoriteListener;
this.geocodingManager = new GeocodingManager(context);
this.favoritesActivity = (FavoritesActivity) context;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.adding_favorite_dialog2);
setTitle("MYTITLE");
setIcon(R.drawable.star_gold);
}
What am i doing wrong? I also tried to set it by calling super.setTitle("MYTITLE"); in onCreate() as well as in the constructor.
EDIT: Even setButton(BUTTON_POSITIVE, context.getString(R.string.button_value_OK),
new OnClickListener() {...} seems not to work.
Use setView instead of setContentView, because setContentView replaces everything in the AlertDialog, including the default title bar and icon (and buttons etc.). Instead, setView only replaces the middle part (the message, if you will).
Use LayoutInflater if you need to.
Extending Dialog instead of AlertDialog will fix the problem.