Есть ли простой способ иметь текст, чтобы иметь черный контур? У меня есть текстовые изображения, которые будут разных цветов, но некоторые цвета не отображаются на моем фоне так хорошо, поэтому мне было интересно, есть ли простой способ получить черный контур или что-то еще, что сделает работу? Я бы предпочел не создавать пользовательский вид и делать холст и т.д.
Текст текста в текстовом виде Android
Ответ 1
Вы можете поместить тень за текст, что часто помогает читабельности. Попробуйте поэкспериментировать с 50% полупрозрачными черными тенями на вашем зеленом тексте. Подробная информация о том, как это сделать, приведена здесь: Android - тень на тексте?
Чтобы действительно добавить штрих вокруг текста, вам нужно сделать что-то более активное, например: Как вы рисуете текст с помощью рамки на MapView в Android?
Ответ 2
Контурный эффект может быть достигнут с использованием тени в TextView:
android:shadowColor="#000000"
android:shadowDx="1.5"
android:shadowDy="1.3"
android:shadowRadius="1.6"
android:text="CCC"
android:textAllCaps="true"
android:textColor="@android:color/white"
Ответ 3
Итак, немного поздно, но MagicTextView будет делать текстовые очертания, между прочим.
<com.qwerjk.better_text.MagicTextView
xmlns:qwerjk="http://schemas.android.com/apk/res/com.qwerjk.better_text"
android:textSize="78dp"
android:textColor="#ff333333"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
qwerjk:strokeColor="#FFff0000"
qwerjk:strokeJoinStyle="miter"
qwerjk:strokeWidth="5"
android:text="Magic" />
Примечание: Я сделал это и отправляю больше ради будущих путешественников, чем ОП. Это пограничный спам, но по теме, возможно, приемлемый?
Ответ 4
Каркас поддерживает текстовую тень, но не поддерживает текстовый контур. Но есть хитрость: тень - это нечто полупрозрачное и исчезающее. Перерисовать тень пару раз, и вся альфа суммируется, и в результате получается контур.
Очень простая реализация расширяет TextView
и переопределяет метод draw(..)
. Каждый раз, когда запрашивается ничья, наш подкласс делает 5-10 рисунков.
public class OutlineTextView extends TextView {
// Constructors
@Override
public void draw(Canvas canvas) {
for (int i = 0; i < 5; i++) {
super.draw(canvas);
}
}
}
<OutlineTextView
android:shadowColor="#000"
android:shadowRadius="3.0" />
Ответ 5
Это довольно старый вопрос, но я не вижу никаких полных ответов. Поэтому я отправляю это решение, надеясь, что кто-то, кто борется с этой проблемой, может оказаться полезным. Самое простое и эффективное решение - переопределить метод onDraw класса TextView. Большинство реализаций, которые я видел, используют метод drawText для рисования штриха, но этот подход не учитывает все выравнивание форматирования и перенос текста. И в результате часто штрих и текст заканчиваются в разных местах. Следующий подход использует super.onDraw, чтобы нарисовать как штрих, так и заполнять части текста, чтобы вам не приходилось беспокоиться о остальной части материала. Вот шаги
- Расширить класс TextView
- Переопределить метод onDraw
- Установить стиль рисования для FILL
- вызов родительского класса в Draw для рендеринга текста в заполнении Режим.
- сохранить текущий цвет текста.
- Установите текущий цвет текста на цвет вашего штриха.
- Установить стиль рисования на обводку
- Установить ширину хода
-
И снова вызовите родительский класс onDraw, чтобы нарисовать штрих ранее отображаемый текст.
package com.example.widgets; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Typeface; import android.util.AttributeSet; import android.widget.Button; public class StrokedTextView extends Button { private static final int DEFAULT_STROKE_WIDTH = 0; // fields private int _strokeColor; private float _strokeWidth; // constructors public StrokedTextView(Context context) { this(context, null, 0); } public StrokedTextView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public StrokedTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); if(attrs != null) { TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.StrokedTextAttrs); _strokeColor = a.getColor(R.styleable.StrokedTextAttrs_textStrokeColor, getCurrentTextColor()); _strokeWidth = a.getFloat(R.styleable.StrokedTextAttrs_textStrokeWidth, DEFAULT_STROKE_WIDTH); a.recycle(); } else { _strokeColor = getCurrentTextColor(); _strokeWidth = DEFAULT_STROKE_WIDTH; } //convert values specified in dp in XML layout to //px, otherwise stroke width would appear different //on different screens _strokeWidth = dpToPx(context, _strokeWidth); } // getters + setters public void setStrokeColor(int color) { _strokeColor = color; } public void setStrokeWidth(int width) { _strokeWidth = width; } // overridden methods @Override protected void onDraw(Canvas canvas) { if(_strokeWidth > 0) { //set paint to fill mode Paint p = getPaint(); p.setStyle(Paint.Style.FILL); //draw the fill part of text super.onDraw(canvas); //save the text color int currentTextColor = getCurrentTextColor(); //set paint to stroke mode and specify //stroke color and width p.setStyle(Paint.Style.STROKE); p.setStrokeWidth(_strokeWidth); setTextColor(_strokeColor); //draw text stroke super.onDraw(canvas); //revert the color back to the one //initially specified setTextColor(currentTextColor); } else { super.onDraw(canvas); } } /** * Convenience method to convert density independent pixel(dp) value * into device display specific pixel value. * @param context Context to access device specific display metrics * @param dp density independent pixel value * @return device specific pixel value. */ public static int dpToPx(Context context, float dp) { final float scale= context.getResources().getDisplayMetrics().density; return (int) (dp * scale + 0.5f); } }
Вот и все. Этот класс использует пользовательские атрибуты XML, чтобы разрешить указывать цвет и ширину штриха из файлов макета XML. Поэтому вам нужно добавить эти атрибуты в файл attr.xml в значениях подпапки под папкой "res". Скопируйте и вставьте следующее в файл attr.xml.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="StrokedTextAttrs">
<attr name="textStrokeColor" format="color"/>
<attr name="textStrokeWidth" format="float"/>
</declare-styleable>
</resources>
Как только вы закончите с этим, вы можете использовать собственный класс StrokedTextView в ваших файлах макета XML и указать цвет и ширину штриха. Вот пример
<com.example.widgets.StrokedTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Stroked text sample"
android:textColor="@android:color/white"
android:textSize="25sp"
strokeAttrs:textStrokeColor="@android:color/black"
strokeAttrs:textStrokeWidth="1.7" />
Не забудьте заменить имя пакета на название своего проекта. Также добавьте пространство имен xmlns в файл макета, чтобы использовать пользовательские атрибуты XML. Вы можете добавить следующую строку в свой файл макета root node.
xmlns:strokeAttrs="http://schemas.android.com/apk/res-auto"
Ответ 6
Я только пытался выяснить, как это сделать, и не смог найти хорошего гида онлайн, но в итоге понял это. Как предложил Стив Померой, вам нужно сделать что-то более активное. Чтобы получить обведенный текстовый эффект, вы рисуете текст дважды: один раз с густым контуром, а затем во второй раз мы рисуем основной текст над контуром. Но задача сделана проще, потому что вы можете очень легко адаптировать один из примеров кода, поставляемых с SDK, а именно под этим именем в вашем каталоге SDK: "/samples/android-/ApiDemos/src/com/example/android/apis/view/LabelView.java". Что также можно найти на веб-сайте разработчика Android здесь.
В зависимости от того, что вы делаете, очень легко увидеть, что вам нужно будет внести незначительные изменения в этот код, например, изменить его на расширение от TextView и т.д. Прежде чем я обнаружил этот образец, я забыл переопределить onMeasure ( ) (что вы должны сделать в дополнение к переопределению onDraw(), как указано в руководстве "Создание пользовательских компонентов" на веб-сайте разработчика Android), что является частью того, почему у меня были проблемы.
Как только вы это сделаете, вы можете делать то, что я сделал:
public class TextViewOutline extends TextView {
private Paint mTextPaint;
private Paint mTextPaintOutline; //add another paint attribute for your outline
...
//modify initTextViewOutline to setup the outline style
private void initTextViewOutline() {
mTextPaint = new Paint();
mTextPaint.setAntiAlias(true);
mTextPaint.setTextSize(16);
mTextPaint.setColor(0xFF000000);
mTextPaint.setStyle(Paint.Style.FILL);
mTextPaintOutline = new Paint();
mTextPaintOutline.setAntiAlias(true);
mTextPaintOutline.setTextSize(16);
mTextPaintOutline.setColor(0xFF000000);
mTextPaintOutline.setStyle(Paint.Style.STROKE);
mTextPaintOutline.setStrokeWidth(4);
setPadding(3, 3, 3, 3);
}
...
//make sure to update other methods you've overridden to handle your new paint object
...
//and finally draw the text, mAscent refers to a member attribute which had
//a value assigned to it in the measureHeight and Width methods
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawText(mText, getPaddingLeft(), getPaddingTop() - mAscent,
mTextPaintOutline);
canvas.drawText(mText, getPaddingLeft(), getPaddingTop() - mAscent, mTextPaint);
}
Итак, чтобы получить обведенный текстовый эффект, вы рисуете текст дважды: один раз с толстым контуром, а затем второй раз рисуем основной текст над контуром.
Ответ 7
Вот трюк, который я нашел, который работает лучше, чем MagicTextView stroke IMO
@Override
protected void onDraw(Canvas pCanvas) {
int textColor = getTextColors().getDefaultColor();
setTextColor(mOutlineColor); // your stroke color
getPaint().setStrokeWidth(10);
getPaint().setStyle(Paint.Style.STROKE);
super.onDraw(pCanvas);
setTextColor(textColor);
getPaint().setStrokeWidth(0);
getPaint().setStyle(Paint.Style.FILL);
super.onDraw(pCanvas);
}
Ответ 8
Я написал класс для выполнения текста с контуром и все еще поддерживаю все остальные атрибуты и рисунок обычного текстового представления.
он в основном использует super.onDraw(Canves canvas)
в TextView
, но рисует дважды с разными стилями.
надеюсь, что это поможет.
public class TextViewOutline extends TextView {
// constants
private static final int DEFAULT_OUTLINE_SIZE = 0;
private static final int DEFAULT_OUTLINE_COLOR = Color.TRANSPARENT;
// data
private int mOutlineSize;
private int mOutlineColor;
private int mTextColor;
private float mShadowRadius;
private float mShadowDx;
private float mShadowDy;
private int mShadowColor;
public TextViewOutline(Context context) {
this(context, null);
}
public TextViewOutline(Context context, AttributeSet attrs) {
super(context, attrs);
setAttributes(attrs);
}
private void setAttributes(AttributeSet attrs){
// set defaults
mOutlineSize = DEFAULT_OUTLINE_SIZE;
mOutlineColor = DEFAULT_OUTLINE_COLOR;
// text color
mTextColor = getCurrentTextColor();
if(attrs != null) {
TypedArray a = getContext().obtainStyledAttributes(attrs,R.styleable.TextViewOutline);
// outline size
if (a.hasValue(R.styleable.TextViewOutline_outlineSize)) {
mOutlineSize = (int) a.getDimension(R.styleable.TextViewOutline_outlineSize, DEFAULT_OUTLINE_SIZE);
}
// outline color
if (a.hasValue(R.styleable.TextViewOutline_outlineColor)) {
mOutlineColor = a.getColor(R.styleable.TextViewOutline_outlineColor, DEFAULT_OUTLINE_COLOR);
}
// shadow (the reason we take shadow from attributes is because we use API level 15 and only from 16 we have the get methods for the shadow attributes)
if (a.hasValue(R.styleable.TextViewOutline_android_shadowRadius)
|| a.hasValue(R.styleable.TextViewOutline_android_shadowDx)
|| a.hasValue(R.styleable.TextViewOutline_android_shadowDy)
|| a.hasValue(R.styleable.TextViewOutline_android_shadowColor)) {
mShadowRadius = a.getFloat(R.styleable.TextViewOutline_android_shadowRadius, 0);
mShadowDx = a.getFloat(R.styleable.TextViewOutline_android_shadowDx, 0);
mShadowDy = a.getFloat(R.styleable.TextViewOutline_android_shadowDy, 0);
mShadowColor = a.getColor(R.styleable.TextViewOutline_android_shadowColor, Color.TRANSPARENT);
}
a.recycle();
}
PFLog.d("mOutlineSize = " + mOutlineSize);
PFLog.d("mOutlineColor = " + mOutlineColor);
}
private void setPaintToOutline(){
Paint paint = getPaint();
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(mOutlineSize);
super.setTextColor(mOutlineColor);
super.setShadowLayer(mShadowRadius, mShadowDx, mShadowDy, mShadowColor);
}
private void setPaintToRegular() {
Paint paint = getPaint();
paint.setStyle(Paint.Style.FILL);
paint.setStrokeWidth(0);
super.setTextColor(mTextColor);
super.setShadowLayer(0, 0, 0, Color.TRANSPARENT);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setPaintToOutline();
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
public void setTextColor(int color) {
super.setTextColor(color);
mTextColor = color;
}
@Override
public void setShadowLayer(float radius, float dx, float dy, int color) {
super.setShadowLayer(radius, dx, dy, color);
mShadowRadius = radius;
mShadowDx = dx;
mShadowDy = dy;
mShadowColor = color;
}
public void setOutlineSize(int size){
mOutlineSize = size;
}
public void setOutlineColor(int color){
mOutlineColor = color;
}
@Override
protected void onDraw(Canvas canvas) {
setPaintToOutline();
super.onDraw(canvas);
setPaintToRegular();
super.onDraw(canvas);
}
}
attr.xml
<declare-styleable name="TextViewOutline">
<attr name="outlineSize" format="dimension"/>
<attr name="outlineColor" format="color|reference"/>
<attr name="android:shadowRadius"/>
<attr name="android:shadowDx"/>
<attr name="android:shadowDy"/>
<attr name="android:shadowColor"/>
</declare-styleable>
Ответ 9
Вы можете сделать это программно с помощью нижеприведенного фрагмента. Это дает белые буквы с черным фоном:
textView.setTextColor(Color.WHITE);
textView.setShadowLayer(1.6f,1.5f,1.3f,Color.BLACK);
Параметры метода: радиус, dx, dy, цвет. Вы можете изменить их для своих нужд.
Надеюсь, что я помогу кому-то, кто создает TextView программно и не имеет его внутри xml.
Приветствия сообществу stackOverflow!
Ответ 10
кредит @YGHM добавить поддержку теней
package com.megvii.demo;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
public class TextViewOutline extends android.support.v7.widget.AppCompatTextView {
// constants
private static final int DEFAULT_OUTLINE_SIZE = 0;
private static final int DEFAULT_OUTLINE_COLOR = Color.TRANSPARENT;
// data
private int mOutlineSize;
private int mOutlineColor;
private int mTextColor;
private float mShadowRadius;
private float mShadowDx;
private float mShadowDy;
private int mShadowColor;
public TextViewOutline(Context context) {
this(context, null);
}
public TextViewOutline(Context context, AttributeSet attrs) {
super(context, attrs);
setAttributes(attrs);
}
private void setAttributes(AttributeSet attrs) {
// set defaults
mOutlineSize = DEFAULT_OUTLINE_SIZE;
mOutlineColor = DEFAULT_OUTLINE_COLOR;
// text color
mTextColor = getCurrentTextColor();
if (attrs != null) {
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.TextViewOutline);
// outline size
if (a.hasValue(R.styleable.TextViewOutline_outlineSize)) {
mOutlineSize = (int) a.getDimension(R.styleable.TextViewOutline_outlineSize, DEFAULT_OUTLINE_SIZE);
}
// outline color
if (a.hasValue(R.styleable.TextViewOutline_outlineColor)) {
mOutlineColor = a.getColor(R.styleable.TextViewOutline_outlineColor, DEFAULT_OUTLINE_COLOR);
}
// shadow (the reason we take shadow from attributes is because we use API level 15 and only from 16 we have the get methods for the shadow attributes)
if (a.hasValue(R.styleable.TextViewOutline_android_shadowRadius)
|| a.hasValue(R.styleable.TextViewOutline_android_shadowDx)
|| a.hasValue(R.styleable.TextViewOutline_android_shadowDy)
|| a.hasValue(R.styleable.TextViewOutline_android_shadowColor)) {
mShadowRadius = a.getFloat(R.styleable.TextViewOutline_android_shadowRadius, 0);
mShadowDx = a.getFloat(R.styleable.TextViewOutline_android_shadowDx, 0);
mShadowDy = a.getFloat(R.styleable.TextViewOutline_android_shadowDy, 0);
mShadowColor = a.getColor(R.styleable.TextViewOutline_android_shadowColor, Color.TRANSPARENT);
}
a.recycle();
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setPaintToOutline();
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
private void setPaintToOutline() {
Paint paint = getPaint();
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(mOutlineSize);
super.setTextColor(mOutlineColor);
super.setShadowLayer(0, 0, 0, Color.TRANSPARENT);
}
private void setPaintToRegular() {
Paint paint = getPaint();
paint.setStyle(Paint.Style.FILL);
paint.setStrokeWidth(0);
super.setTextColor(mTextColor);
super.setShadowLayer(mShadowRadius, mShadowDx, mShadowDy, mShadowColor);
}
@Override
public void setTextColor(int color) {
super.setTextColor(color);
mTextColor = color;
}
public void setOutlineSize(int size) {
mOutlineSize = size;
}
public void setOutlineColor(int color) {
mOutlineColor = color;
}
@Override
protected void onDraw(Canvas canvas) {
setPaintToOutline();
super.onDraw(canvas);
setPaintToRegular();
super.onDraw(canvas);
}
}
определить
<declare-styleable name="TextViewOutline">
<attr name="outlineSize" format="dimension"/>
<attr name="outlineColor" format="color|reference"/>
<attr name="android:shadowRadius"/>
<attr name="android:shadowDx"/>
<attr name="android:shadowDy"/>
<attr name="android:shadowColor"/>
</declare-styleable>
код XML ниже
<com.megvii.demo.TextViewOutline
android:id="@+id/product_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="110dp"
android:background="#f4b222"
android:fontFamily="@font/kidsmagazine"
android:padding="10dp"
android:shadowColor="#d7713200"
android:shadowDx="0"
android:shadowDy="8"
android:shadowRadius="1"
android:text="LIPSTICK SET"
android:textColor="@android:color/white"
android:textSize="30sp"
app:outlineColor="#cb7800"
app:outlineSize="3dp" />
Ответ 11
Значит, вам нужен обводки вокруг текста? К сожалению, нет простого способа сделать это с помощью стиля. Вам нужно будет создать другое представление и поместить ваше текстовое изображение сверху, сделав родительский вид (тот, что он сверху) всего на несколько пикселей больше - это должно создать контур.
Ответ 12
MagicTextView очень полезен для создания шрифта штриха, но в моем случае он вызывает ошибку, например this эта ошибка вызвана атрибутами фона дублирования, которые установлены MagicTextView
поэтому вам нужно отредактировать attrs.xml и MagicTextView.java
attrs.xml
<attr name="background" format="reference|color" />
↓
<attr name="mBackground" format="reference|color" />
MagicTextView.java 88:95
if (a.hasValue(R.styleable.MagicTextView_mBackground)) {
Drawable background = a.getDrawable(R.styleable.MagicTextView_mBackground);
if (background != null) {
this.setBackgroundDrawable(background);
} else {
this.setBackgroundColor(a.getColor(R.styleable.MagicTextView_mBackground, 0xff000000));
}
}
Ответ 13
Я создал библиотеку на основе Нуман Ханиф с некоторыми дополнениями. Например, исправление ошибки, вызвавшей косвенный бесконечный цикл при вызовах View.invalidate().
OTOH, библиотека также поддерживает выделенный текст в виджетах EditText, так как это была моя настоящая цель, и для этого требовалась немного больше работы, чем TextView.
Вот ссылка на мою библиотеку: https://github.com/biomorgoth/android-outline-textview
Спасибо Нуману Ханифу за первоначальную идею о решении!
Ответ 14
Я нашел простой способ выделить вид без наследования из TextView. Я написал простую библиотеку, которая использует Android Spannable для выделения текста. Это решение дает возможность выделить только часть текста.
Я уже ответил на тот же вопрос (ответ)
Класс:
class OutlineSpan(
@ColorInt private val strokeColor: Int,
@Dimension private val strokeWidth: Float
): ReplacementSpan() {
override fun getSize(
paint: Paint,
text: CharSequence,
start: Int,
end: Int,
fm: Paint.FontMetricsInt?
): Int {
return paint.measureText(text.toString().substring(start until end)).toInt()
}
override fun draw(
canvas: Canvas,
text: CharSequence,
start: Int,
end: Int,
x: Float,
top: Int,
y: Int,
bottom: Int,
paint: Paint
) {
val originTextColor = paint.color
paint.apply {
color = strokeColor
style = Paint.Style.STROKE
this.strokeWidth = [email protected]
}
canvas.drawText(text, start, end, x, y.toFloat(), paint)
paint.apply {
color = originTextColor
style = Paint.Style.FILL
}
canvas.drawText(text, start, end, x, y.toFloat(), paint)
}
}
Библиотека: OutlineSpan
Ответ 15
Я хочу добавить решение, чтобы решить проблему производительности. Например, ответ @YGHM и нескольких других делает свою работу, но это вызывает бесконечный вызов onDraw
потому что setTextColor
вызывает invalidate()
. Поэтому, чтобы решить эту проблему, вам также необходимо переопределить invalidate()
и добавить переменную isDrawing
которой вы установите значение true
, когда onDraw()
выполняется и рисует штрихом. invalidate вернется, если переменная true
.
override fun invalidate() {
if (isDrawing) return
super.invalidate()
}
Ваш onDraw будет выглядеть так:
override fun onDraw(canvas: Canvas) {
if (strokeWidth > 0) {
isDrawing = true
val textColor = textColors.defaultColor
setTextColor(strokeColor)
paint.strokeWidth = strokeWidth
paint.style = Paint.Style.STROKE
super.onDraw(canvas)
setTextColor(textColor)
paint.strokeWidth = 0f
paint.style = Paint.Style.FILL
isDrawing = false
super.onDraw(canvas)
} else {
super.onDraw(canvas)
}
}