Текст тексту тексту Androidview


83

Чи є простий спосіб, щоб текст міг мати чорний контур? У мене є текстові перегляди, які будуть різних кольорів, але деякі кольори не так добре відображаються на моєму фоні, тому мені було цікаво, чи є простий спосіб отримати чорний контур чи щось інше, що зробить цю роботу? Я вважаю за краще не створювати власний вигляд і робити полотно тощо.


7
Для тих, хто читає це питання та розглядає можливість використання рішення Paint-Stroke, зверніть увагу, що в Android 4.4 є помилка з обведеннями . Якщо розмір тексту перевищує 256 пікселів, це призводить до дуже дивного обведення. Обхідним шляхом є накреслення контуру / обведення альтернативним методом, представленим у цій відповіді . Я не хотів спамувати це на кожну відповідь типу Stroke, тому розміщуючи це тут, щоб люди усвідомлювали і рятували їх горе, яке я пережив.
Tony Chan

Відповіді:


56

Ви можете нанести тінь за текст, що часто може допомогти прочитати. Спробуйте поекспериментувати з 50% напівпрозорими чорними тінями на вашому зеленому тексті. Детальна інформація про те, як це зробити, закінчена тут: Android - тінь на тексті?

Щоб дійсно додати обведення навколо тексту, потрібно зробити дещо більш залучене, наприклад: Як малювати текст з межею на MapView в Android?


3
Зверніть увагу, що в Android 4.4 є помилка з обведеннями . Якщо розмір тексту перевищує 256 пікселів, це призводить до дуже дивного обведення. Обхідним шляхом є накреслення контуру / обведення альтернативним методом, представленим у цій відповіді .
Тоні Чан

Чи відноситься цей коментар до перегляду тексту чи розміру шрифту?
Джон

тінь недостатньо хороша, білий текст на макеті білого тла все ще виглядає дуже погано з цією чорною тінню
user924

82

ефект контуру можна досягти за допомогою тіні в 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"

це має бути найкращим рішенням. Це було чудово! Дякую
Ренан Бандейра

5
Це не призводить до окреслення, оскільки воно відображається лише з двох сторін.
заборона геоінженерії

Мені ідеально !!
Елі Дантас,

ця тінь дуже слабка для контуру
user924

55

Отже, трохи пізно, але 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. Це прикордонний спам, але бути присвяченим темі, можливо, прийнятно?


1
Привіт, як ми можемо додати такі межі до тексту, що вводиться в EditText?
TilalHusain

Будь-які ідеї щодо EditText?
Piotr

dreamText.setStroke (4, Color.BLACK); dreamText.setTextColor (Color.WHITE); Я ВИКОРИСТОВУЮ ці налаштування, але мій колір тексту прозорий, я бачу чорний контур. Що не так?
Мухаммед Умар

нічого страшного, але насправді не додає межі. Він скоріше бере текст і використовує зовнішній край як межу, що не дає однакового візуального результату.
Warpzit

1
Це рішення викликає onDrawрекурсивний виклик через виклик setTextColorвсередині onDraw.
Серміліон

23

Це досить давнє запитання, але все одно я не бачу повної відповіді. Тому я публікую це рішення, сподіваючись, що хтось, хто бореться з цією проблемою, може виявитися корисним. Найпростішим і найефективнішим рішенням є перевизначення методу onDraw класу TextView. Більшість реалізацій, які я бачив, використовують метод drawText для малювання штриху, але цей підхід не враховує всі вирівнювання форматування та обтікання тексту, що входять. І як результат, часто обведення та текст потрапляють в різні місця. Наступний підхід використовує super.onDraw, щоб намалювати обведення та заповнити частини тексту, щоб вам не довелося турбуватися про решту матеріалів. Ось кроки

  1. Розширити клас TextView
  2. Перевизначити метод onDraw
  3. Встановіть стиль фарби на ЗАПОВНЕННЯ
  4. викликати батьківський клас на Draw, щоб відтворити текст у режимі заливки.
  5. зберегти поточний колір тексту -.
  6. Встановіть для поточного кольору тексту колір обведення
  7. Встановіть стиль фарби на Обведення
  8. Встановити ширину обведення
  9. І знову зателефонуйте батьківському класу 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 у підпапці 'Значення' в папці 'Рез'. Скопіюйте та вставте наступне у свій файл 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"

2
Яке чудове, елегантне рішення! Я реалізував це, і воно працює добре. Я щойно змінив textStrokeWidth на розмірність (і a.getDimensionPixelSize). Дякую!
dgmltn

1
Працює добре, дякую. Коли вони накреслили весь текст, я змінив порядок у своєму випадку: спочатку малюється контур, а потім текст.
mdiener

2
Чудово працює. Не використовуйте дизайн дизайну Android Studio для тестування контурів, подання є недостатньо точним. Щойно витратив 2 години на налагодження не-проблеми.
llmora

7
Це рішення призведе до нескінченної кількості onDraw, оскільки виклики setTextColor недійсні.
Гуляш

2
Насправді, @Guliash правильний. Після тестування, коли цей метод називається він викликає нескінченний цикл , які називають себе з - за invalidate()виклик похований в внутрішніх роботах setTextColor. Якщо ви не хочете скопіювати кожен останній рядок коду TextViewу свій власний клас, єдиний спосіб обійти це, що я бачу, - це грубий доступ до приватного mCurTextColor поля TextViewвикористання Reflection. Дивіться цю відповідь, щоб приблизно зрозуміти, як це зробити. Просто використовуйте field.set(this, colorInt)замість використання field.get().
VerumCH

23

Фреймворк підтримує text-shadow, але не підтримує контур тексту. Але є хитрість: тінь - це щось напівпрозоре і тьмяніє. Перемалюйте тінь пару разів, і всі альфа-суми підсумовуються, і результат є контуром.

Дуже проста реалізація розширює 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" />

3
Дуже дякую. Однак я скоріше використовую цей метод: '@Override protected void onDraw (Canvas canvas) {for (int i = 0; i <5; i ++) {super.onDraw (canvas); }} '
IHeartAndroid

1
Додаткова інформація: Потрібно впровадити принаймні ctor із Context та AttributeSet. Інакше натрапляєш. java.lang.NoSuchMethodException: <init> [class android.content.Context, interface android.util.AttributeSet
Бевор,

15

Я просто намагався зрозуміти, як це зробити, і не зміг знайти хорошого путівника в Інтернеті, але врешті-решт це зрозумів. Як запропонував Стів Померой, вам потрібно зробити щось більш залучене. Для того, щоб отримати ефект окресленого тексту, ви малюєте текст двічі: один раз товстим контуром, а потім другий раз малюємо основний текст над контуром. Але завдання полегшено, оскільки ви можете дуже легко адаптувати один із зразків коду, що надається разом із SDK, а саме той під цим іменем у вашому каталозі SDK: "/ sample / 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);
   }

Отже, щоб отримати контурний текстовий ефект, ви малюєте текст двічі: один раз товстим контуром, а потім другий раз малюємо основний текст над контуром.


9

кредит @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" />

8

Ось фокус, який я виявив, працює ефективніше, ніж обведення IMT 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 обрізається - і контур не повністю намальований на цій стороні ... як ніби закінчується місце
RoundSparrow hilltx

7
Іще одне. Я підозрюю, що setTextColor змушує перемальовувати - що призводить до того, що нескінченний цикл цього onDraw викликається знову і знову. Під час тестування рекомендується застосовувати logcat або інший індикатор у цьому методі.
RoundSparrow hilltx

@RoundSparrowhilltx є правильним. Як я вже згадував у коментарі до іншої подібної відповіді, я підозрюю, що єдиним способом обійти цей короткий варіант копіювання та вставки цілого TextViewу свій власний клас є використання Reflection для безпосереднього доступу до приватного mCurTextColor поля в TextView. Ця відповідь дає загальний настанов, як це зробити. Якщо ви хочете, щоб текст підказки та посилання також мав обведення, вам також доведеться змінити mHintTextColorі mLinkTextColor. На жаль, зміна mTextColorнічого не робить, оскільки на неї посилаються лише.
VerumCH

in will loop onDraw forever
user924

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>

a.recycle () на TypedArray відсутній
Леос Літерак

5

Ви можете зробити це програмно за допомогою наведеного нижче фрагмента. Це забезпечує білі літери з чорним фоном:

textView.setTextColor(Color.WHITE);            
textView.setShadowLayer(1.6f,1.5f,1.3f,Color.BLACK);

Параметри методу - радіус, dx, dy, колір. Ви можете змінити їх для своїх конкретних потреб.

Сподіваюся, я допоможу комусь, хто створює TextView програмно і не має його в xml.

Вітаємо із спільнотою steckOverflow!


2

Я створив бібліотеку на основі відповіді Нумена Ханіфа з деякими доповненнями. Наприклад, виправлення помилки, яка спричинила непрямий нескінченний цикл у викликах View.invalidate ().

OTOH, бібліотека також підтримує окреслений текст у віджетах EditText, оскільки це було моєю справжньою метою, і вона потребувала трохи більше роботи, ніж TextView.

Ось посилання на мою бібліотеку: https://github.com/biomorgoth/android-outline-textview

Дякуємо Нуману Ханіфу за початкову ідею рішення!


2

Я хочу додати рішення для вирішення проблеми продуктивності. Наприклад, відповідь @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)
    }
  }

1

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));
}
}

1

Я знайшов простий спосіб окреслити подання без успадкування від TextView . Я написав просту бібліотеку, яка використовує Spannable для Android для окреслення тексту. Це рішення дає можливість окреслити лише частину тексту.

Я вже відповів на те саме питання ( відповідь )

Клас:

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


1
він не підтримує багаторядковий текст
user924

0

Отже, вам потрібен штрих навколо перегляду тексту? На жаль, немає простого способу зробити це за допомогою укладання. Вам доведеться створити інший вигляд і розмістити текстовий вигляд зверху, зробивши батьківський вигляд (той, який знаходиться зверху) просто на кілька пікселів більшим - це має створити контур.


Хм, це звучить як біль, ніж це варте. Мені цікаво лише те, щоб зелений текст читався на білому фоні (зараз це важко читати) img88.imageshack.us/i/devicez.png Червоний виглядає добре. Можливо, якщо я просто
перейду

Тоді ви намагаєтесь окреслити сам текст? Це все ще насправді неможливо, якщо ви не зробите власний власний TextView, але це, мабуть, більше роботи, ніж варто. Можливо, простіше просто зробити його темно-зеленим.
xil3

2
Один незначний запит від когось, хто є червоно-зеленим дальтоніком: будь ласка, подумайте про те, щоб додати в альтернативному поданні ту саму інформацію про червоний / зелений, оскільки бачення темно-зеленого проти темно-червоного нам часто важко. Може, стрілка вгору / вниз також?
Стів Померой

Це хороший момент, Стів. Можливо, я додаю це в майбутньому.
Фальмаррі,

0

Ось найпростіший спосіб, який я міг знайти, розширивши 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.

введіть тут опис зображення

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.