I add a Snackbar to my app. The problem is that in API 19 it's not at the bottom of the screen.
In API 21 it's ok. Here is my layout
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data />
<android.support.design.widget.CoordinatorLayout
android:id="#+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="true"
android:focusableInTouchMode="true">
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="#string/home_search_input_hint"
android:inputType="text"
android:maxLength="30"
android:maxLines="1"/>
</android.support.constraint.ConstraintLayout>
</android.support.design.widget.CoordinatorLayout>
</layout>
And my OnCreate
#Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_home);
super.onCreate(savedInstanceState);
// binding
binding = DataBindingUtil.setContentView(this, R.layout.activity_home);
// snackbar test
Snackbar snackbar = Snackbar.make(binding.root, "Snackbar", Snackbar.LENGTH_INDEFINITE);
snackbar.show();
}
Do you have any ideas how to fix it?
UPDATE: It seems that margin from the bottom is really random, I rerun emulator and see this.
and this
If you use 26 or 27 support, it is probably this known bug: Google Issue Tracker: Snackbar wrong placement on the Android 4x versions
To workaround it, use a small time delay before showing the Snackbar. This is needed if you want to show the Snackbar immediately when the screen displays, in onCreateView() or onCreate().
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
// For com.android.support:design:v26 or v27, use a small
// time delay, to prevent bottom gap bug on Android 4.4
if (isAdded()) { snackbar.show(); }
}
}, 500);
Because you have a time delay, remember to protect against null Activity by checking for isAdded() or check getActivity() != null.
This issue can be avoided by moving the code which shows snackbar to onGlobalLayout() like following.
binding.root.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener()
{
#Override
public void onGlobalLayout()
{
// snackbar test
Snackbar snackbar = Snackbar.make(binding.root, "Snackbar", Snackbar.LENGTH_INDEFINITE);
snackbar.show();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
binding.root.getViewTreeObserver().removeOnGlobalLayoutListener(this);
} else {
binding.root.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
}
});
This is a bug which is still not fixed.
Workaround - small delay on pre-lollipop devices.
Delay not noticeable on UI.
Put these methods in your base fragment and base activity and use everywhere:
protected ViewGroup getRootView() {
return (ViewGroup) getView().getParent();
}
protected void showSnackbar(#StringRes int resId) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
new Handler().postDelayed(() -> Snackbar.make(getRootView(), resId, Snackbar.LENGTH_SHORT).show(), 200);
} else {
Snackbar.make(getRootView(), resId, Snackbar.LENGTH_SHORT).show();
}
}
protected void showSnackbar(String message) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
new Handler().postDelayed(() -> Snackbar.make(getRootView(), message, Snackbar.LENGTH_SHORT).show(), 200);
} else {
Snackbar.make(getRootView(), message, Snackbar.LENGTH_SHORT).show();
}
}
Related
I just installed the Android R (API 30) image in my emulator to try my app, and it crashed when trying to set the Background color of a Toast.
Toast toast = Toast.makeText(ctxt, msg, duration);
View view = toast.getView();
view.setBackgroundColor(0xFF303030);
TextView tview = view.findViewById(android.R.id.message);
tview.setTextColor(Color.WHITE);
toast.show();
This is really strange as in Android Q (API 29) works perfectly.
My build.gradle updated for Android R (API 30)
compileSdkVersion 30
buildToolsVersion "30.0.1"
Is there a new way to do it??
Since Android 11, custom toasts/ toast modifications are deprecated, according to Google to "protect users". Hence why your app in Android 30 is not able to display custom toasts.
From Android Developers documentation:
Custom toast views are deprecated. Apps can create a standard text toast with the makeText(android.content.Context, java.lang.CharSequence, int)
The only way I have found of showing custom toasts from API 30 onwards is by creating them ad hoc.
XML LAYOUT
Customize as needed
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".main_activity">
<!--Ad hoc toast Textview-->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="12dp"
android:layout_margin="18dp"
android:background="#drawable/ad_hoc_toast_background"
android:textColor="#1e1e1e"
android:gravity="center"
android:visibility="gone"
android:layout_alignParentBottom="true"
android:id="#+id/ad_hoc_toast_textview"
tools:text="Temporary message bla bla bla ..."/>
</RelativeLayout>
TOAST BACKGROUND (ad_hoc_toast_background.xml)
Customize as needed
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size
android:width="220dp"
android:height="100dp"/>
<corners
android:radius="25dp"
/>
<solid
android:color="#e6ffffff"
/>
</shape>
</item>
</selector>
Define the show_ad_hoc_toast() method
private void show_ad_hoc_toast(final TextView ad_hoc_toast_textview, String text){
//Set the text
ad_hoc_toast_textview.setText(text);
//Create alpha animation
AlphaAnimation animation1 = new AlphaAnimation(0f, 1f);
//Set duration
animation1.setDuration(300);
//Set that the animation changes persist once the animation finishes
animation1.setFillAfter(true);
//Set on AnimationEnd Listner
animation1.setAnimationListener(new Animation.AnimationListener() {
#Override public void onAnimationStart(Animation animation){}
#Override public void onAnimationRepeat(Animation animation){}
#Override public void onAnimationEnd(Animation animation){
//After 2250 millis -> hide the toast
new CountDownTimer(2250, 1) {
public void onTick(long millisUntilFinished){}
public void onFinish() {hide_ad_hoc_toast(ad_hoc_toast_textview);}
}.start();
}
});
//Make the view visible
ad_hoc_toast_textview.setVisibility(View.VISIBLE);
//Start animation
ad_hoc_toast_textview.startAnimation(animation1);
}
Define the hide_ad_hoc_toast() method
private void hide_ad_hoc_toast(final TextView ad_hoc_toast_textview){
//Create alpha animation
AlphaAnimation animation1 = new AlphaAnimation(1f, 0f);
//Set duration
animation1.setDuration(300);
//Set that the animation changes persist once the animation finishes
animation1.setFillAfter(true);
//Set on AnimationEnd Listner
animation1.setAnimationListener(new Animation.AnimationListener() {
#Override public void onAnimationStart(Animation animation) { }
#Override public void onAnimationRepeat(Animation animation) { }
#Override public void onAnimationEnd(Animation animation) {
//Make the view gone
ad_hoc_toast_textview.setVisibility(View.GONE);
}
});
//Start animation
ad_hoc_toast_textview.startAnimation(animation1);
}
Call the method from your code when needed
//Find ad_hoc_toast textview
TextView ad_hoc_toast_textview = findViewById(R.id.ad_hoc_toast_textview);
//Define the text to be shown
String text = "This is the custom toast message"
//Show the ad_hoc toast
show_ad_hoc_toast(ad_hoc_toast_textview, text);
RESULT
You can check before to custumized toast
Toast toast = Toast.makeText(ctxt, msg, duration);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
View view = toast.getView();
view.setBackgroundColor(0xFF303030);
TextView tview = view.findViewById(android.R.id.message);
tview.setTextColor(Color.WHITE);
}
toast.show();
#pvalle & #Aayush Panda, It works for me in Android 11. Please check below code
public static void showCenterToastMessage(Context context, String msg) {
LayoutInflater inflater = LayoutInflater.from(context);
View layout = inflater.inflate(R.layout.custom_toast,null);
TextView text = (TextView) layout.findViewById(R.id.text);
text.setText(msg);
text.setPadding(20,0,20,0);
text.setTextSize(18);
text.setTextColor(Color.WHITE);
Toast toast = new Toast(context);
toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
toast.setDuration(Toast.LENGTH_LONG);
layout.setBackgroundColor(Color.DKGRAY);
toast.setView(layout);
toast.show();
}
layout is as follows
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/text"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
The solution with setting a custom view on Toast is deprecated for API level 30
Documentation says
This method was deprecated in API level 30. Custom toast views are
deprecated. Apps can create a standard text toast with the
makeText(android.content.Context, java.lang.CharSequence, int) method,
or use a Snackbar when in the foreground. Starting from Android
Build.VERSION_CODES#R, apps targeting API level Build.VERSION_CODES#R
or higher that are in the background will not have custom toast views
displayed.
There is a walkaround though which still works and is not deprecated
Toast.makeText(applicationContext,
HtmlCompat.fromHtml("<font color='red'>custom toast message</font>", HtmlCompat.FROM_HTML_MODE_LEGACY),
Toast.LENGTH_LONG).show()
Html color tag can also be <font color='#ff6347'>
WindowManager interface can be an alternative to the toast after the Android 11 limitations.
https://developer.android.com/reference/android/view/WindowManager
But you just need user permission to display custom messages over the apps.
I want to implement the Extended FAB button in the format mentioned on the material website (https://kstatic.googleusercontent.com/files/8f9b57829c943c97be7c4b2485cf678f041dfe7c7ef523cfb2e97f1aeee21431f83d98cc07befeeed904fabb258298e3a7ac95f9da5d3da7a4adcff658cea851)
https://material.io/components/buttons-floating-action-button#types-of-transitions
Kindly help on how to achieve the same.
You can use the Material motion and the Transition between Views.
For example define in your layout:
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="#+id/root"
..>
<com.google.android.material.card.MaterialCardView
android:id="#+id/end_card"
android:visibility="gone" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#+id/fab"
.. />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Then just define the MaterialContainerTransform:
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
showEndView(fab);
}
});
with:
private void showEndView(View startView) {
// Construct a container transform transition between two views.
MaterialContainerTransform transition = new MaterialContainerTransform();
transition.setScrimColor(Color.TRANSPARENT);
transition.setInterpolator(new FastOutSlowInInterpolator());
//set the duration....
//Define the start and the end view
transition.setStartView(startView);
transition.setEndView(endCard);
transition.addTarget(startView);
// Trigger the container transform transition.
TransitionManager.beginDelayedTransition(root, transition);
if (startView != null) {
startView.setVisibility(View.INVISIBLE);
}
if (endCard != null) {
endCard.setVisibility(View.VISIBLE);
}
}
Note: it requires at least the version 1.3.0-alpha01.
I've seen other threads with this issue, but none of them has worked for me.
I have an Activity A, which calls Activity B. I'm doing a fade animation programmatically both entry and exit, and a shared FAB is translated to its new position. This translation works, but the fade does not.
Code of Activity A
profilePic.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
// Activity A starting Activity B
Activity activity = MainScreenUI.this;
Intent intent = new Intent(MainScreenUI.this, ProfileUI.class);
int[] imageScreenLocation = new int[2];
profilePic.getLocationInWindow(imageScreenLocation);
intent.putExtra(Properties.PACKAGE + ".left", imageScreenLocation[0])
.putExtra(Properties.PACKAGE + ".top", imageScreenLocation[1])
.putExtra(Properties.PACKAGE + ".width", profilePic.getWidth())
.putExtra(Properties.PACKAGE + ".height", profilePic.getHeight());
startActivity(intent);
activity.overridePendingTransition(0, 0);
}
});
Code of Activity B
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_profile);
...
mTopLevelLayout = (CoordinatorLayout)findViewById(R.id.profile_coordinator);
mBackground = new ColorDrawable(Color.WHITE);
mTopLevelLayout.setBackground(mBackground);
if (savedInstanceState == null)
{
final ViewTreeObserver observer = mProfileFAB.getViewTreeObserver();
observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener()
{
#Override
public boolean onPreDraw()
{
mProfileFAB.getViewTreeObserver().removeOnPreDrawListener(this);
...
_runEnterAnimation();
return true;
}
});
}
}
private void _runEnterAnimation()
{
mProfileFAB.setPivotX(0);
mProfileFAB.setPivotY(0);
mProfileFAB.setScaleX(mWidthScaleImage);
mProfileFAB.setScaleY(mHeightScaleImage);
mProfileFAB.setTranslationX(mLeftDeltaImage);
mProfileFAB.setTranslationY(mTopDeltaImage);
mProfileFAB.animate()
.setDuration(ANIM_DURATION)
.scaleX(1).scaleY(1)
.translationX(0).translationY(0)
.setInterpolator(ACCELERATE_DECELERATE_INTERPOLATOR);
// This animation is not applied
ObjectAnimator bgAnim = ObjectAnimator.ofInt(mBackground, "alpha", 0, 255);
bgAnim.setDuration(ANIM_DURATION);
bgAnim.start();
}
#Override
public void onBackPressed()
{
_runExitAnimation(new Runnable() {
public void run() {
finish();
}
});
}
#Override
public void finish()
{
super.finish();
overridePendingTransition(0, 0);
}
private void _runExitAnimation(final Runnable endAction)
{
int[] currentLocation = new int[2];
mProfileFAB.getLocationOnScreen(currentLocation);
mTopDeltaImage = mThumbnailTop - currentLocation[1];
mProfileFAB.animate()
.setDuration(ANIM_DURATION)
.setStartDelay(0)
.scaleX(mWidthScaleImage).scaleY(mHeightScaleImage)
.translationX(mLeftDeltaImage).translationY(mTopDeltaImage)
.withEndAction(endAction);
// This animation is not working either
ObjectAnimator bgAnim = ObjectAnimator.ofInt(mBackground, "alpha", 0);
bgAnim.setDuration(ANIM_DURATION);
bgAnim.start();
}
XML of Activity B
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/profile_coordinator"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".Activities.ProfileUI"
xmlns:app="http://schemas.android.com/apk/res-auto">
<android.support.design.widget.AppBarLayout
android:id="#+id/profile_appbar_layout"
android:layout_width="match_parent"
android:layout_height="192dp"
android:background="#drawable/filter_bottom_border">
<android.support.design.widget.CollapsingToolbarLayout
android:id="#+id/profile_collapsing_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:contentScrim="?attr/colorPrimary">
...
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:background="#android:color/white"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
...
</android.support.v4.widget.NestedScrollView>
<com.wallakoala.wallakoala.Views.FloatingActionImageView
android:id="#+id/profile_floating_pic"
android:layout_width="84dp"
android:layout_height="84dp"
android:src="#drawable/female_icon"
app:borderWidth="0dp"
app:layout_anchor="#id/profile_appbar_layout"
app:layout_anchorGravity="bottom|center"/>
</android.support.design.widget.CoordinatorLayout>
The strange thing is that this logic is applied in other places and it's working fine, I don't know what I've messed here.
Thanks in advance,
Provide exit and enter animations inside overridePendingTransition() method in your activity A> onclick method. There you provided overridePendingTransition(0,0).
You should rather use overridePendingTransition() in the onCreate() method of the Activity that you are trying to open than after raising the intent for the same.
If you kept the parameters 0 just to hide your code in the question its fine, but if not, let me tell you that it expects animation resource files as the input parameters. You can probably use the fade animations available in the support library as:
overridePendingTransition(R.anim.abc_fade_in, R.anim.abc_fade_out);
It takes in the first parameter as the enter animation of the Activity being opened, the second parameter for the exit animation of the previous one.
I am working on an app using geolocation and I want to set a layout VISIBLE when close to a point and GONE when too far from the point.
This is my xml :
<LinearLayout
android:visibility="gone"
android:id="#+id/slidePane"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
Then I use the ID in the activity as usual :
declare it in the class attribute :
private LinearLayout slidepane;
set it in the onCreate() method :
slidepane = (LinearLayout) findViewById(R.id.slidePane);
And then I try to update it in onLocationChanged() method as follows :
if (InterestPoint.CalculationByDistance(location, new LatLng(47.247801, -1.551883)) < 0.03) {
Toast.makeText(MainActivity.mContext, "InterestPoint close", Toast.LENGTH_SHORT).show();
slidepane.setVisibility(View.VISIBLE);
} else {
slidepane.setVisibility(View.GONE);
}
So the condition is correct since the Toast appears but the layout does not become visible, why?
Thank you.
EDIT : I am using https://github.com/umano/AndroidSlidingUpPanel and try to set the visibility of the second child (the sliding panel). I didn't see anything in the documentation about visibility. I can still access the children of the LinearLayout but not the container itself (and especialy the visibility).
Replace this:
slidepane.setVisibility(View.VISIBLE);
with this:
runOnUiThread(new Runnable() {
#Override
public void run() {
slidepane.setVisibility(View.VISIBLE);
}
});
Make sure you set the visibility of view at the time of initialization.
#Override
protected void onCreate(Bundle savedInstanceState) {
........
slidepane = (LinearLayout) findViewById(R.id.slidePane);
slidepane.setVisibility(View.GONE);
.......
//onLocationChanged() add ->
if (InterestPoint.CalculationByDistance(location, new LatLng(47.247801, -1.551883)) < 0.03) {
Toast.makeText(MainActivity.mContext, "InterestPoint close", Toast.LENGTH_SHORT).show();
slidepane.setVisibility(View.VISIBLE);
} else {
slidepane.setVisibility(View.GONE);
}
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.