Есть ли простой способ сделать текст черным? У меня есть текстовые представления, которые будут разных цветов, но некоторые цвета не так хорошо отображаются на моем фоне, поэтому мне было интересно, есть ли простой способ получить черный контур или что-то еще, что подойдет для этой работы? Я бы предпочел не создавать собственный вид, создавать холст и тому подобное.
83
Ответы:
Вы можете добавить тень за текстом, что часто может улучшить читаемость. Попробуйте поэкспериментировать с 50% полупрозрачными черными тенями на зеленом тексте. Подробности о том, как это сделать, находятся здесь: Android - тень на тексте?
Чтобы действительно добавить обводку вокруг текста, вам нужно сделать что-то более сложное, например: как нарисовать текст с рамкой на MapView в Android?
источник
Эффект контура может быть достигнут с помощью тени в 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"
источник
Итак, немного поздно, но 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" />
Примечание: я сделал это и публикую больше для будущих путешественников, чем OP. Это пограничный спам, но, возможно, приемлемо быть по теме?
источник
onDraw
рекурсивный вызов из-за вызоваsetTextColor
внутриonDraw
.Это довольно старый вопрос, но до сих пор я не вижу полных ответов. Итак, я публикую это решение, надеясь, что кто-то, борющийся с этой проблемой, сочтет его полезным. Самое простое и эффективное решение - переопределить метод onDraw класса TextView. Большинство реализаций, которые я видел, используют метод drawText для рисования штриха, но этот подход не учитывает все происходящее выравнивание форматирования и перенос текста. И в результате часто штрих и текст оказываются в разных местах. В следующем подходе super.onDraw используется для рисования как обводки, так и для заливки частей текста, поэтому вам не нужно беспокоиться об остальном. Вот шаги
И снова вызовите родительский класс 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 в подпапку «values» в папке «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. Вы можете добавить следующую строку в корневой узел файла макета.
xmlns:strokeAttrs="http://schemas.android.com/apk/res-auto"
источник
invalidate()
вызова, скрытого во внутренней работеsetTextColor
. Если вы не хотите копировать каждую последнюю строку кода изTextView
в свой собственный класс, единственный способ обойти это, который я вижу, - это грубый доступ к частномуmCurTextColor
полю сTextView
использованием Reflection. См. Этот ответ, чтобы примерно увидеть, как это сделать. Просто используйтеfield.set(this, colorInt)
вместо использованияfield.get()
.Фреймворк поддерживает тень текста, но не поддерживает контур текста. Но есть одна хитрость: тень - это нечто полупрозрачное и блеклое. Перерисуйте тень пару раз, и вся альфа будет суммирована, и в результате получится контур.
Очень простая реализация расширяет
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" />
источник
java.lang.NoSuchMethodException: <init> [class android.content.Context, interface android.util.AttributeSet
Я просто пытался понять, как это сделать, и не мог найти хорошего руководства в Интернете, но в конце концов понял это. Как предложил Стив Помрой, вам нужно сделать что-то более активное. Чтобы получить эффект обведенного текста, вы рисуете текст дважды: один раз толстым контуром, а затем второй раз мы рисуем основной текст поверх контура. Но задача упрощается, потому что вы можете очень легко адаптировать один из примеров кода, поставляемых с 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); }
Итак, чтобы получить эффект обрисованного текста, вы рисуете текст дважды: один раз толстым контуром, а затем второй раз мы рисуем основной текст поверх контура.
источник
кредит @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); } }
attr определить
<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" />
источник
Вот трюк, который, как я обнаружил, работает лучше, чем ход IMO MagicTextView.
@Override protected void onDraw(Canvas pCanvas) { int textColor = getTextColors().getDefaultColor(); setTextColor(mOutlineColor); // your stroke's 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); }
источник
TextView
в ваш собственный класс - это использовать Reflection для прямого доступа к частномуmCurTextColor
полю вTextView
. Этот ответ дает общее руководство, как это сделать. Если вы хотите, чтобы текст подсказки и ссылки также имел обводку, вам также придется изменитьmHintTextColor
иmLinkTextColor
. К сожалению, изменениеmTextColor
ничего не дает, поскольку на него только ссылаются.Я написал класс для выполнения текста с контуром и по-прежнему поддерживает все другие атрибуты и рисование обычного текстового представления.
он в основном использует
super.onDraw(Canves canvas)
on the,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>
источник
Вы можете сделать это программно с помощью приведенного ниже фрагмента. Это дает белые буквы на черном фоне:
textView.setTextColor(Color.WHITE); textView.setShadowLayer(1.6f,1.5f,1.3f,Color.BLACK);
Параметры метода: радиус, dx, dy, цвет. Вы можете изменить их под свои нужды.
Надеюсь, я помогу кому-то, кто создает TextView программно и не имеет его внутри xml.
Приветствую сообщество stackOverflow!
источник
Я создал библиотеку на основе ответа Нумана Ханифа с некоторыми дополнениями. Например, исправление ошибки, которая вызывала косвенный бесконечный цикл при вызовах View.invalidate ().
OTOH, библиотека также поддерживает выделенный текст в виджетах EditText, поскольку это была моя настоящая цель, и для этого потребовалось немного больше работы, чем для TextView.
Вот ссылка на мою библиотеку: https://github.com/biomorgoth/android-outline-textview
Спасибо Nouman Hanif за первоначальную идею решения!
источник
Я хочу добавить решение, чтобы решить проблему с производительностью. Например, ответ @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) } }
источник
MagicTextView очень полезно сделать обводки шрифта, но в моем случае, это может вызвать ошибку , как эта этой ошибку , вызванной дублирование фоновых атрибутов , которые устанавливают на 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)); } }
источник
Я нашел простой способ конструировать вид без наследования от 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 = this@OutlineSpan.strokeWidth } 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
источник
Итак, вы хотите обвести текстовое поле обводкой? К сожалению, нет простого способа сделать это с помощью стиля. Вам нужно будет создать другое представление и разместить текстовое представление поверх, сделав родительский вид (тот, над которым он находится) всего на несколько пикселей больше - это должно создать контур.
источник
Вот самый простой способ, который я смог найти, расширив TextView
public class CustomTextView extends androidx.appcompat.widget.AppCompatTextView { float mStroke; public CustomTextView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomTextView); mStroke=a.getFloat(R.styleable.CustomTextView_stroke,1.0f); a.recycle(); } @Override protected void onDraw(Canvas canvas) { TextPaint paint = this.getPaint(); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(mStroke); super.onDraw(canvas); } }
тогда вам нужно только добавить следующее в файл attrs.xml
<declare-styleable name="CustomTextView"> <attr name="stroke" format="float"/> </declare-styleable>
и теперь вы сможете установить ширину обводки,
app:stroke
сохранив при этом все другие желаемые свойства TextView. мое решение рисует только обводку без заливки. это делает его немного проще, чем другие. ниже снимок экрана с результатом при установке пользовательского шрифта для моего customtextview.источник