I'm trying to perform an action in one fragment, then move to the previous fragment and show a snackbar with a message, confirming the action from the first fragment. However, I'm creating and showing the snackbar in the first fragment (the one I'm moving from), and the snackbar does not appear in the fragment I'm changing to, probably because it's shown in the fragment I'm moving from.
I'm executing the code inside an alertdialog:
builder.setPositiveButton(positiveText, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dbHandler.deleteExercise(exercise.getId());
// Making the snackbar here did not work, either.
getFragmentManager().popBackStack();
Snackbar snack = Snackbar.make(mainLayout, "Exercise deleted", Snackbar.LENGTH_SHORT);
snack.show();
}
});
Any idea how I could go about achieving this?
Thanks!
EDIT:
I made this incredibly crude drawing of the flow to make it clearer what I'm trying to achieve.
I ended up implementing messaging between Fragments via the main Activity and checking for messages in the fragment's onResume() method:
MainActivity:
private String fragmentTransactionMessage;
#Override
protected void onCreate(Bundle savedInstanceState) {
// Snip
// Initialize fragment transaction message
fragmentTransactionMessage = null;
// Snip
}
// Three methods used to send information (text) between fragments
public void setFragmentTransactionMessage(String message) {
this.fragmentTransactionMessage = message;
}
public String getFragmentTransactionMessage() {
return fragmentTransactionMessage;
}
public void resetFragmentTransactionMessage() {
this.fragmentTransactionMessage = null;
}
Fragment 2:
// Remove exercise from database
dbHandler.deleteExercise(exercise.getId());
// Update message in main activity
((MainActivity)getActivity()).setFragmentTransactionMessage("Item deleted");
// Move to previous fragment
getFragmentManager().popBackStack();
Fragment 1:
#Override
public void onResume() {
super.onResume();
// Check for messages in main activity
String message = ((MainActivity)getActivity()).getFragmentTransactionMessage();
// If any, display as snackbar
if(message != null) {
Snackbar snack = Snackbar.make(mainLayout, message, Snackbar.LENGTH_SHORT);
snack.show();
// Reset message in activity
((MainActivity)getActivity()).resetFragmentTransactionMessage();
}
}
Another suggestion is to wrap getFragmentManager().popBackStack(); in a method which takes a Runnable Obj which will run after a chosen delay
For Example:
public void goBack(Runnable runAfterBack, long delay) {
mFragmentActivity.onBackPressed();
if (runAfterBack != null) {
(new Handler(Looper.getMainLooper())).postDelayed(runAfterBack, delay);
}
}
Usage:
goBack(new Runnable() {
#Override
public void run() {
Toast.makeText(mActivity, "STRING", Toast.LENGTH_SHORT).show();}
}, 1500);
Related
I created an interface so I can communicate between a dialogue and a fragment.
Goal: When the user selects anything from the dialogue it should display it on a text view.
In this interface, I created an interface method, called in the main activity and passed the value the user selected in the dialogue. Along with the user selected value, in my fragment, I created a method that will set the text view to that value. However, whenever I call that method it always returns null.
I did plenty of testing with logs and found that the values being passed through my method is NOT null, everything seems to work the exact way I want it to which is odd. What confuses me even more, is that this method isn't even running, it immediately returns null before executing the code inside which is really strange to me.
Dialog Code:
public String users_time;
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final String time_options[] = {"10", "20", "30", "40", "50", "60", "70", "80", "90"}; // Since we know how many options there are in the array we use an array instead of an arraylist
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle("Choose the time");
builder.setSingleChoiceItems(time_options, -1, new DialogInterface.OnClickListener() { // check items = what index is auto selected in the dialog, use -1 bc you dont want that
#Override
public void onClick(DialogInterface dialog, int which) { // which = Index in the array
CharSequence time = time_options[which];
Log.i("this" ,"LOG 1 dialogsTime retusn" + time);
listener.onDialogInput(time);
users_time = time_options[which];
int usersTime = Integer.valueOf(users_time);
listener.grabTime(usersTime);
}
});
builder.setPositiveButton("Set Time", new DialogInterface.OnClickListener() { // positive = Ok or continue
#Override
public void onClick(DialogInterface dialog, int which) {
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { // Negative = Cancel or stop
#Override
public void onClick(DialogInterface dialog, int which) {
}
});
return builder.create(); // always return this at the end
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof DiaglogListener) {
listener = (DiaglogListener) context;
}
else {
throw new RuntimeException(context.toString()
+ " must implement DiaglogListener");
}
}
#Override
public void onDetach() {
super.onDetach();
listener = null;
}
}
Main Activity Interface Method:
#Override
public void onDialogInput(CharSequence dialogsTime) {
Fragment1_timer frag1 = new Fragment1_timer();
Log.i("this" ,"LOG 2 runs successfully");
try {
frag1.setDialogTime(dialogsTime);
} catch (Exception e){
Toast.makeText(this, "Null error :/", Toast.LENGTH_SHORT).show();
}
}
Fragment Method:
public void setDialogTime(CharSequence time){
Log.i("this" ,"LOG 3 ran successfully");
text_view_time.setText(time + ":00");
}
You can't use onAttach method for Fragment to DialogFragment communication.
you will have to use "setTargetFragment" & "getTargetFragment" for that.
you can refer this answer. https://stackoverflow.com/a/32323822/9792247
If i call the snackbar multiple times in a row, only the last snackbar item is displayed.
e.g. with the codes below, only Item 3 would be shown. it seems that Snackbar.LENGTH_LONG is ignored (and set to zero?) for item 1 and 2.
Snackbar.make(view, "Item 1", Snackbar.LENGTH_LONG).show();
Snackbar.make(view, "Item 2", Snackbar.LENGTH_LONG).show();
Snackbar.make(view, "Item 3", Snackbar.LENGTH_LONG).show();
yet in the google documents, I see that it is possible to queue the messages.
public boolean isShownOrQueued ()
Returns whether this Snackbar is currently being shown,
or is queued to be shown next.
so how do we actually queue the snackbar?
Here is a partial snippet that solves your problem, although it might not
be the correct way to go about things:
//using a queue to pass string to the snackbar
Queue<String> myQueue = new LinkedList<String>();
myQueue.offer("item 1");
myQueue.offer("item 2");
myQueue.offer("item 3");
displaysnack(myQueue, view);
public void displaysnack(final Queue dQueue, final View view){
Snackbar.make(view, (String)dQueue.poll(), Snackbar.LENGTH_LONG).setCallback(new Snackbar.Callback() {
#Override
public void onDismissed(Snackbar snackbar, int event) {
switch (event) {
case Snackbar.Callback.DISMISS_EVENT_ACTION:
Toast.makeText(getApplicationContext(), "Clicked the action", Toast.LENGTH_LONG).show();
break;
//once the timeout expires, display the next one in the queue.
case Snackbar.Callback.DISMISS_EVENT_TIMEOUT:
Toast.makeText(getApplicationContext(), "Showing: "+ (dQueue.size()), Toast.LENGTH_SHORT).show();
if (dQueue.size()>0){displaysnack(dQueue, view);}
break;
case Snackbar.Callback.DISMISS_EVENT_CONSECUTIVE:
//Toast.makeText(getApplicationContext(), "Multiple Shown", Toast.LENGTH_SHORT).show();
break;
}
}
I also needed to implement a queue of snackbars but did not find ready solution. So I decided to implement it on my own. You can try it https://github.com/AntonyGolovin/FluentSnackbar.
Just call important() on Builder and it will be added to the queue.
I also implemented my own, probably not the slickest but suited my needs. Its in C# for Xamarin though.
public class SnackbarManager : Snackbar.Callback
{
List<Snackbar> snackbarsWaiting;
List<Snackbar> snackbarsHolding;
public SnackbarManager()
{
snackbarsWaiting = new List<Snackbar>();
snackbarsHolding = new List<Snackbar>();
}
public void AddToQueue(Snackbar snackbar)
{
if (snackbar.Duration == Snackbar.LengthIndefinite) snackbar.SetDuration(Snackbar.LengthLong);
snackbar.SetCallback(this);
if (snackbarsWaiting.Count > 0 && snackbarsWaiting[0].IsShown) snackbarsHolding.Add(snackbar);
else snackbarsWaiting.Add(snackbar);
}
public void Show()
{
if (snackbarsWaiting.Count > 0 && !snackbarsWaiting[0].IsShown)
snackbarsWaiting[0].Show();
}
public override void OnDismissed(Snackbar snackbar, int evt)
{
base.OnDismissed(snackbar, evt);
snackbarsWaiting.Remove(snackbar);
if (snackbarsHolding.Count > 0)
{
snackbarsWaiting.AddRange(snackbarsHolding);
snackbarsHolding.Clear();
}
if (snackbarsWaiting.Count > 0) snackbarsWaiting[0].Show();
}
}
I met this problem too, this is my solution.
static List<Snackbar> snackBarList = new ArrayList<>();
public static void mySnackBar(CoordinatorLayout coordinatorLayout, String s,boolean queued) {
Snackbar snackbar = Snackbar.make(coordinatorLayout, s, Snackbar.LENGTH_SHORT);
if (queued) {
//if true set onDismiss CallBack
snackbar.setCallback(new Snackbar.Callback()
{
#Override
public void onDismissed(Snackbar currentSnackbar, int event) {
super.onDismissed(currentSnackbar, event);
//first remove current snackBar in List, then if List not empty show the first one
snackBarList.remove(currentSnackbar);
if (snackBarList.size() > 0)
snackBarList.get(0).show();
}
});
//add (set callback) snackBar to List
snackBarList.add(snackbar);
//the beginning
if (snackBarList.size() == 1)
snackBarList.get(0).show();
} else snackbar.show();
}
I've written a library that do just that. It also includes progressBar. Try it out https://github.com/tingyik90/snackprogressbar
mSomeFragment = new SomeFragment();
mSomeFragment.show(getFragmentManager(), "some");
The Fragment shows fine.
mSomeFragment = new SomeFragment();
mSomeFragment.show(getFragmentManager(), "some");
mSomeFragment.onDismiss(new DialogInterface() {
#Override
public void cancel() {
//
}
#Override
public void dismiss() {
//
}
});
But when I set onDismiss, this doesn't work (the Fragment doesn't shows). I wanna do some operations when the dialog dismisses.
Could you tell me why??
Calling onDismiss actually calls this code
if (mDialog != null) {
mDialog.dismiss();
mDialog = null;
}
which dismisses the dialog. If you want to listen for events on the dialog use onOptionsItemSelected()
I have a custom preference, TimePreference, which extends DialogPreference. It has a custom dialog resource, which looks like this
The source is
#Override
protected void onBindDialogView(View v){
super.onBindDialogView(v);
v.findViewById(R.id.butCancel).setOnClickListener(onClickL);
v.findViewById(R.id.butNow).setOnClickListener(onClickL);
v.findViewById(R.id.butOK).setOnClickListener(onClickL);
//....
}
//...
private final View.OnClickListener onClickL = new View.OnClickListener(){
#Override
public void onClick(View v) {
Log.d(lTag, v + " clicked");
switch (v.getId()) {
case R.id.butOK: saveToSP(false);break;
case R.id.butNow: saveToSP(true);
}
try {
getDialog().dismiss(); //may throw null pointer
} catch (Exception e) { Log.w(lTag, "Exc #onClickL", e); }
}
};
//...
I found a bug where, if you clicked the same preference really fast twice (at the preference screen) two dialogs would open. You could close the first one but, when you would try to close the second, the app would crash. It was a NullPointerException, so I enclosed it in a try-catch block. Now, the exception is caught, but the buttons do not close the dialog. Notice that, by clicking back, it does close.
How can I close the second dialog (possibly by simulating the behaviour of the back button?) ? Note, I want the API level below 10.
Okay, I found a soultion. I have a static boolean, which shows if there is an open dialog.
private static boolean isAnyDialogOpen = false;
On dialog bind, I set it to true,
And after I close the dialog, I set it to false.
Turned out that even this was problematic, but the solution was easier
#Override
protected void onClick() {
if (isAnyDialogOpen)
Log.i(lTag, "there is a dialog already");
else {
isAnyDialogOpen = true;
super.onClick();
}
}
#Override
public void onDismiss(DialogInterface dialog) {
Log.d(lTag, "dismiss, dialog= "+dialog);
isAnyDialogOpen = false;
if (dialog != null) super.onDismiss(dialog);
}
I have a requirement of having a activity as dialog (Not an Alert dialog, instead a progress dialog). There are no ui components in this activity since this activity does server interaction, based on server response next activities are started. Only a progress dialog with cancel button needs to be shown in this activity. Now the problem is when ever this activity is launched just before progress dialog is displayed a small black rectanagle is visible for a second or more, also when this activity is finished this is visible. How to get rid of this ? or is there any better way of haveing a progress dialog as activity ?
-Thanks & regards,
Manju
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityAsDialogActivity.this.requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.main);
String str ="Hellow World";
txtView = (TextView)findViewById(R.id.txtview);
this.setFinishOnTouchOutside(false);
txtView.setText(R.string.select_dialog);
ActivityAsDialogActivity.this.setTitle(R.string.app_name);
mProgressHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (mProgress >= MAX_PROGRESS) {
mProgressDialog.dismiss();
Intent intent = new Intent(ActivityAsDialogActivity.this, ActivityOne.class);
startActivity(intent);
finish();
} else {
mProgress++;
mProgressDialog.incrementProgressBy(1);
mProgressHandler.sendEmptyMessageDelayed(0, 100);
}
}
};//end of handler
showDialog(DIALOG_PROGRESS);
mProgressDialog.setProgress(0);
mProgressHandler.sendEmptyMessage(0);
}//end of onCreate()
#Override
public void onBackPressed(){
Log.d("Manju ==>"," back key pressed");
finish();
}
#Override
public void finish(){
Log.d("Manju ==>", " inside finish()");
super.finish();
}
In Creating Global Dialog, I used a transparent activity with a dialog inside. I think you can do the same thing. I do not recall facing the problem you are facing, and it worked perfectly.
Check it out