I have an activity with FragmentDialog. In onResume of this dialog I set height and weight of it to 80% and 90% by code:
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
layoutParams.copyFrom(getDialog().getWindow().getAttributes());
layoutParams.width = (int)(screenWidth * 0.9);
layoutParams.height = (int)(screenHeight * 0.8);
getDialog().getWindow().setAttributes(layoutParams);
It works perfectly, background has shadow, foreground FragmentDialog has proper dimensions. Question is - how can I show SnackBar at the bottom of the screen not affected by FragmentDialog (one that has shadow, Activity view) without shadow? Is there any way to disable shadow for specific view at activity that is in background of FragmentDialog?
Material design documentation says "Snackbars appear above most elements on screen, and they are equal in elevation to the floating action button. However, they are lower in elevation than dialogs, bottom sheets, and navigation drawers". From there, I think you should consider displaying the Snackbar inside the DialogFragment or just display a small dialog on top of it.
If you want to display Snackbar inside dialog fragment, you could do something like this:
public void showSnackBar(final View parent, final String text) {
Snackbar sb = Snackbar.make(parent, text, Snackbar.LENGTH_LONG);
sb.show();
}
NOTE: parent is the entire dialog's view. You could improve it by making the root view of the fragment a CoordinatorLayout and showing the Snackbar on that view.
It's late but there is a solution I think may be useful for others. To disable the dialog fragment shadow (in fact it is called DIM), add below code to your dialog fragment onResume method.
For Kotlin:
override fun onResume() {
super.onResume()
dialog?.window!!.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
}
For Java:
#Override
public void onResume() {
super.onResume();
if(getActivity()!=null)
getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
}
by the this is not exactly what question is asking but this will omit all the shadow behind dialog fragment.
To show snackbar in you parent fragment rather than your dialog fragment, you can pass parent fragment reference to dialog fragment constructor and instantiate snackbar with parent's view. this will show the snackbar at the bottom of parent fragment.
showSnackbar method would be like this:
For Kotlin:
private fun showSnackbar(messege: String) =
Snackbar.make(parent.view!!, messege, Snackbar.LENGTH_SHORT).show()
For Java:
private void showSnackBar(String messege) {
if (parent.getView() != null)
Snackbar.make(parent.getView(), messege, Snackbar.LENGTH_SHORT).show();
}
dialog fragment complete code would be like:
For kotlin:
class MyDialogFramgent(parent: Fragment) : DialogFragment() {
// class code ...
override fun onResume() {
super.onResume()
dialog?.window!!.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
}
private fun showSnackbar(messege: String) =
Snackbar.make(parent.view!!, messege, Snackbar.LENGTH_SHORT).show()
}
For Java:
public class MyDialogFragment extends DialogFragment {
private Fragment parent;
public MyDialogFragment(Fragment parent) {
this.parent = parent;
}
#Override
public void onResume() {
super.onResume();
if (getActivity() != null)
getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
}
private void showSnackBar(String messege) {
if (parent.getView() != null)
Snackbar.make(parent.getView(), messege, Snackbar.LENGTH_SHORT).show();
}
}
Related
I have more than 3 Activity but let's just take three of them, BaseActivity, Splashscreen, MainActivity. Splashscreen and Mainactivity extends BaseActivity.
Now i am showing a Snackbar inside the BaseActivity whenever I get failureresponse from the server. The response code is in BaseActivity and parallely i am transitioning from splashscreen to MainActivity.
The snackbar is not showing up? What could be the error? is there a way to make static snackbar? i have tried it but didnt get the getWindow from a static method?
Anyone faced this problem?
Yes you can create a static method in your utils and create snack bar in utils class and use anywhere in your app
public static void showSnackBar(View view, String msg) {
if (view != null) {
Snackbar snackbar = Snackbar.make(view, msg, Snackbar.LENGTH_LONG);
View snackbarView = snackbar.getView();
TextView textView = snackbarView.findViewById(android.support.design.R.id.snackbar_text);
textView.setMaxLines(5);
//snackBarView.setBackgroundColor(Color.argb(255, 8, 20, 37));
snackbar.show();
}
}
Use:
Utils.showSnackBar(getView(), failureMessage);
Is it possible to disable dragging for a BottomSheetDialogFragment, containing scrollable views such as a ViewPager or a NestedScrollView, such that it can't be dragged neither up nor down but still be dismissed by touching outside and that the children can be dragged anyways?
I've looked at all the answers here but I am not pleased since most don't take into account scrollable children or work by forcing the expanded state. The closest is this answer but nonetheless allows dragging the sheet up.
Is there any solution or at least guidance at what should I modify of the original source code?
If you debug your application and use Layout Inspector tool, you will see that BottomSheetDialogFragment uses CoordinatorLayout. Dimmed background is a simple view with an OnClickListener that closes the dialog, and sheet movement is driven by CoordinatorLayout.Behavior.
This can be overriden by modifying created dialog:
Java:
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Dialog d = super.onCreateDialog(savedInstanceState);
// view hierarchy is inflated after dialog is shown
d.setOnShowListener(new DialogInterface.OnShowListener() {
#Override
public void onShow(DialogInterface dialogInterface) {
//this disables outside touch
d.getWindow().findViewById(R.id.touch_outside).setOnClickListener(null);
//this prevents dragging behavior
View content = d.getWindow().findViewById(R.id.design_bottom_sheet);
((CoordinatorLayout.LayoutParams) content.getLayoutParams()).setBehavior(null);
}
});
return d;
}
Kotlin:
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val d = super.onCreateDialog(savedInstanceState)
//view hierarchy is inflated after dialog is shown
d.setOnShowListener {
//this disables outside touch
d.window.findViewById<View>(R.id.touch_outside).setOnClickListener(null)
//this prevents dragging behavior
(d.window.findViewById<View>(R.id.design_bottom_sheet).layoutParams as CoordinatorLayout.LayoutParams).behavior = null
}
return d
}
This does use internal IDs of design library, but unless for some reason they're changed this should be stable.
I'm using BottomSheetDialogFragment from the support library and it warns me that the function setupDialog() should only be used within the library group. But this function is the place where I initialize my dialog:
#Override
public void setupDialog(final Dialog dialog, int style) {
super.setupDialog(dialog, style);
FragmentArgs.inject(this);
dialog.setOnShowListener(dialogINterface -> {
if(dialog.getWindow() != null) {
dialog.getWindow().setLayout(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.MATCH_PARENT);
}
});
BottomSheetStatisticsExportBinding binding = DataBindingUtil.inflate(
LayoutInflater.from(getContext()),
R.layout.bottom_sheet_statistics_export,
null,
false
);
View contentView = binding.getRoot();
dialog.setContentView(contentView);
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) ((View) contentView.getParent()).getLayoutParams();
CoordinatorLayout.Behavior behavior = params.getBehavior();
if( behavior != null && behavior instanceof BottomSheetBehavior )
((BottomSheetBehavior) behavior).setBottomSheetCallback(bottomSheetBehaviorCallback);
for (Export export : exports)
binding.flexbox.addView(new ExportItemView(getContext(), export));
}
The warning is because I'm using the super method. But what should I do instead? Should I move my code inside another function (onCreateDialog(), onResume()...?), should I remove the call to the super?
Anyone knows?
Should I move my code inside another function (onCreateDialog(),
onResume()...?)
Yes. As it demonstrates in the DialogFragment documentation (which BottomSheetDialogFragment extends), you should use onCreateView() to set up your dialog.
The View you return from this method will be set as the content view for the dialog provided by onCreateDialog(). And the getDialog() method can be used from within onCreateView() to make any adjustments to the aforementioned Dialog.
onCreateDialog() would be used to replace the default Dialog. Which in your case you probably don't want to do; considering that is the method BottomSheetDialogFragment uses to replace the default Dialog with a BottomSheetDialog (in fact, it is the only method BottomSheetDialogFragment overrides).
Here are the BottomSheetDialog and BottomSheetDialogFragment classes I created to replace the support library versions (see comments for more information).
I'm using CustomSnackBar library from GITHUB ,
Check this: https://github.com/TakeoffAndroid/CustomSnackBar/blob/master/app/src/main/java/com/takeoffandroid/customsnackbar/SnackBar.java
This is so useful to customize my snackbar.
But I want to DISABLE snackbar opening animation , now its opening from bottom screen with some duration (Y- translation) .
Is there any way to show snackbar without animation or disable the animation or reduce animation duration to 0(zero) ??
Android Studio ver:2.1
compileSdkVersion 23
buildToolsVersion "23.0.3"
Design Lib : com.android.support:design:23.3.0
Thanks in advance,
With current version (design-24.1.0), a simple fix doesn't look possible, but there's a way to hack it (see below)
Inside Snackbar, the showView method is invoked to display the snackbar
final void showView() {
[...]
if (ViewCompat.isLaidOut(mView)) {
if (shouldAnimate()) {
// If animations are enabled, animate it in
animateViewIn();
} else {
// Else if anims are disabled just call back now
onViewShown();
}
}
[...]
}
private boolean shouldAnimate() {
return !mAccessibilityManager.isEnabled();
}
showView is final so we can't do anything, plus we can't touch any of the code that determines if the snackbar should be animated.
Regarding the animation itself, the methods animateViewIn and animateViewOut are both private, and all of the code inside relies on private fields or constants
Edit:
What I do to simulate that there are no animations is handle Snackbar's visibility
Snackbar snackbar = Snackbar.make(view, message, Snackbar.LENGTH_INDEFINITE);
snackbar.getView().setVisibility(View.INVISIBLE);
[...]
snackbar.setCallback(new Snackbar.Callback() {
#Override
public void onShown(Snackbar snackbar) {
super.onShown(snackbar);
snackbar.getView().setVisibility(View.VISIBLE);
}
});
snackbar.show();
And since in my case I control when the Snackbar is dimissed, I can do this
snackbar.getView().setVisibility(View.GONE);
snackbar.dismiss();
Unfortunately, I don't think this last step is easy for non-indeterminate Snackbars. A way to hack this is to schedule a setVisibility(View.INVISIBLE) at the duration of LENGTH_SHORT and LENGTH_LONG
In SnackbarManager. We can't access the constants, but we can copy&paste
private static final int SHORT_DURATION_MS = 1500;
private static final int LONG_DURATION_MS = 2750;
Then in your code, inside Callback.onShown()
handler.postDelayed(new Runnable() {
#Override
public void run() {
snackbar.getView().setVisibility(View.INVISIBLE);
}
}, LONG_DURATION_MS or SHORT_DURATION_MS)
I haven't tested this myself, maybe it should be called before onShown. If someone tries it, edit the post or let me know in the comments.
For kotlin user, you can try following code:
showError(message: String, viewGroup: ViewGroup) {
val duration: Long = 2000
val snackBar = Snackbar.make(viewGroup, message, duration.toInt())
snackBar.withColor(ContextCompat.getColor(this, com.sdei.sdeiarchitecture.R.color.colorAccent))
val params = snackBar.view.layoutParams as FrameLayout.LayoutParams
params.gravity = Gravity.TOP
snackBar.view.layoutParams = params
val messageTv = snackBar.view.findViewById(com.google.android.material.R.id.snackbar_text) as TextView
messageTv.textSize = 20.0f
snackBar.view.visibility = View.INVISIBLE
snackBar.addCallback(object : Snackbar.Callback() {
override fun onShown(snackbar: Snackbar?) {
super.onShown(snackbar)
snackbar!!.view.visibility = View.VISIBLE
Handler().postDelayed({
snackbar.view.visibility = View.INVISIBLE
}, duration)
}
})
snackBar.show()
}
Hope it will help you out.
I have a custom component that extends RelativeLayout which in turns holds a GridLayout(named mFormLayout). I have a public method that adds two spinners with their proper adapter source and an imageview which acts as a button to remove rows.
public class EditableTwinSpinnerGridForm extends EditableGridForm
{
public void addTwinSpinnerRow(final Locale.MapKey spinner1DefVal, final Locale.MapKey spinner2DefVal)
{
Spinner spinner1 = createSpinner(mTSP.getFirstSpinnerRes(), spinner1DefVal.getId());
spinner1.setOnItemSelectedListener(mTSP.getIsl());
Spinner spinner2 = createSpinner(mTSP.getSecondSpinnerRes(), spinner2DefVal.getId());
ImageView rmvBtn = createRemoveBtn();
mFormLayout.addView(spinner1);
mFormLayout.addView(spinner2);
mFormLayout.addView(rmvBtn);
}
}
For some reason, this method works when I am adding rows from a call to onCreate in an activity, but when I am calling this method after the activity is created(from an onclicklistener) the Spinners are either not there or only one of them shows up. They do take the space because I see the row and the removable image view.
I have also noticed that when I focus on a EditText in the same activity and the keyboard pops up, the added spinners show up when I press back to remove the keyboard.
Here's the code I use to create a spinner :
protected Spinner createSpinner(Integer spinnerSrc, String defaultSpinnerValue)
{
Spinner spinner = new Spinner(mCtx, Spinner.MODE_DIALOG);
// Setting the bg color to the containing color to remove the spinner arrow.
spinner.setBackgroundResource(R.color.container_bg);
SparseArray<Phrase> map = Locale.getInstance().getMap(spinnerSrc);
PhraseArrayAdapter adapter = createSpinnerFromMap(spinnerSrc);
spinner.setAdapter(adapter);
if (defaultSpinnerValue.equals(Utilities.EMPTY_STRING) || defaultSpinnerValue.isEmpty())
{
throw new IllegalStateException();
}
else
{
Utilities.getInstance().setMapSpinnerPosByValue(map, defaultSpinnerValue, spinner);
}
adapter.setDropDownViewResource(R.layout.editable_spinner_dropdown_item);
setSpinnerLayoutParams(spinner);
return spinner;
}
protected void setSpinnerLayoutParams(Spinner spinner)
{
GridLayout.LayoutParams lp = createDefaultGridParams();
lp.setGravity(Gravity.FILL_HORIZONTAL);
lp.width = 250;
lp.rightMargin = 0;
spinner.setLayoutParams(lp);
}
The code works when the activity is loaded so I'm a bit stumped. I looked around and some people suggested I set LayoutParams in addView, but why would this method work in onCreate, but not afterwards?
Here's what's happening visually(The first three rows are added from a loop in onCreate(), the two second ones are added by pressing "Add +"). As you can see the second spinner isn't showing up, sometimes both aren't showing up. I also tried calling invalidate and requestLayout to no avail.
I had looked into the invalidate method, here's where it is located currently :
public abstract class EditableForm extends RelativeLayout implements ObservableInt
{
private class OnAddClicked implements OnClickListener
{
#Override
public void onClick(View v)
{
onAddClicked(v);
EditableForm.this.invalidate();
mFormLayout.invalidate();
}
}
}
Which calls(In a subclass of EditableGrid) :
#Override
protected void onAddClicked(View clickedView)
{
addTwinSpinnerRow();
notifyObservers(new ObservableData(EDITABLE_ADD_GRID_CLICKED, null));
}
protected void addTwinSpinnerRow()
{
Locale.MapKey v1 = Locale.getInstance().getMap(mTSP.getFirstSpinnerRes()).get(0).getMapId();
Locale.MapKey v2 = Locale.getInstance().getMap(mTSP.getSecondSpinnerRes()).get(0).getMapId();
addTwinSpinnerRow(v1, v2);
}
Have you tried calling the Invalidate method of the container view rather than the added view?
Most likely the views you are adding are there, they just need to be drawn which is suggested by your keyboard hide/show difference. Does rotating the device also cause them to appear? If so, this again suggests that you need to redraw your custom layout.
When it's necessary to execute invalidate() on a View?