Set vertical padding on Android SnackBar - android

So I want to change the vertical padding on Android's SnackBar in a clean maintainable way.
I tried to use:
setPadding and setMargin on the FrameLayout, LinearLayout and TextView used to create the SnackBar
includeFontPadding, setLineSpacing and setSingleLineon the TextView
But nothing seemed to work.
However, I found this answer:
In dimens.xml.
Use this:
<dimen name="design_snackbar_padding_horizontal">0dp</dimen>
But remember that this will get applied to all the snackbars in your application.
Which does work, but the ugly way, it's applied to all the SnackBars in the app and it's not safe since I'm overriding a private property that maybe renamed or removed in the future.
I'm trying to create something similar and Facebook Messenger's (previously) and Youtube's (Funny enough, Google) internet connection indicator:
Snackbar.make(container, message, duration).apply {
val snackBarView = view.apply {
setBackgroundColor(ContextCompat.getColor(this#MainActivity, connectivity.color))
}
(snackBarView.findViewById(com.google.android.material.R.id.snackbar_text) as TextView).apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
textAlignment = View.TEXT_ALIGNMENT_CENTER
else
gravity = Gravity.CENTER
}
}.show()
So this's what i'm expecting and am getting using the answer provided above:
And this's what i'm actually getting:
Thanks in advance!

Make a custom snackbar:
Snackbar snackbar = Snackbar.make(coordinatorLayout, R.string.custom_color, Snackbar.LENGTH_LONG).setAction(R.string.Ok, new View.OnClickListener() {
#Override
public void onClick(View view) {
Snackbar.make(coordinatorLayout, R.string.click_action, Snackbar.LENGTH_SHORT).show();
}
}).setActionTextColor(Color.RED);
View snackview = snackbar.getView();
TextView txtView = snackview.findViewById(R.id.snackbar_text);
txtView.setPadding(0,0,0,0);
snackbar.show();

The provided answer didn't work out for me. So after a lot of trial, I finally solved my problem (which is exactly the same as described above).
To reduce the vertical padding in snackbar, try this:
LinearLayout.LayoutParams llp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
llp.topMargin = -30;
llp.bottomMargin = -30;
snackText.setLayoutParams(llp);
Below is the full code I wrote to make functionality like the youtube app for checking and showing internet errors.
public void showNoInternet(){
noInternetSnack = Snackbar.make(noInternetSnackParent, "No internet connection", Snackbar.LENGTH_INDEFINITE)
.setAnimationMode(BaseTransientBottomBar.ANIMATION_MODE_SLIDE)
.setBackgroundTint(PaletteUtils.getSolidColor(PaletteUtils.MATERIAL_RED));
Snackbar.SnackbarLayout snackbarLayout = (Snackbar.SnackbarLayout) noInternetSnack.getView();
TextView snackText = snackbarLayout.findViewById(com.google.android.material.R.id.snackbar_text);
snackText.setTextSize(getResources().getDimension(R.dimen.newsMoreTextSize)/3);
snackText.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
LinearLayout.LayoutParams llp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
llp.topMargin = -30;
llp.bottomMargin = -30;
snackText.setLayoutParams(llp);
noInternetSnack.show();
}
public void hideNoInternet(){
if (noInternetSnack != null && noInternetSnack.isShown()){
Snackbar.SnackbarLayout snackbarLayout = (Snackbar.SnackbarLayout) noInternetSnack.getView();
noInternetSnack.setBackgroundTint(PaletteUtils.getSolidColor(PaletteUtils.MATERIAL_GREEN));
TextView snackText = snackbarLayout.findViewById(com.google.android.material.R.id.snackbar_text);
snackText.setText("You are back online");
new java.util.Timer().schedule(
new java.util.TimerTask() {
#Override
public void run() {
noInternetSnack.dismiss();
}
},
500
);
}
}
private BroadcastReceiver mConnReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
NetworkInfo currentNetworkInfo = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
if (currentNetworkInfo.isConnected()) {
hideNoInternet();
} else {
showNoInternet();
}
}
};

Related

Android - automatically scroll within AlertDialog TextView

I am using an AlertDialog to track a logFile which is updating in real time, and need to automatically scroll to the bottom of the view whenever an extra line is added.
I am able to cast the AlertDialog to a TextView (and e.g. using this TextView alter the text size) but any methods involving scrolling don't work.
Code:
LogFileView.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
try {
String logFile = "/data/data/com.test/test.log";
String logFileOutput = getFileOutput(logFile);
final AlertDialog dialog = new AlertDialog.Builder(getActivity()).setMessage(logFileOutput).show();
TextView textView = dialog.findViewById(android.R.id.message);
textView.setTextSize(8);
textView.scrollTo(0, textView.getLineCount());
} catch (IOException e) {
e.printStackTrace();
}
}
});
textView.setTextSize(8); will alter the text size on display
textView.scrollTo(0, textView.getLineCount()); will do nothing, and the alert dialog, despite having a scrollbar available, will remain focussed on the first line
Update 1:
I see there are a few requests for the dialog creation code/errors in the console output.
Firstly, I am not actually using a separate layout/class to create the dialog. It is applying the default layout associated with (android.R.id.message) to an instance of android.app.AlertDialog and is only constructed within the onClick method for the onClickListener in the code above.
Based on the feedback I've received so far, the code I've most recently attempted to use looks as follows:
LogFileView.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
try {
String logFile = "/data/data/com.test/test.log";
String logFileOutput = getFileOutput(logFile);
final AlertDialog dialog = new AlertDialog.Builder(getActivity()).setMessage(logFileOutput).show();
TextView textView = dialog.findViewById(android.R.id.message);
textView.setTextSize(8);
//textView.scrollTo(0, textView.getLineCount());
textView.setMovementMethod(new ScrollingMovementMethod());
textView.post(new Runnable() {
#Override
public void run() {
textView.scrollTo(0, textView.getLineCount());
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
});
Secondly, there is nothing appearing in the console when the scroll attempt is made - this simply gets ignored at runtime.
The default layout seemed like it would be fine for my purpose, given it appears like a blank TextView with a scrollbar attached, but I think it may be a sensible next step to use a new custom layout and add a ScrollView and see what happens
Use below code. I Tested & verified.
textView.movementMethod = ScrollingMovementMethod() // Add this
textView.text = "your text"
textView.post {
val scrollAmount = textView.layout.getLineTop(textView.lineCount) - textView.height
textView.scrollTo(0, textView.lineCount)
}
Edit:
Equivalent to java:
textView.setMovementMethod(new ScrollingMovementMethod());
textView.setText("your text");
textView.post(new Runnable() {
#Override
public void run() {
int scrollAmount = textView.getLayout().getLineTop(textView.getLineCount()) - textView.getHeight();
textView.scrollTo(0, scrollAmount);
}
});
Try to wrap it with post instead of calling directly to scrollTo,
something like this:
textView.post(new Runnable() {
#Override
public void run() {
textView.scrollTo(0, textView.getLineCount());
}
});

Why this import static <project-name> .R.id.snackbarView

We added a android.support.design.widget.CoordinatorLayout to the XML file for the Activity that is using a Snackbar. All the Gradle information is correct and the two other corresponding imports are imported minSdk is 19 Support Design is v25.3.1 My question is why is this import happening and have I omitted some setting somewhere?
The original XML code file has a Relative Layout plus other items and the XML for the CoordinatorLayout widget
code for snackbar
public void showSnackbar(){
//coordinatorLayout = findViewById(snackbarView);
//Snackbar snackbar = null;
final Snackbar snackbar = Snackbar
.make(findViewById(snackbarView), getText(R.string.snackbar_text),1);
snackbar.setActionTextColor(Color.RED);
snackbar.setAction("EXIT", new View.OnClickListener(){
#Override
public void onClick(View view) {
//Intent intent = new Intent(DetailsActivity.this, MainActivity.class);
//startActivity(intent);
snackbar.dismiss();
}
});
TextView snackbarActionTextView = (TextView) snackbar.getView().findViewById( android.support.design.R.id.snackbar_action );
snackbarActionTextView.setTextSize( 30 );
snackbarActionTextView.setTypeface(snackbarActionTextView.getTypeface(), Typeface.BOLD);
TextView snackbarTextView = (TextView) snackbar.getView().findViewById(android.support.design.R.id.snackbar_text);
snackbarTextView.setTextSize( 30 );
snackbarTextView.setMaxLines( 3 );
snackbarTextView.setTextColor(Color.YELLOW);
snackbar.setDuration(Snackbar.LENGTH_INDEFINITE);
snackbar.show();
}
here is my import
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
James_Duh here is some code that will create a plain Snackbar no Action button and the other method creates a Snackbar with the Action button enjoy Not sure about the import?
public void onPLAIN(View view){
noActLayout = (CoordinatorLayout)findViewById(R.id.USE_ME_TWICE);
sbNoAct = Snackbar.make(noActLayout,R.string.real_csb_noaction_text,1);// any interger will make it happy
sbNoAct.setDuration(4000);// 4 sec // OR Snackbar.LENGTH_LONG matters NOT you are setting duration here
View sbView = sbNoAct.getView();
sbView.setBackgroundColor(ContextCompat.getColor(this, R.color.color_lightBlue));
TextView textViewNoAct = sbView.findViewById(android.support.design.R.id.snackbar_text);
//set text color
textViewNoAct.setTextColor(ContextCompat.getColor(this,R.color.color_White));
textViewNoAct.setMaxLines(10);
textViewNoAct.setTextSize(18);
//increase max lines of text in snackbar. default is 2.
sbNoAct.show();
}
public void onWithAct(View view){
myLayout = (CoordinatorLayout) findViewById(R.id.USE_ME_TWICE);
sb = Snackbar.make(myLayout, R.string.real_csb_text, Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.real_csb_action, myOnClickListener)
.setActionTextColor(ContextCompat.getColor(context, R.color.color_Red));
View sbView = sb.getView();
sbView.setBackgroundColor(ContextCompat.getColor(this, R.color.color_White));
TextView textView = sbView.findViewById(android.support.design.R.id.snackbar_text);
//set text color
textView.setTextColor(ContextCompat.getColor(this,R.color.color_deepBlue));
textView.setTextSize(18);
//increase max lines of text in snackbar. default is 2.
textView.setMaxLines(10);
// NOTE new View
TextView textAction = sbView.findViewById(android.support.design.R.id.snackbar_action);
//set Action text color
textAction.setTextColor(ContextCompat.getColor(this,R.color.color_Red));
textAction.setTextSize(18);
sb.show();
}
View.OnClickListener myOnClickListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
//sb.dismiss();
System.out.println("=== I WAS DISMISSED OR SENT TO MainActivity===");
// OR use and Intent to go somewhere have a nice trip
Intent intent = new Intent(PageThreeActivity.this, MainActivity.class);
startActivity(intent);
}
};
}

How to disable android snackbar opening animation from bottom screen

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.

Android Multiline Snackbar

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()

Android custom dialog skipped frames

I've coded a custom dialog for my application but once I try to open it, I got a lag and the following message is displayed in the logcat console:
Skipped 31 frames! The application may be doing too much work on its main thread.
To lower the work, I've moved the Typeface loading to an AsyncTask but it doesn't solve my problem.
public class CustomDialog
{
private Context context = null;
private Dialog dialog = null;
private boolean cancelable = true;
private OnCancelListener cancelListener = null;
private Typeface latoFont = null;
private RelativeLayout layout_root = null;
private TextView text_title = null;
private TextView text_message = null;
private LinearLayout layout_buttons = null;
public CustomDialog(Context context)
{
this.context = context;
this.dialog = new Dialog(context, android.R.style.Theme_Translucent_NoTitleBar);
this.dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
this.dialog.setContentView(R.layout.dialog);
this.dialog.setCancelable(true);
layout_root = (RelativeLayout) dialog.findViewById(R.id.layout_root);
text_title = (TextView) dialog.findViewById(R.id.text_title);
text_message = (TextView) dialog.findViewById(R.id.text_message);
layout_buttons = (LinearLayout) dialog.findViewById(R.id.layout_buttons);
// Set background color
layout_root.setBackgroundColor(FlatUtils.transparentDark(FlatConstants.DOMIANT_COLOR));
// Set font
new TypeFaceTask(context, new TextView[]{text_title, text_message}).execute();
// Attach listener
layout_root.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view)
{
if (view.getId() != R.id.layout_root) return;
if (cancelable)
{
if (cancelListener != null) cancelListener.onCancel();
dialog.dismiss();
}
}
});
}
public void setTitle(String title)
{
text_title.setVisibility(View.VISIBLE);
text_title.setText(title);
}
public void setMessage(String message)
{
text_message.setVisibility(View.VISIBLE);
text_message.setText(message);
text_message.setMovementMethod(new ScrollingMovementMethod());
}
public void addButton(String value, final OnClickListener listener)
{
// Create button
FlatButton button = new FlatButton(context);
button.setText(value);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, dpToPx(40));
params.setMargins(0, dpToPx(10), 0, 0);
layout_buttons.addView(button, params);
// Set typeface
if (latoFont != null) button.setTypeface(latoFont);
// Attach listener
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view)
{
if (listener != null) listener.onClick(view);
dialog.dismiss();
}
});
}
public void setCancelable(boolean b)
{
cancelable = b;
dialog.setCancelable(b);
}
public void setOnCancelListener(OnCancelListener listener)
{
this.cancelListener = listener;
dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
#Override
public void onCancel(DialogInterface dialog)
{
cancelListener.onCancel();
}
});
}
public void show()
{
dialog.show();
}
public void dismiss()
{
dialog.dismiss();
}
public interface OnCancelListener {
public void onCancel();
}
private int dpToPx(int dp)
{
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
return (int)(dp * (metrics.densityDpi / 160f));
}
}
This is way I create a new dialog:
CustomDialog dialog = new CustomDialog(TestActivity.this);
dialog.addButton("Hello 1", null);
dialog.addButton("Hello 2", null);
dialog.addButton("Hello 3", null);
dialog.setTitle("Dummy dialog");
dialog.setMessage("Plop plop");
dialog.show();
So my question is: Is there a way to optimized this code ?
First of all i think you should try extending the Dialog class instead of wrapping it like this...Your code will be cleaner and more reusable and i think will yield some performance improvement. It wont take you much, most of the code will be the same, but its a general rule of thumb if you want a custom Dialog you should extend the existing elements, similar to when you create an Activity or Fragment you extend them :).
Also i dont know what you are actually doing with your layouts but i see you are getting the screen width and height and then setting margins etc...Such calculations can cause your frames to skip and i would advise you to try making the layout through xml. Trust me, just try playing with layout params through xml and i bet you will get the same result when it comes to margins padings and the overal look, and use the different layout folders(and dimens for margins and paddings for ex) for different screen densities and sizes to achieve the same look across all devices. Not to mention this will perform faster.
Edit 1:
Typefaces wont cause such problems.
I see you said you are running this on emulator?! Well thats the issue :) i can guarantee that wont happen on a real device. :)) its a common thing on the emulator. Always develop on real devices, only they can mimic all the real problems and bugs you will encounter.
But still listen to the advice's here and follow "best practices" when programming.
is it phone or emulator? (try to run on phone)
Best practice - to extend DialogFragment(Dialog)
Adding buttons from code is strange :) You can declare yor dialog ui using XML layout, inflate it and set inflated view as content in your dialog
You don't need to load Typeface in asynctask.
So my general suggestion is to learn best practices of creating dialogs.

Categories

Resources