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.
Related
I have one activity which is hosting several tabs(Fragment), now I want to get EditText by id from tab(Fragment) to its hosting activity and set listener on it.
So please help me to get rid of it.
Thanks in advance
To find view by id from fragment, add this:
EditText edittext = getActivity().findViewById(R.id.edittext);
The simple solution is
EditText editText = (EditText)getActivity().findViewById(R.id.viewid);
if( editText != null){
// set listener here
}
In your Activity
public EditText editText;
if(editText != null)
editText.setOnClickListner(this);
In your Fragment
Activity activity = (Activity)context;
//where context is your fragment's context
activity.edtText = (EditText)findViewById(R.id.viewid);
Make sure you set listener when editText is not null otherwise you will get null pointer exeption.
There can be multiple solutions to this. In my opinion the best way is to make an interface named Callback as below
public interface Callback
{
public void onListenerActionPerformed(Some args);
}
Now in whatever fragment you want the listener to be attached, write a function
private Callback callback;
public void setCallback(Callback callback)
{
this.callback=callback;
}
Now register the listener on your EditText in the fragment and inside the body of the corresponding function write
callback.onListenerActionPerformed(Some parameter)
Now from your activity, right below where you have instantiated the fragment write,
fragmentInstanceName.setCallback(new Callback(){
public void onListenerActionPerformed(Some args){
//Your implementation
}
});
I have a Button in a ListViewItem in a ListView within a Fragment. I have code that successfully notifies the host Activity of the button being tapped. While the code works, I want to make sure that this is the best design pattern to use here.
Here is a summary of the code:
The Activity (MainActivity) passes a reference of itself (this) to the Fragment in a variable called mainActivityReference.
The Fragment passes this reference to the ArrayAdapter object in a variable also called mainActivityReference.
In the ArrayAdapter getView method, I set the onClickListener and call a method within the mainActivityReference with the index position of the item as a parameter as follows:
viewHolder.soundButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d(TAG, "Sound button tapped at position " + position);
mainActivityReference.chooseSoundForIndex(position);
}
});
Does this seem kosher? Or should I use something like a LocalBroadcastManager?
The basic strategy of passing an "owner" object to an adapter is quite kosher, and very common. Often I will make a listener interface, and have the parent activity or fragment implement the interface. Since it is your activity that is responding to the click, you wouldn't even need to pass the reference to the fragment and then the adapter. You could just get the View's context, and check if it implements the interface. Like this:
public interface SoundChooser {
void chooseSoundForIndex(int position);
}
viewHolder.soundButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d(TAG, "Sound button tapped at position " + position);
Context context = v.getContext();
if (context instanceof SoundChooser) {
((SoundChooser)context).chooseSoundForIndex(position);
} else {
Log.w(TAG, "Activity should implement SoundChooser:" + context.getClass().getName());
}
}
});
I have a Custom BaseAdapter showing a ListView containing a TextView and a CheckBox. The TextView has an OnClickListener implemented to perform a specific action when clicked on the text. Within the Adapter I have a OnCheckedChangeListener registered which keeps track of which item is checked (because of the ListView recycling).
I want to start an ActionMode when I check a CheckBox and stop it when I uncheck it.
How can I inform the main activity hosting the adapter that a check has been made?
Create a listener call back interface, something like:
public interface ListenerCheckBox
{
public void onRowChecked(int rowNun);
}
Then, make your main activity implement this listener:
public class ActivityMain implements ListenerCheckBox
Then, when you instantiate your custom BaseAdapter, pass in the listener:
//kv 3rd parameter would be listener
CustomBaseAdapter customBaseAdapter = new CustomBaseAdapter(this, items, this);
Then, in the constructor of your CustomBaseAdapter, set a member field to the listener:
public CustomBaseAdapter(Context context, ArrayList<Item> items, ListenerCheckBox listenerCheckBox)
{
mListenerCheckBox = listenerCheckBox;
...
}
Then, every time an item is checked, call:
mListenerCheckBox.onRowChecked(rowNum);
Here is a Android Studio project that shows an example of how to do what you are asking. In particular, check out the check listener and its onClick():
#Override
public void onClick(View v) {
if (mMode != null) {
mMode = mListView.startActionMode(new ExampleMultiChoiceModeListener(mActivity));
}
mListView.setItemChecked(mPosition, ((Checkable) v).isChecked());
}
DogActivity is using a custom View. The custom view handles some logic and so has fields. When a particular field reaches a certain value, I want to start a fragment whose parent is DogActivity. How would I do that?
Is it advisable to put a callback inside a custom view so that it calls its parent activity? Or is there a simpler way?
When programming you should always look for consistency, i.e. look around you and see how similar stuff to what you want to do is done. The Android SDK makes heavy use of callback listeners, so they are the way to go here.
In fact we don't even need to know what kind of View your CustomView really is, we can build a general purpose solution. Don't forget to adapt/optimize according to your specific surroundings however. And think about abstraction and generalisation once you get to a point where all your Views are spammed with listeners!
You will need 3 things:
A listener interface
public interface OnCountReachedListener {
public void onCountReached();
}
A place to accept the listener and a place to alert the listener in your CustomView
public class CustomView extends View {
private int theCount;
private OnCountReachedListener mListener;
public void setOnCountReachedListener(OnCountReachedListener listener) {
mListener = listener;
}
private void doSomething() {
while (theCount < 100) {
theCount++;
}
// The count is where we want it, alert the listener!
if (mListener != null) {
mListener.onCountReached();
}
}
An implementation of the interface in your Activity
public class DogActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
View myView = new CustomView();
myView.setOnCountReachedListener(new OnCountReachedListener() {
#Override
public void onCountReached() {
Log.w("tag", "COUNT REACHED!");
// START YOUR FRAGMENT TRANSACTION HERE
}
});
}
}
For further information look at the source code of the View class and all the On**XY**Listener interfaces in the Android SDK. They should give you plenty to think about
What is the type of the field? Is it an EditText? SeekBar? Depending on the View, you'll be able to specify different listeners/callbacks to determine when they have changed and if they've reached a certain threshold. I would attach these listeners within onCreate of DogActivity. When the threshold is reached, use a FragmentTransaction to add your Fragment as the child of a container View in DogActivity.
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()
}