Android textview наброски текста


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

13 64

13 ответов:

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

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

Итак, немного поздно, но 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" />

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

эффект контура может быть достигнут с помощью тени в 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"

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

очень простая реализация расширяет 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" />

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

  1. расширить класс TextView
  2. переопределить метод onDraw
  3. установить стиль краски для заполнения
  4. вызов родительского класса при рисовании для отображения текста в заливке режим.
  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-файл в подпапке "значения" в папке "res". Скопируйте и вставьте следующее ваше внимание.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"

Я просто пытался выяснить, как это сделать, и не смог найти хорошего гида в интернете, но в конечном итоге понял это. Как предположил Стив Померой, вы должны сделать что-то более активное. Чтобы получить эффект выделенного текста, вы рисуете текст дважды: один раз с толстым контуром, а затем во второй раз мы рисуем основной текст по контуру. Но, задача упрощается, потому что вы можете очень легко адаптировать один из примеров кода, поставляемых с SDK, а именно тот, который находится под этим имя в каталоге SDK: "/ samples/android-/ApiDemos/src/com/example/android/apis/view / LabelView.ява." Которые также можно найти на веб-сайте разработчиков 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);
   }

Итак, чтобы получить эффект выделенного текста, вы рисуете текст дважды: один раз с толстым контуром, а затем во второй раз мы рисуем основной текст по контуру.

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

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

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

он в основном использует 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>

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

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

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

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

Ура сообществу stackOverflow!

Итак, вы хотите обвести вокруг textview? К сожалению, нет простого способа сделать это с помощью укладки. Вам нужно будет создать другой вид и поместить свой textview поверх, сделав Родительский вид (тот, на котором он находится) всего на несколько пикселей больше - это должно создать контур.

MagicTextView очень полезно сделать шрифт Штриха, но в моем случае это вызывает ошибку, например этой эта ошибка вызвана дублированием фоновых атрибутов, которые устанавливаются MagicTextView

поэтому вам нужно отредактировать attrs.xml и MagicTextView.java

attrs.xml

<attr name="background" format="reference|color" />
 ↓
<attr name="mBackground" format="reference|color" />

MagicTextView.Ява 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));
}
}

Я создал библиотеку на основе ответ Ноумана Ханифа С некоторыми дополнениями. Например, исправление ошибки, которая вызвала непрямой бесконечный цикл на вид.признать недействительными() звонки.

OTOH, библиотека также поддерживает выделенный текст в виджетах EditText, так как это была моя настоящая цель, и ей нужно было немного больше работы, чем TextView.

вот ссылка на мою библиотеку: https://github.com/biomorgoth/android-outline-textview

спасибо Ноуман Ханиф за первоначальную идею по решению!

Я нашел простой способ схема без наследования от 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