Android 中带有渐变的文本
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2680607/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Text with gradient in Android
提问by Casebash
How would I extend TextView
to allow the drawing of text with a gradient effect?
我将如何扩展TextView
以允许绘制具有渐变效果的文本?
采纳答案by Casebash
It doesn't appear possible to extend TextView to draw text with a gradient. It is, however, possible to achieve this effect by creating a canvas and drawing on it. First we need to declare our custom UI element. In the initiation we need to create a subclass of Layout. In this case, we will use BoringLayoutwhich only supports text with a single line.
似乎无法扩展 TextView 以绘制带有渐变的文本。但是,可以通过创建画布并在其上绘图来实现此效果。首先,我们需要声明我们的自定义 UI 元素。在初始化中,我们需要创建Layout的子类。在这种情况下,我们将使用仅支持单行文本的BoringLayout。
Shader textShader=new LinearGradient(0, 0, 0, 20,
new int[]{bottom,top},
new float[]{0, 1}, TileMode.CLAMP);//Assumes bottom and top are colors defined above
textPaint.setTextSize(textSize);
textPaint.setShader(textShader);
BoringLayout.Metrics boringMetrics=BoringLayout.isBoring(text, textPaint);
boringLayout=new BoringLayout(text, textPaint, 0, Layout.Alignment.ALIGN_CENTER,
0.0f, 0.0f, boringMetrics, false);
We then override onMeasure
and onDraw
:
然后我们覆盖onMeasure
和onDraw
:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
setMeasuredDimension((int) textPaint.measureText(text), (int) textPaint.getFontSpacing());
}
@Override
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
boringLayout.draw(canvas);
}
Our implementation of onDraw
is at this point quite lazy (it completely ignores the measurement specs!, but so long as you guarantee that the view is given sufficent space, it should work okay.
我们的 实现onDraw
在这一点上非常懒惰(它完全忽略了测量规范!但只要你保证视图有足够的空间,它应该可以正常工作。
Alternatively, it would be possible to inherit from a Canvas
and override the onPaint
method. If this is done, then unfortunately the anchor for text being drawn will always be on the bottom so we have to add -textPaint.getFontMetricsInt().ascent()
to our y coordinate.
或者,可以从 a 继承Canvas
并覆盖该onPaint
方法。如果这样做,那么不幸的是,绘制文本的锚点将始终位于底部,因此我们必须添加-textPaint.getFontMetricsInt().ascent()
到我们的 y 坐标。
回答by Taras
TextView secondTextView = new TextView(this);
Shader textShader=new LinearGradient(0, 0, 0, 20,
new int[]{Color.GREEN,Color.BLUE},
new float[]{0, 1}, TileMode.CLAMP);
secondTextView.getPaint().setShader(textShader);
回答by hanswim
I have used the top answer(@Taras) with a gradient of 5 colors, but there is a problem: the textView looks like that I have put a white cover on it. Here is my code and the screenshot.
我使用了带有 5 种颜色渐变的最佳答案(@Taras),但是有一个问题:textView 看起来好像我在上面放了一个白色的封面。这是我的代码和屏幕截图。
textView = (TextView) findViewById(R.id.main_tv);
textView.setText("Tianjin, China".toUpperCase());
TextPaint paint = textView.getPaint();
float width = paint.measureText("Tianjin, China");
Shader textShader = new LinearGradient(0, 0, width, textView.getTextSize(),
new int[]{
Color.parseColor("#F97C3C"),
Color.parseColor("#FDB54E"),
Color.parseColor("#64B678"),
Color.parseColor("#478AEA"),
Color.parseColor("#8446CC"),
}, null, Shader.TileMode.CLAMP);
textView.getPaint().setShader(textShader);
After many hours, I found out that I need to call textView.setTextColor()
with the first color of the gradient. Then the screenshot:
几个小时后,我发现我需要textView.setTextColor()
使用渐变的第一种颜色进行调用。然后截图:
Hope help someone!
希望对某人有所帮助!
回答by koush
I've rolled up a library that encompasses both of these methods. You can create GradientTextView in XML or just use GradientTextView.setGradient(TextView textView...) to do it on a regular TextView object.
我已经汇总了一个包含这两种方法的库。您可以在 XML 中创建 GradientTextView 或仅使用 GradientTextView.setGradient(TextView textView...) 在常规 TextView 对象上执行此操作。
回答by Dustin
Here it is with multiline support as a one liner. This should work for Buttons too.
在这里,它具有多线支持作为单衬。这也适用于按钮。
Shader shader = new LinearGradient(0,0,0,textView.getLineHeight(),
startColor, endColor, Shader.TileMode.REPEAT);
textView.getPaint().setShader(shader);
回答by pgsandstrom
A simple but somewhat limited solution would be to use these attributes:
一个简单但有些有限的解决方案是使用这些属性:
android:fadingEdge="horizontal"
android:scrollHorizontally="true"
I have used it on textfields where I want them to fade out if they get too long.
我已经在文本字段上使用它,如果它们太长,我希望它们淡出。
回答by android developer
Here's a nice way to do it:
这是一个很好的方法:
/**
* sets a vertical gradient on the textView's paint, so that on its onDraw method, it will use it.
*
* @param viewAlreadyHasSize
* set to true only if the textView already has a size
*/
public static void setVerticalGradientOnTextView(final TextView tv, final int positionsAndColorsResId,
final boolean viewAlreadyHasSize) {
final String[] positionsAndColors = tv.getContext().getResources().getStringArray(positionsAndColorsResId);
final int[] colors = new int[positionsAndColors.length];
float[] positions = new float[positionsAndColors.length];
for (int i = 0; i < positionsAndColors.length; ++i) {
final String positionAndColors = positionsAndColors[i];
final int delimeterPos = positionAndColors.lastIndexOf(':');
if (delimeterPos == -1 || positions == null) {
positions = null;
colors[i] = Color.parseColor(positionAndColors);
} else {
positions[i] = Float.parseFloat(positionAndColors.substring(0, delimeterPos));
String colorStr = positionAndColors.substring(delimeterPos + 1);
if (colorStr.startsWith("0x"))
colorStr = '#' + colorStr.substring(2);
else if (!colorStr.startsWith("#"))
colorStr = '#' + colorStr;
colors[i] = Color.parseColor(colorStr);
}
}
setVerticalGradientOnTextView(tv, colors, positions, viewAlreadyHasSize);
}
/**
* sets a vertical gradient on the textView's paint, so that on its onDraw method, it will use it. <br/>
*
* @param colors
* the colors to use. at least one should exist.
* @param tv
* the textView to set the gradient on it
* @param positions
* where to put each color (fraction, max is 1). if null, colors are spread evenly .
* @param viewAlreadyHasSize
* set to true only if the textView already has a size
*/
public static void setVerticalGradientOnTextView(final TextView tv, final int[] colors, final float[] positions,
final boolean viewAlreadyHasSize) {
final Runnable runnable = new Runnable() {
@Override
public void run() {
final TileMode tile_mode = TileMode.CLAMP;
final int height = tv.getHeight();
final LinearGradient lin_grad = new LinearGradient(0, 0, 0, height, colors, positions, tile_mode);
final Shader shader_gradient = lin_grad;
tv.getPaint().setShader(shader_gradient);
}
};
if (viewAlreadyHasSize)
runnable.run();
else
runJustBeforeBeingDrawn(tv, runnable);
}
public static void runJustBeforeBeingDrawn(final View view, final Runnable runnable) {
final OnPreDrawListener preDrawListener = new OnPreDrawListener() {
@Override
public boolean onPreDraw() {
view.getViewTreeObserver().removeOnPreDrawListener(this);
runnable.run();
return true;
}
};
view.getViewTreeObserver().addOnPreDrawListener(preDrawListener);
}
Also, if you wish to use a bitmap of the gradient, instead or a real one, use:
此外,如果您希望使用渐变的位图或真实的位图,请使用:
/**
* sets an image for the textView <br/>
* NOTE: this function must be called after you have the view have its height figured out <br/>
*/
public static void setBitmapOnTextView(final TextView tv, final Bitmap bitmap) {
final TileMode tile_mode = TileMode.CLAMP;
final int height = tv.getHeight();
final int width = tv.getWidth();
final Bitmap temp = Bitmap.createScaledBitmap(bitmap, width, height, true);
final BitmapShader bitmapShader = new BitmapShader(temp, tile_mode, tile_mode);
tv.getPaint().setShader(bitmapShader);
}
EDIT: Alternative to runJustBeforeBeingDrawn: https://stackoverflow.com/a/28136027/878126
编辑:runJustBeforeBeingDrawn 的替代方法:https://stackoverflow.com/a/28136027/878126
回答by beniozhang
Here's my solved way. Implement with text span. screenshot
这是我解决的方法。使用文本跨度实现。 截屏
class LinearGradientForegroundSpan extends CharacterStyle implements UpdateAppearance {
private int startColor;
private int endColor;
private int lineHeight;
public LinearGradientForegroundSpan(int startColor, int endColor, int lineHeight) {
this.startColor = startColor;
this.endColor = endColor;
this.lineHeight = lineHeight;
}
@Override
public void updateDrawState(TextPaint tp) {
tp.setShader(new LinearGradient(0, 0, 0, lineHeight,
startColor, endColor, Shader.TileMode.REPEAT));
}
}
Styled your gradient text.
为您的渐变文本设置样式。
SpannableString gradientText = new SpannableString("Gradient Text");
gradientText.setSpan(new LinearGradientForegroundSpan(Color.RED, Color.LTGRAY, textView.getLineHeight()),
0, gradientText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
SpannableStringBuilder sb = new SpannableStringBuilder();
sb.append(gradientText);
sb.append(" Normal Text");
textView.setText(sb);
回答by Android
Here is an example for linearlayout, you can use this example for textview too, and in the source code there wont be gradient coding, you get the source code and add the code from that site itself - http://android-codes-examples.blogspot.com/2011/07/design-linearlayout-or-textview-and-any.html
这是线性布局的示例,您也可以将此示例用于 textview,并且在源代码中不会有梯度编码,您可以从该站点本身获取源代码并添加代码 - http://android-codes-examples .blogspot.com/2011/07/design-linearlayout-or-textview-and-any.html