如何设置支持库快餐栏文本颜色以外的其他 Android: textColor?

因此,我已经开始在设计支持库中使用新的 Snackbar,但是我发现,当您在主题中定义“ android: textColor”时,它将应用于 Snackbar 的文本颜色。如果您的主文本颜色是深色,这显然是一个问题。

enter image description here

有人知道解决这个问题的方法吗? 或者有什么建议告诉我应该怎样给短信上色?

编辑2017年1月: (回复)

虽然有一些自定义的解决方案来修复下面的问题,但是提供正确的方法来主题 Snackbar 可能是好的。

首先,您可能根本不应该在主题中定义 android:textColor(除非您真正知道使用主题的范围)。这基本上设置了连接到主题的每个视图的文本颜色。如果希望在视图中定义非默认的文本颜色,请使用 android:primaryTextColor并在自定义视图中引用该属性。

然而,为了将主题应用到 Snackbar,请参考来自第三方材料文档的质量指南: http://www.materialdoc.com/snackbar/(遵循编程主题实现,使其不依赖于 xml 样式)

参考资料:

// create instance
Snackbar snackbar = Snackbar.make(view, text, duration);


// set action button color
snackbar.setActionTextColor(getResources().getColor(R.color.indigo));


// get snackbar view
View snackbarView = snackbar.getView();


// change snackbar text color
int snackbarTextId = android.support.design.R.id.snackbar_text;
TextView textView = (TextView)snackbarView.findViewById(snackbarTextId);
textView.setTextColor(getResources().getColor(R.color.indigo));


// change snackbar background
snackbarView.setBackgroundColor(Color.MAGENTA);

(你也可以创建自己的自定义 Snackbar布局,参见上面的链接。如果这个方法感觉太糟糕,并且您想要一个可靠的方法让您的自定义 Snackbar 在可能的支持库更新中保持最后的状态,请这样做)。

或者,看看下面类似的答案,也许更快的答案来解决你的问题。

47388 次浏览

The only way I see is using getView() and cycling through its child. I don't know if it's going to work, and it is bad as it looks. I hope they'll add some API about this issue soon.

Snackbar snack = Snackbar.make(...);
ViewGroup group = (ViewGroup) snack.getView();
for (int i = 0; i < group.getChildCount(); i++) {
View v = group.getChildAt(i);
if (v instanceof TextView) {
TextView t = (TextView) v;
t.setTextColor(...)
}
}
snack.show();

Alright so I fixed it by basically reorganizing the way I do text colors.

In my light theme, I set android:textColorPrimary to the normal dark text I wanted, and I set android:textColor to white.

I updated all of my text views and buttons to have android:textColor="?android:attr/textColorPrimary".

So because snackbar draws from textColor, I just set all of my other text to textColorPrimary.

EDIT JANUARY 2017: ----------------------------------------------------

So as the comments say, and as stated in the edited original question above, you should probably not define android:textColor in your themes, as this changes the text color of every view inside the theme.

I found this at What are the new features of Android Design Support Library and how to use its Snackbar?

This worked for me for changing the text color in a Snackbar.

Snackbar snack = Snackbar.make(view, R.string.message, Snackbar.LENGTH_LONG);
View view = snack.getView();
TextView tv = (TextView) view.findViewById(android.support.design.R.id.snackbar_text);
tv.setTextColor(Color.WHITE);
snack.show();



UPDATE: ANDROIDX: As dblackker points out in the comments, with the new AndroidX support library, code to find the ID of Snackbar TextView changes to:

TextView tv = view.findViewById(com.google.android.material.R.id.snackbar_text);
tv.setTextColor(ContextCompat.getColor(requireContext(), R.color.someColor))

I changed my theme

Theme.AppCompat.Light.NoActionBar

to

Theme.AppCompat.NoActionBar

It worked.Try to use simple theme instead of light or other theme.

One approach is to use spans:

final ForegroundColorSpan whiteSpan = new ForegroundColorSpan(ContextCompat.getColor(this, android.R.color.white));
SpannableStringBuilder snackbarText = new SpannableStringBuilder("Hello, I'm white!");
snackbarText.setSpan(whiteSpan, 0, snackbarText.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);


Snackbar.make(view, snackbarText, Snackbar.LENGTH_LONG)
.show();

With spans you can also add several colors and styles inside one Snackbar. Here's a nice guide:

https://androidbycode.wordpress.com/2015/06/06/material-design-snackbar-using-the-design-support-library/

You can use this library: https://github.com/SandroMachado/restaurant

new Restaurant(MainActivity.this, "Snackbar with custom text color", Snackbar.LENGTH_LONG)
.setTextColor(Color.GREEN)
.show();

Disclaimer: I made the library.

This is what I use when I need custom colors

    @NonNull
public static Snackbar makeSnackbar(@NonNull View layout, @NonNull CharSequence  text, int duration, int backgroundColor, int textColor/*, int actionTextColor*/){
Snackbar snackBarView = Snackbar.make(layout, text, duration);
snackBarView.getView().setBackgroundColor(backgroundColor);
//snackBarView.setActionTextColor(actionTextColor);
TextView tv = (TextView) snackBarView.getView().findViewById(android.support.design.R.id.snackbar_text);
tv.setTextColor(textColor);
return snackBarView;
}

And consumed as:

CustomView.makeSnackbar(view, "Hello", Snackbar.LENGTH_LONG, Color.YELLOW,Color.CYAN).setAction("DO IT", myAction).show();

I also noticed the same problem. Thanks to the answers here I've created a small class, which can help to solve this problem in more easily, just by replacing this:

Snackbar.make(view, "Error", Snackbar.LENGTH_LONG).show();

with this:

Snackbar2.make(view, "Error", Snackbar.LENGTH_LONG).show();

Here is my class:

public class Snackbar2 {
static public Snackbar make(View view, int resid, int duration){
Snackbar result = Snackbar.make(view, resid, duration);
process(result);
return result;
}
static public Snackbar make(View view, String text, int duration){
Snackbar result = Snackbar.make(view, text, duration);
process(result);
return result;
}
static private void process(Snackbar snackbar){
try {
View view1= snackbar.getView();


TextView tv = (TextView) view1.findViewById(android.support.design.R.id.snackbar_text);
tv.setTextColor(Color.WHITE);


}catch (Exception ex)
{
//inform about error
ex.printStackTrace();
}
}

}

Hacking on android.support.design.R.id.snackbar_text is fragile, a better or less hacky way to do that will be:

String snackText = getResources().getString(YOUR_RESOURCE_ID);
SpannableStringBuilder ssb = new SpannableStringBuilder()
.append(snackText);
ssb.setSpan(
new ForegroundColorSpan(Color.WHITE),
0,
snackText.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
Snackbar.make(
getView(),
ssb,
Snackbar.LENGTH_SHORT)
.show();

I know this has been answered already but the easiest way I found was directly in the make using the Html.fromHtml method and a font tag

Snackbar.make(view,
Html.fromHtml("<font color=\"#ffffff\">Tap to open</font>").show()

I have a simple code that will help to get an instance of both the textview of Snackbar, after that you can call all methods that are applicable on a textview.

Snackbar snackbar = Snackbar.make( ... )    // Create Snack bar




snackbar.setActionTextColor(getResources().getColor(R.color.white));  //if you directly want to apply the color to Action Text


TextView snackbarActionTextView = (TextView) snackbar.getView().findViewById( android.support.design.R.id.snackbar_action );


snackbarActionTextView.setTextColor(Color.RED);  //This is another way of doing it


snackbarActionTextView.setTypeface(snackbarActionTextView.getTypeface(), Typeface.BOLD);


//Below Code is to modify the Text in Snack bar
TextView snackbarTextView = (TextView) snackbar.getView().findViewById(android.support.design.R.id.snackbar_text);
snackbarTextView.setTextSize( 16 );
snackbarTextView.setTextColor(getResources().getColor(R.color.white));

Created this kotlin extention function i use in my projects:

fun Snackbar.setTextColor(color: Int): Snackbar {
val tv = view.findViewById(com.google.android.material.R.id.snackbar_text) as TextView
tv.setTextColor(color)


return this
}

Usage like you would expect:

Snackbar.make(view, R.string.your_string,Snackbar.LENGTH_LONG).setTextColor(Color.WHITE).show()

Just to save your precious development time, here is the static method I am using:

public static void snack(View view, String message) {
if (!TextUtils.isEmpty(message)) {
Snackbar snackbar = Snackbar.make(view, message, Snackbar.LENGTH_SHORT);
snackbar.getView().setBackgroundColor(Color.YELLOW);
TextView tv =  snackbar.getView().findViewById(android.support.design.R.id.snackbar_text); //snackbar_text
tv.setTextColor(Color.BLACK);
snackbar.show();
}
}

This is how it looks:

yellow snackbar with black text

If you are in Kotlin, you can create an extension :

fun Snackbar.withTextColor(color: Int): Snackbar {
val tv = this.view.findViewById(android.support.design.R.id.snackbar_text) as TextView
tv.setTextColor(color)
return this
}

Usage :

yourSnackBar.withTextColor(Color.WHITE).show()

If you decide to use the dirty and hacky solution with finding TextView in Snackbar by id and you already migrated to androidx, then here's the code:

val textViewId = com.google.android.material.R.id.snackbar_text
val snackbar = Snackbar.make(view, "Text", Snackbar.LENGTH_SHORT)
val textView = snackbar.view.findViewById(textViewId) as TextView
textView.setTextColor(Color.WHITE)

If you migrated to androidX use com.google.android.material.R.id.snackbar_text instead of android.support.design.R.id.snackbar_text for changing color of text on snackbar.

If you will migrate your code to AndroidX, the TextView property is now:

com.google.android.material.R.id.snackbar_text

Find by id did't work for me so I found another solution:

Snackbar snackbar = Snackbar.make(view, text, duration);//just ordinary creation


ViewGroup snackbarView = (ViewGroup) snackbar.getView();
SnackbarContentLayout contentLayout = (SnackbarContentLayout) snackbarView.getChildAt(0);
TextView tvText = contentLayout.getMessageView();
tvText.setTextColor(/*your color here*/);


//set another colors, show, etc

As per new AndroidX Jitpack components

implementation 'com.google.android.material:material:1.0.0'

Use this extension which i had created

inline fun View.snack(message: String, length: Int = Snackbar.LENGTH_LONG,
f: Snackbar.() -> Unit) {
val snack = Snackbar.make(this, message, length)
snack.f()
snack.show()
}


fun Snackbar.action(action: String, actionColor: Int? = null, textColor: Int? = null, listener: (View) -> Unit) {
setAction(action, listener)
actionColor?.let {
setActionTextColor(it)
}
textColor?.let {
this.view.findViewById<TextView>(R.id.snackbar_text).setTextColor(it)
}
}

Use it like this

btn_login.snack(
getString(R.string.fields_empty_login),
ContextCompat.getColor(this@LoginActivity, R.color.whiteColor)
) {
action(getString(R.string.text_ok), ContextCompat.getColor(this@LoginActivity, R.color.gray_300),ContextCompat.getColor(this@LoginActivity, R.color.yellow_400)) {
this@snack.dismiss()
}
}

This is my workaround to solve this type of problem in androidx using kotlin

 fun showSnackBar(message: String) {
mContent?.let {
val snackbar = Snackbar.make(it, message, Snackbar.LENGTH_LONG)
val snackbarView = snackbar.view
val tv = snackbarView.findViewById<TextView>(R.id.snackbar_text)
tv.setTextColor(Color.WHITE) //change the color of text
tv.maxLines = 3 //specify the limit of text line
snackbar.duration = BaseTransientBottomBar.LENGTH_SHORT //specify the duraction of text message
snackbar.show()
}
}

You need initialize mContent inside onViewCreated method like below

var mContent: View? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mContent = view
}

Use the Snackbar included in the Material Components Library and apply the

Something like:

Snackbar snackbar = Snackbar.make(view, "My custom Snackbar", Snackbar.LENGTH_LONG);
snackbar.setTextColor(ContextCompat.getColor(this,R.color.xxxxx));
snackbar.setActionTextColor(ContextCompat.getColor(this,R.color.my_selector));
snackbar.setBackgroundTint(ContextCompat.getColor(this,R.color.xxxx));
snackbar.show();

enter image description here


With Jetpack Compose you can customize the SnackbarHost defining a custom Snackbar

    snackbarHost = {
// reuse default SnackbarHost to have default animation and timing handling
SnackbarHost(it) { data ->
Snackbar(
snackbarData = data,
contentColor = Yellow,
actionColor = Red.copy(alpha = 0.9f)
)
}
}

enter image description here

Then just use it:

scope.launch {
scaffoldState.snackbarHostState.showSnackbar(
message = "Snackbar text  # ${++clickCount}",
actionLabel = "Done")
}

Currently (January 2020) with com.google.android.material:material:1.2.0 and probably also 1.1.0

Is definitely the best way how to do it by overrides these styles:

<item name="snackbarStyle">@style/Widget.MaterialComponents.Snackbar</item>
<item name="snackbarButtonStyle">@style/Widget.MaterialComponents.Button.TextButton.Snackbar</item>
<item name="snackbarTextViewStyle">@style/Widget.MaterialComponents.Snackbar.TextView</item>

If you use a material theme with .Bridge at the end, for some reason, neither of these styles are defined. So Snackar will use some legacy layout without these styles.

I found in the source code that both snackbarButtonStyle and snackbarTextViewStyle must be defined otherwise it will be not used.

This worked for me

Snackbar.make(v, "Loading...", Snackbar.LENGTH_LONG).setTextColor(getColor(R.color.orange)).show();

Before that I declared my color in color.xml

<color name="orange">#FF9800</color>

You can try this code

Snackbar snackbar;
snackbar = Snackbar.make(binding.getRoot(), "Hello World!", Snackbar.LENGTH_INDEFINITE);
View snackBarView = snackbar.getView();
snackBarView.setBackgroundColor(getColor(R.color.purple_700));
snackbar.show();


snackbar.setTextColor(ContextCompat.getColor(this, R.color.white));
snackbar.setActionTextColor(ContextCompat.getColor(this, R.color.purple_200));
snackbar.setBackgroundTint(ContextCompat.getColor(this, R.color.purple_700));
snackbar.setAction("Ok", new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(MainActivity.this, "Click", Toast.LENGTH_SHORT).show();
}
});
snackbar.show();