Related
I have an app in which I have to show SnackBar message in whole app where needed, for this I had made a class named as "CSnackBar" and defined Snackbar implementation and calling this method in activity where needed.My question is that is this correct way by memory point of view or I can define that one using interface instead, If yes then how and what this will effect app smoothness.
code for "CSnackBar" class
private static CSnackBar s_m_oCSnackBar;// declare snack bar variable
public static CSnackBar getInstance() {// creating Instance of nack bar
if (s_m_oCSnackBar == null) {
s_m_oCSnackBar = new CSnackBar();
}
return s_m_oCSnackBar;
}
public void showSnackBarError(View v, String message, Context context) {// parametrised constructor
Snackbar snack = Snackbar.make(v, "" + message, Snackbar.LENGTH_SHORT);
View view = snack.getView();
TextView tv = (TextView) view.findViewById(android.support.design.R.id.snackbar_text);
tv.setTextColor(Color.WHITE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)
tv.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
else
tv.setGravity(Gravity.CENTER_HORIZONTAL);
ColoredSnackbar.alert(snack).show();
}
public void showSnackBarSuccess(View v, String message, Context context) {
Snackbar snack = Snackbar.make(v, "" + message, Snackbar.LENGTH_SHORT);
View view = snack.getView();
TextView tv = (TextView) view.findViewById(android.support.design.R.id.snackbar_text);
tv.setTextColor(Color.WHITE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)
tv.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
else
tv.setGravity(Gravity.CENTER_HORIZONTAL);
ColoredSnackbar.confirm(snack).show();
}
and I am calling Snack bar like this in activity where I need to show Snackbar message.
CSnackBar.getInstance().showSnackBarError(m_MainLayout, "Invalid Password", getApplicationContext());
You are creating a singletone class for this, meaning that your snackbar instance will be on memory always after creating it. Also see that you are not accessing to any property of the instance inside your methods, so I find it unnecessary.
What I would do is to make showSnackBarError and showSnackBarSuccess static (with a bit of refactoring):
public static void showSnackBarError(View v, String message, Context context) {
Snackbar snack = initSnackBar();
ColoredSnackbar.alert(snack).show();
}
public static void showSnackBarSuccess(View v, String message, Context context) {
Snackbar snack = initSnackBar();
ColoredSnackbar.confirm(snack).show();
}
private initSnackBar(View v, String message, Context context){
Snackbar snack = Snackbar.make(v, "" + message, Snackbar.LENGTH_SHORT);
View view = snack.getView();
TextView tv = (TextView) view.findViewById(android.support.design.R.id.snackbar_text);
tv.setTextColor(Color.WHITE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
tv.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
} else {
tv.setGravity(Gravity.CENTER_HORIZONTAL);
}
}
And then calling them as:
CSnackBar.showSnackBarError(m_MainLayout, "Invalid Password", getApplicationContext());
About defining an interface and the overriding it with those methods, I will use it if you create a class that extends Snackbar, something like
interface SnackbarInterface{
showSnackBarError(View v, String message, Context context);
showSnackBarSuccess(View v, String message, Context context )
}
class CSnackBar extends SnackBar implements SnackBarInterface{
CSnackBar();
#Override
showSnackBarError(View v, String message, Context context){
...
}
#Override
showSnackBarSuccess(View v, String message, Context context ){
...
}
The diference is that in that way, you are creating a custom snackbar, so you should modify its properties instead of creating a Snackbar object. As I see you are using the default Snackbar provided, I guess its not appealing to you to use it in this way.
The other option I see with interfaces in the way you are showing the snackbar is that your activities extends a custom class that implements that interface so they would have the showSnackBarError and showSnackBarSuccess inherited. However, I see the best way to proceed will be the first one.
Hope I answered your answer instead of confusing you more :/
Interfaces are used to get callbacks from another class or activity to fetch data.
As of my knowledge you are doing right with creating a class and making some methods in it that will be fine. pardon me for my english.
If we are using lots of Snackbars, would it be better to create a class that controls the snackbar creation or would it even slow performance? Because we would be creating one more object for the garbage collector(instance of that creator class).
I'm using such code a lot:
final Snackbar snackbar = Snackbar
.make(coordinatorLayout, "Added to favourites", Snackbar.LENGTH_LONG);
snackbar.setAction("UNDO", new View.OnClickListener() {
#Override
public void onClick(View view) {
snackbar.dismiss();
}
});
snackbar.show();
or the same but with color options too:
View sbView = snackbar.getView();
TextView textView = (TextView) sbView.findViewById(android.support.design.R.id.snackbar_text);
textView.setTextColor(ContextCompat.getColor(MyApplication.getAppContext(), R.color.accentColor));
snackbar.show();
Is it a good idea to create such a class for making those Snackbars or would it just put more weight on the performance?
Now with a helper class, code looks much more readable:
SnackBarHelper snackBarHelper = new SnackBarHelper(coordinatorLayout, "You already signed up for this one!", Snackbar.LENGTH_LONG);
snackBarHelper.addColor(R.color.accentColor);
snackBarHelper.getMySnackbar().show();
This is without the onclick code though.
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();
}
}
Trying out the new Design Support Library, I added a snackbar; but unlike its main background, the text area is not colored with the default value of #323232. Instead, it looks like this. It seems to take its color from the android:background value defined in the custom theme in my styles.xml, which goes like this:
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
...
<item name="android:background">#4f4f5e</item>
...
</style>
If I try to forcefully color it with
View snackbarView = snackbar.getView();
snackbarView.setBackgroundColor(Color.YELLOW);
it only impacts the main background, like this, and the text background still gets colored by the custom theme. Is there a way to both keep my custom theme, and have a standard snackbar? Thanks!
To change the Snackbar's background colour you can do the following from code:
Snackbar snack = Snackbar.make(...);
ViewGroup group = (ViewGroup) snack.getView();
group.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.red));
snack.show();
Instead of red you can use the Snackbar's default colour: #323232
.setBackgroundColor allows you to change background color of snackbar
msnackBar.setBackgroundColor(Color.parseColor("#009688"));
or
msnackBar.setBackgroundColor(getResources().getColor(R.color.BLUE)););
Here is complete tutorial to use snackbar using design support library.
The snackbar contains a TextView, so you need to change the background color for both, the snackbar the way you already did, and then the TextView like this:
View snackbarView = snackbar.getView();
TextView textView = (TextView)snackbarView.findViewById(android.support.design.R.id.snackbar_text);
textView.setBackgroundColor(Color.YELLOW);
This effect happens when in style attribute android:background is set.
Removing that will of course affect all layouts in your application but snackbar will be fixed.
Here is a complete sample:
Snackbar snack = Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null);
ViewGroup group = (ViewGroup) snack.getView();
group.setBackgroundColor(ContextCompat.getColor(MainActivity.this, R.color.blue));
snack.show();
replace MainActivity.this with your currently activity or getAppContext()
You can simply create your own Snackbar class and simulate Snackbar's make method. Doing this, you just have to use this class instead of android's snackbar widget.
Snackbar.class
import android.graphics.Color;
import android.support.annotation.IntDef;
import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.FloatingActionButton;
import android.view.View;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
public class Snackbar {
/** Snackbar's lengths **/
public static final int LENGTH_SHORT = android.support.design.widget.Snackbar.LENGTH_SHORT;
public static final int LENGTH_LONG = android.support.design.widget.Snackbar.LENGTH_LONG;
public static final int LENGTH_INDEFINITE = android.support.design.widget.Snackbar.LENGTH_INDEFINITE;
#NonNull
public static android.support.design.widget.Snackbar make(#NonNull View view, #NonNull CharSequence text,
#Duration int duration) {
android.support.design.widget.Snackbar snackbar = android.support.design.widget.Snackbar.make(view, text, duration);
// TODO: This is where you have to customize your snackbar
snackbar.getView().setBackgroundColor(Color.RED);
return snackbar;
}
#NonNull
public static android.support.design.widget.Snackbar make(#NonNull View view, #StringRes int resId, #Duration int duration) {
return make(view, view.getResources().getText(resId), duration);
}
// Optional
#IntDef({LENGTH_INDEFINITE, LENGTH_SHORT, LENGTH_LONG})
#IntRange(from = 1)
#Retention(RetentionPolicy.SOURCE)
public #interface Duration {}
}
Use:
// WARNING: Make sure you're using your snackbar's package
import com.mypackage.custom_views.Snackbar;
public class MyActivity extends Activity {
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
Snackbar.make(view, R.string.my_msg, Snackbar.LENGTH_LONG).show();
}
}
Hope this helps!
You can use this library: https://github.com/SandroMachado/restaurant
new Restaurant(MainActivity.this, "Snackbar with custom background and text color", Snackbar.LENGTH_LONG)
.setBackgroundColor(Color.GRAY)
.show();
Disclaimer: I made the library.
this is how i'm using custom snackbar
Snackbar snackbar_network = Snackbar.make(rLayout, "Your Message", Snackbar.LENGTH_SHORT)
.setAction("EXIT", new View.OnClickListener() {
#Override
public void onClick(final View v) {
finish();
}
});
Action Text Color
snackbar_network.setActionTextColor(Color.RED);
Action Message Text Color
final View sbView = snackbar_network.getView();
final TextView tv = (TextView) sbView.findViewById(android.support.design.R.id.snackbar_text);
tv.setTextColor(Color.YELLOW);
Set Snackbar Background
sbView.setBackgroundColor(ContextCompat.getColor(MapsActivity.this, R.color.black));
snackbar_network.show();
Works this way for me:
LinearLayout linearLayout = (LinearLayout) findViewById(R.id.ll);
Snackbar snackbar = Snackbar.make(lineatLayout, "TEXT", Snackbar.LENGTH_LONG);
ViewGroup group = (ViewGroup) snackbar.getView();
group.setBackgroundColor(ContextCompat.getColor(getApplicationContext(), R.color.yourColor));
TextView textView = (TextView) group.findViewById(android.support.design.R.id.snackbar_text);
textView.setTextColor(ContextCompat.getColor(this, R.color.yor collor));
snackbar.show();
I also faced similar issue & unfortunately no solution works for me Hence I write my own solution where I set background color for parent view too.
TextView snackbarTextView = snackbar.getView().findViewById(android.support.design.R.id.snackbar_text);
snackbarTextView.setBackgroundColor(ContextCompat.getColor(getActivity(), R.color.colorPrimary));
ViewParent parentView = snackbarTextView.getParent();
if (parentView instanceof View) {
((View) parentView).setBackgroundColor(ContextCompat.getColor(getActivity(), R.color.colorPrimary));
}
View snackbarView = snackbar.getView();
snackbarView.setBackgroundColor(ContextCompat.getColor(getActivity(), R.color.colorPrimary));
snackbar.show();
I'm trying to leverage new Snackbar from Android Design Support Library to display multiline snackbar, as shown in http://www.google.com/design/spec/components/snackbars-toasts.html#snackbars-toasts-specs:
import android.support.design.widget.Snackbar;
final String snack = "First line\nSecond line\nThird line";
Snackbar.make(mView, snack, Snackbar.LENGTH_LONG).show();
It displays only First line... on my Nexus 7. How to make it display all lines?
PS: I tried Toast and it displayed all lines.
Just set the maxLines attribute of Snackbars Textview
View snackbarView = snackbar.getView();
TextView textView = (TextView) snackbarView.findViewById(android.support.design.R.id.snackbar_text);
textView.setMaxLines(5); // show multiple line
If you're using the more recent "com.google.android.material:material:1.0.0"dependency, then you will use this: com.google.android.material.R.id.snackbar_text to access the Snackbar's TextView.
You can use even R.id.snackbar_text as well. it's work for me.
One can override the predefined value used for that in values.xml of the app
<integer name="design_snackbar_text_max_lines">5</integer>
This value is used by Snackbar by default.
With the Material Components Library you can define it using with the snackbarTextViewStyle attribute in the app theme:
<style name="AppTheme" parent="Theme.MaterialComponents.*">
...
<item name="snackbarTextViewStyle">#style/snackbar_text</item>
</style>
<style name="snackbar_text" parent="#style/Widget.MaterialComponents.Snackbar.TextView">
...
<item name="android:maxLines">5</item>
</style>
Note: it requires the version 1.2.0 of the library.
Snackbar snackbar = Snackbar.make(view, "Text",Snackbar.LENGTH_LONG).setDuration(Snackbar.LENGTH_LONG);
View snackbarView = snackbar.getView();
TextView tv= (TextView) snackbarView.findViewById(android.support.design.R.id.snackbar_text);
tv.setMaxLines(3);
snackbar.show();
Here is my finding on this :
Android does support multiline snackbars but it has a max limit of 2 lines which matches the design guideline where it says that the height of multiline snack bar should be 80dp (almost 2 lines)
To verify this, i used the cheesesquare android sample project. If i use following string:
Snackbar.make(view, "Random Text \n When a second snackbar is triggered while the first is displayed", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
In this case, i can see the multiline snack bar with the text of 2nd line, i.e. "When a second snackbar is triggered" but if i change this code to following implementation:
Snackbar.make(view, "Random Text \n When \n a second snackbar is triggered while the first is displayed", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
I can only see the "Random Text\nWhen ...". This means that design library is intentionally forcing the textview to be of max 2 lines.
In kotlin you can use extensions.
// SnackbarExtensions.kt
fun Snackbar.allowInfiniteLines(): Snackbar {
return apply { (view.findViewById<View?>(R.id.snackbar_text) as? TextView?)?.isSingleLine = false }
}
Usage:
Snackbar.make(view, message, Snackbar.LENGTH_LONG)
.allowInfiniteLines()
.show()
For Material Design, the reference is com.google.android.material.R.id.snackbar_text
val snack = Snackbar.make(myView, R.string.myLongText, Snackbar.LENGTH_INDEFINITE).apply {
view.findViewById<TextView>(com.google.android.material.R.id.snackbar_text).maxLines = 10
}
snack.show()
In Kotlin, you can just do
Snackbar.make(root_view, "Yo\nYo\nYo!", Snackbar.LENGTH_SHORT).apply {
view.snackbar_text.setSingleLine(false)
show()
}
You could also replace setSingleLine(false) with maxLines = 3.
Android Studio should prompt you to add
import kotlinx.android.synthetic.main.design_layout_snackbar_include.view.*
EDIT
I haven't been able to get this to work again, so I'll just share what I think is the cleanest way to write in Kotlin what a few others have already shared:
import com.google.android.material.R as MaterialR
Snackbar.make(root_view, "Yo\nYo\nYo!", Snackbar.LENGTH_SHORT).apply {
val textView = view.findViewById<TextView>(MaterialR.id.snackbar_text)
textView.setSingleLine(false)
show()
}
2021 Answer in Kotlin for com.google.android.material:material:1.4.0
isSingleLine = false is required as well as maxLines = 5
Snackbar.make(view, "line 1\nline 2", BaseTransientBottomBar.LENGTH_INDEFINITE)
.apply {
this.view.findViewById<TextView>(com.google.android.material.R.id.snackbar_text)?.apply {
maxLines = 5
isSingleLine = false
}
}
.show()
An alternative to the suggestions that involve hardcoding the resource ID for the textview contained by the snackbar is to iterate to find the TextView. It's safer long-term and lets you update the support library with minimal fear of the ID changing.
Example:
public static Snackbar getSnackbar(View rootView, String message, int duration) {
Snackbar snackbar = Snackbar.make(rootView, message, duration);
ViewGroup snackbarLayout = (ViewGroup) snackbar.getView();
TextView text = null;
for (int i = 0; i < snackbarLayout.getChildCount(); i++) {
View child = snackbarLayout.getChildAt(i);
// Since action is a button, and Button extends TextView,
// Need to make sure this is the message TextView, not the
// action Button view.
if(child instanceof TextView && !(child instanceof Button)) {
text = (TextView) child;
}
}
if (text != null) {
text.setMaxLines(3);
}
return snackbar;
}
Instead of using setMaxLines, i use setSingleLine to make the textview wrap to its content.
String yourText = "First line\nSecond line\nThird line";
Snackbar snackbar = Snackbar.make(mView, yourText, Snackbar.LENGTH_SHORT);
TextView textView =
(TextView) snackbar.getView().findViewById(android.support.design.R.id.snackbar_text);
textView.setSingleLine(false);
snackbar.show();
this works for me
Snackbar snackbar = Snackbar.make(mView, "Your text string", Snackbar.LENGTH_INDEFINITE);
((TextView) snackbar.getView().findViewById(android.support.design.R.id.snackbar_text)).setSingleLine(false);
snackbar.show();
Late, but might be helpful to someone:
public void showSnackBar(String txt, View view){
final Snackbar snackbar = Snackbar.make(view,txt,Snackbar.LENGTH_INDEFINITE)
.setAction("OK", new View.OnClickListener() {
#Override
public void onClick(View view) {
//do something
}
});
View view = snackbar.getView();
TextView textView = (TextView) view.findViewById(android.support.design.R.id.snackbar_text);
textView.setMaxLines(5);
snackbar.show();
}
May i suggest you to use com.google.android.material.snackbar.Snackbar. This is the recommanded way by google. First you have to add your snackbar.
final Snackbar snackbar = Snackbar.make(
findViewById(R.id.activity_layout),
"snackbar explanation text \n multilines \n\n here",
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.action_settings, new View.OnClickListener() {
#Override
public void onClick(View view) {
// your action here
}
});
Then to add multilines support
TextView messageView = snackbar.getView().findViewById(R.id.snackbar_text);
messageView.setMaxLines(4);
Finally show the snackbar.
snackbar.show();
A way to do it which won't crash in case things change on newer versions of the library :
Snackbar.make(...).setAction(...) {
...
}.apply {
(view.findViewById<View?>(R.id.snackbar_text) as? TextView?)?.setSingleLine(false)
}.show()
And a way to do it without having ids being used, setting all TextViews in the Snackbar to have unlimited multi-lines :
#UiThread
fun setAllTextViewsToHaveInfiniteLinesCount(view: View) {
when (view) {
is TextView -> view.setSingleLine(false)
is ViewGroup -> for (child in view.children)
setAllTextViewsToHaveInfiniteLinesCount(child)
}
}
Snackbar.make(...).setAction(...) {
...
}.apply {
setAllTextViewsToHaveInfiniteLinesCount(view)
}.show()
The same function in Java:
#UiThread
public static void setAllTextViewsToHaveInfiniteLines(#Nullable final View view) {
if (view == null)
return;
if (view instanceof TextView)
((TextView) view).setSingleLine(false);
else if (view instanceof ViewGroup)
for (Iterator<View> iterator = ViewGroupKt.getChildren((ViewGroup) view).iterator(); iterator.hasNext(); )
setAllTextViewsToHaveInfiniteLines(iterator.next());
}
Just a quick comment, if you are using com.google.android.material:material the prefix or package for R.id should be com.google.android.material
val snackbarView = snackbar.view
val textView = snackbarView.findViewById<TextView>(com.google.android.material.R.id.snackbar_text)
textView.maxLines = 3
so as i am using latest material design library from google, com.google.android.material:material:1.1.0 and i used simple following code snipet below, to resolve allow to more lines in snackbar. hope it will help to new developers as well.
TextView messageView = snackbar.getView().findViewById(R.id.snackbar_text);
messageView.setMaxLines(5);
To avoid flakiness of other answers can use updateMaxLine, this solution is less likely to break if Google decide to change the id of a text view)
val snackBar = Snackbar.make(view, message, duration)
snackBar.view.allViews.updateMaxLine(5)
snackBar.show()
just note, this option will update the max line for all the text views in the Snakbar view (which tbh I do not think it matters)
add this as extension
private fun <T> Sequence<T>.updateMaxLine(maxLine : Int) {
for (view in this) {
if (view is TextView) {
view.maxLines = maxLine
}
}
}
Snackbar height adjustment:
val sMsg = "Msg\n\n"
val sOk = getString(R.string.ok)
val sMoreLines = StringBuilder()
for (iCtr in 1..6) {
sMoreLines.append("\n")
}
Snackbar
.make(
this.requireActivity().findViewById(android.R.id.content),
sMsg,
Snackbar.LENGTH_INDEFINITE)
.setAction("$sMoreLines$sOk\n$sMoreLines") {
// ...
}
.show()