Android textview наброски текста - PullRequest
73 голосов
/ 06 июля 2010

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

Ответы [ 15 ]

53 голосов
/ 06 февраля 2015

Эффект контура может быть достигнут с помощью тени в 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"
52 голосов
/ 07 июля 2010

Вы можете поместить тень за текстом, что часто может помочь читаемости. Попробуйте поэкспериментировать с 50% полупрозрачными черными тенями на зеленом тексте. Подробности о том, как это сделать, приведены здесь: Android - тень на тексте?

Чтобы действительно добавить обводку вокруг текста, вам нужно сделать что-то более сложное, например: Как рисовать текст с рамкой на MapView в Android?

50 голосов
/ 24 апреля 2012

Итак, немного поздно, но MagicTextView , помимо прочего, будет делать контуры текста.

enter image description here

<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" />

Примечание: я сделал это, иЯ публикую больше ради будущих путешественников, чем ОП.Это пограничный спам, но быть по теме, возможно, приемлемым?

21 голосов
/ 09 августа 2011

Каркас поддерживает тень текста, но не поддерживает структуру текста.Но есть хитрость: тень - это нечто полупрозрачное и исчезающее.Перерисуйте теневую пару раз, и вся альфа будет суммирована, и в результате получится контур.

Очень простая реализация расширяет 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" />
18 голосов
/ 11 августа 2015

Это довольно старый вопрос, но все же я не вижу полных ответов. Поэтому я публикую это решение, надеясь, что кто-то, кто борется с этой проблемой, может найти его полезным. Самое простое и эффективное решение - переопределить метод onDraw класса TextView. Большинство реализаций, которые я видел, используют метод drawText для рисования обводки, но этот подход не учитывает все выравнивание форматирования и обтекание текста, которое происходит. В результате часто обводка и текст оказываются в разных местах. Следующий подход использует super.onDraw для рисования как обводки, так и заливки частей текста, чтобы вам не пришлось беспокоиться об остальной части материала. Вот шаги

  1. Расширение класса TextView
  2. Переопределить метод onDraw
  3. Установить стиль рисования FILL
  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 в подпапку '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"
15 голосов
/ 05 октября 2010

Я просто пытался понять, как это сделать, и не смог найти хорошего гида в Интернете, но в конце концов понял.Как предположил Стив Померой, вы должны сделать что-то более сложное.Чтобы получить эффект выделенного текста, вы рисуете текст дважды: один раз жирным контуром, а второй раз мы рисуем основной текст поверх контура.Но задача стала проще, потому что вы можете очень легко адаптировать один из примеров кода, поставляемых с 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 голосов
/ 28 февраля 2016

Я написал класс для выполнения текста с контуром и все еще поддерживаю все остальные атрибуты и отрисовку обычного текстового представления.

он в основном использует 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>
7 голосов
/ 28 мая 2014

Вот трюк, который я нашел, который работает лучше, чем мазок MagicTextView.

3 голосов
/ 24 мая 2018

Вы можете сделать это программно с помощью приведенного ниже фрагмента. Это обеспечивает белые буквы с черным фоном:

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

Параметры метода: радиус, дх, ду, цвет. Вы можете изменить их для своих конкретных потребностей.

Надеюсь, я помогу тому, кто создает TextView программно и не имеет его внутри xml.

Приветствую вас в сообществе stackOverflow!

2 голосов
/ 14 марта 2019

кредит @YGHM добавить поддержку теней enter image description here

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 define

<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" />
...