I want to be able to block all UI interaction with a fragment until a callback occurs.
I have two buttons: ButtonA and ButtonB.
ButtonA shows a progress bar and kicks-off an asynchronous thread that calls-back to the fragment when it's done. In the meantime, someone can press ButtonB which I don't want to allow.
My solution was to spin up another fragment which is transparent and intercepts all clicks. However there appears to be delay between FragmentManagers commit() and the fragment actually working.
I've tried calling executePendingTransactions() but I still end up with threading issues whereby the fragment isn't in a state to accept onClick events before the user hits ButtonB.
Is there a more elegant solution?
Thanks,
John
Another option is to use a progress dialog fragment and set it to be non cancelable. It will cover the fragment and prevent the underlying fragment from receiving any touch event.
Instead of calling another fragment,u can have another tranparent view with a progress dialog above the present view and make its visibility VIEW or GONE accordingly.Else u can simply show a prgress dialog with cancelable parameter as false.
Call buttonB.setEnabled(false); after clicking buttonA.
CustomButton extends View {
private boolean mIsEnabled = true;
public void setEnabled (boolean enabled) {
this.mIsEnabled = enabled;
}
#Override
public void onClick() {
if (mIsEnabled) {
mOnClickListener.onClick();
} else {
return;
}
}
}
I didnt understood the question perfectly..
hope it may helps you.
when you adding transaprent fragment over it make the transparent layout clickable=true
if a view is mentioned as clickable it does not pass touch events to below views.
sorry if i understtod your question wrong.
Can't button A put its containing activity in a given state (with a boolean flag raised in its listener) and button B read that flag before doing any stuff ?
I think it's not only a UI issue here but also some presentation logic and mini-state machine that you should implement. This mechanism plus the fragment you already have should be enough to prevent gaps in the sequence of executions of UI Thread events.
Related
I need to show a preference dialog that wait for Joypad keypress.
I know that DialogFragment has its own Window then has its own onKeyListener.
I can easily catch Joypad press by setting a listener like that.
public class MyDialogFragment extends PreferenceDialogFragmentCompat {
#Override
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
super.onPrepareDialogBuilder(builder);
builder.setOnKeyListener(new DialogInterface.OnKeyListener() {
#Override
public boolean onKey(DialogInterface dialogInterface, int i, KeyEvent keyEvent) {
// do stuff with intercepted key press
return true;
}
});
}
}
But, I have trouble catching GenericMotionEvents. In order to intercept them, I've overridden onGenericMotionEvents in the Activity class and eventually forward the Event by calling a method on MyDialogFragment class.
It works 100% correctly when MyDialogFragment is not shown as when an analog trigger/direction stick is moved, I can get an event.
The weird part is that IF MyDialogFragment is shown, then I can get only analog direction stick events BUT NOT left/right analog triggers events.
Does someone know why and how to fix this behaviuor?
I've had a similar issue some time ago. You can use onGenericMotionEvent of Dialog or even some of the Views. It has some limitations though and works not as expected sometimes. It does work as intended though - it is just that sometimes all the generic motion events are being intercepted by something else and they aren't propagated any further anywhere - in this situation, you won't receive the callback trigger.
That is what was happening in my case and even overriding the callback method in Dialog(I haven't tried View though) failed to give me the needed result. What I did is a bit of a kludgy trick, but it did the job. I created my activity's UI as a child of one parent FrameLayout and my dialog UI was the topmost element in this FrameLayout. This trick allowed me to use the activity's native onGenericMotionEvent callback. It added some navigation handling overhead and was generally possible because of allowing UI design(without dialog shadow etc) but yeah...
Maybe some of these approaches will help you.
I have an activity which have multiple piece of UI panel(you can think them as view in android), these panels will be invisible by default.
Now when user trigger action1, PanelA will display, when trigger action2, PanelB will display(at different location of the screen).
Both PanelA and PanelB is visible at the moment, now when user hit the back menu, the PanelB should disappear, and PanelA should disappear when hit the back menu again.
At first, I use View to hold different panels, however I found it is difficult to keep the state consist, for example, the activity will be a little different between PanelA and PanelB.
Then I found the fragment, however after I tried and tested, I found that the addTobackStack() can not apply to my us-case, since the PanelA and PanelB are at different location, android can not save their state by default.
So I wonder if there is any other solution for my requirement?
You need to manually handle this scenario inside onBackPressed() method of an Activity.
For instance -
#Override
public void onBackPressed() {
if (panelB.isOpened()) {
panelB.close()
} else if (panelA.isOpened()) {
panelA.close()
} else {
super.onBackPressed();
}
}
When panelB is opened, it will close only panelB and wont do anything else. Same goes for panelA, if its opened and when both the panel are closed then it will exit the app like normal.
I highly recommend to use DialogFragments here as you can call show() and dismiss() any point of time on that and they can handle custom views pretty well.
Hope it helps.
Basically, I have a list of detail fragments and each one represents a list of peers of that phone. When one of these DeviceDetails is tapped on, a selection of buttons and text appears. Which of these buttons and text appears depends on the status of the phone; it is either connected to the phone being used, or it is available for connection.
I currently use the fragment's onCreateView to make 3 buttons appear. 1 of these buttons should appear every time, and the other 2 alternate depending on the connected/available state.
I am trying to figure out which overridden fragment method should handle the changing UI's. It should just be a case of if statements (or maybe switch statements?) but I am not sure where to place these?
Well if let's say you have an Activity and it is hosting all these Fragments.
I assume there is some event that triggers this state to happen, maybe in that Activity
public void onSomeEventThatICareAbout(EventDetails deets) {
Fragment fragment = getFragmentManager().findFragmentById(R.id.my_fragment_with_buttons);
if (fragment != null) {
((MyButtonsFragment)).disableButtons(deets);
}
}
Basically just treat the fragment like any other component and call methods on it based on events like normal, whether it is an onClick(), a AsyncTask callback, or whatever. Just call the function right on the fragment.
Define your own way for your fragment to do what you want,
public void disableButtons(EventDetails deets) {
View view = getView();
view.findViewById(R.id.button1).setEnabled(false);
view.findViewById(R.id.button2).setEnabled(false);
}
Hi everybody =) i am a new android developer and i need a help about dismissing fragment.
My application have an login fragment and when the user touch the outside of it i want to hide login fragment. How can i make this? OnTouchEvent() method may be useful or not?
Please say something. Thanks =)
place the login layout inside a transparent, full screen layout, and detect touch events on the larger layout.
I think better way is removing fragment in order to release memory resources.
My solution is having having this method in fragment:
private void closeFragment() {
getActivity().getSupportFragmentManager().beginTransaction().remove(YOUR_FRAGMENT.this).commit();
}
Hi again =) i solve this problem using OnTouchListener on my Homepage activity.I have an gridviews background in my homepage layout and if the user doesn't login, onTouch() method runs.When the login fragment is visible and the user touch outside of it my hideLoginFragment() method calling for dismissing the fragment..
gridView = (ShelvesView) findViewById(R.id.grid_shelves);
gridView.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (application.getDbManager().getUser().key.equals("-1")){
hideLoginFragment();
loginButton.setVisibility(View.VISIBLE);
exitButton.setVisibility(View.INVISIBLE);}
return false;
}
});
I started using DialogFragment, because they are working nicely through orientation changes, and stuff. But there is nasty problem I encountered.
I have AsyncTask that shows progress DialogFragment and dismisses it onPostExecute. Everything works fine, except when onPostExecute happens while application is in background (after pressing Home button, for example). Then I got this error on DialogFragment dismissing - "Can not perform this action after onSaveInstanceState". Doh. Regular dialogs works just fine. But not FragmentDialog.
So I wonder, what is the proper way of dismissing DialogFragment while application is in background? I haven't really worked with Fragments a lot, so I think that I'm just missing something.
DialogFragment has a method called dismissAllowingStateLoss()
This is what I did (df == dialogFragment):
Make sure that you call the dialog this way:
df.show(getFragmentManager(), "DialogFragment_FLAG");
When you want to dismis the dialog make this check:
if (df.isResumed()){
df.dismiss();
}
return;
Make sure that you have the following in the onResume() method of your fragment (not df)
#Override
public void onResume(){
Fragment f = getFragmentManager().findFragmentByTag("DialogFragment_FLAG");
if (f != null) {
DialogFragment df = (DialogFragment) f;
df.dismiss();
}
super.onResume();
}
This way, the dialog will be dismissed if it's visible.. if not visible the dialog is going to be dismisded next the fragment becomes visible (onResume)...
This is what I had to do to achieve what you want:
I have a Fragment activity on which i was showing a dialog fragment named fragment_RedemptionPayment which is globally declared at the top. The following code dismisses the DialogFragment if it was showing before the activity goes in background and comes back in foreground.
#Override
public void onResume() {
super.onResume();
if(fragment_RedemptionPayment.isVisible()){
fragment_RedemptionPayment.dismiss();
}
}
Another new way of checking the state before calling dismiss is this:
if(!dialog.isStateSaved){
dialog.dismiss()
} else {
//Change the UI to suit your functionality
}
In this way its is checked that state is saved or not, basically on pause and onSaveInstanceState has been called.
For Java you can use isStateSaved()
A solution that might work is setting Fragment.setRetainInstance(true) in your dialogfragment, but that's not the prettiest of fixes.
Sometimes I have noticed that I have to queue up my dialog actions to let the framework restore the state first. If you can get hold of the current Looper (Activity.getMainLooper()) and wrap that in a Handler you could try passing your dismissal to the back of the queue by posting a runnable on that queue.
I often end up using a separate fragment that it retaininstance(true) that has a ResultReceiver. So i pass on that result receiver to my jobs and handle callbacks in its onReceive (often as a router for other receivers). But that might be a bit more work than it is worth if you are using async tasks.