Texto de esquema de vista de texto de Android

82

¿Existe una manera sencilla de que el texto pueda tener un contorno negro? Tengo vistas de texto que serán de diferentes colores, pero algunos de los colores no se ven tan bien en mi fondo, así que me preguntaba si hay una manera fácil de obtener un contorno negro o algo más que funcione. Preferiría no tener que crear una vista personalizada y hacer un lienzo y demás.

Falmarri
fuente
6
Para cualquiera que lea esta pregunta y esté considerando usar la solución Paint-Stroke, tenga en cuenta que hay un error con los trazos en Android 4.4 . Si el tamaño del texto es superior a 256 píxeles, el resultado es una representación de trazos muy extraños. Una solución alternativa es dibujar el contorno / trazo con el método alternativo presentado en esta respuesta . No quería enviar esto por correo no deseado en cada respuesta de tipo Stroke, así que póngalo aquí para concienciar a la gente y evitarles el dolor por el que pasé.
Tony Chan
1
Posible duplicado de Agregar "sombra" opaca (contorno) a Android TextView
juergen d

Respuestas:

54

Puede poner una sombra detrás del texto, lo que a menudo puede mejorar la legibilidad. Intente experimentar con sombras negras translúcidas al 50% en su texto verde. Los detalles sobre cómo hacer esto están aquí: Android - ¿sombra en el texto?

Para agregar realmente un trazo alrededor del texto, debe hacer algo un poco más complicado, como esto: ¿Cómo se dibuja texto con un borde en un MapView en Android?

Steve Pomeroy
fuente
2
Tenga en cuenta que hay un error con los golpes en Android 4.4 . Si el tamaño del texto es superior a 256 píxeles, el resultado es una representación de trazos muy extraños. Una solución alternativa es dibujar el contorno / trazo con el método alternativo presentado en esta respuesta .
Tony Chan
¿Este comentario se refiere a la vista de texto o al tamaño de fuente?
Juan
la sombra no es lo suficientemente buena, el texto blanco en el diseño de fondo blanco todavía se ve muy mal con esa sombra negra
user924
81

El efecto de contorno se puede lograr usando la sombra en 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"
Rafał
fuente
esta debería ser la mejor solución. ¡Fue grandioso! Gracias
Renan Bandeira
5
Esto no da como resultado un contorno, ya que solo se muestra en dos lados.
ban-geoengineering
¡¡Perfecto para mi!!
Ely Dantas
esta sombra es muy débil para el contorno
user924
55

Entonces, un poco tarde, pero MagicTextView hará esquemas de texto, entre otras cosas.

ingrese la descripción de la imagen aquí

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

Nota: Hice esto y estoy publicando más por el bien de los futuros viajeros que por el OP. Está en el límite del spam, pero si es sobre el tema, ¿quizás sea aceptable?

ABentSpoon
fuente
1
Hola, ¿cómo podemos agregar bordes como estos en el texto que se ingresa en EditText?
TilalHusain
¿Alguna idea sobre EditText?
Piotr
dreamText.setStroke (4, Color.BLACK); dreamText.setTextColor (Color.WHITE); ESTOY UTILIZANDO estos ajustes, pero el color de mi texto es transparente, pero puedo ver un contorno negro. ¿Qué está mal?
Muhammad Umar
está bien, pero en realidad no agrega un borde. Más bien toma el texto y usa el borde exterior como borde, lo que no da el mismo resultado visual.
Warpzit
1
Esta solución hace onDrawque se llame de forma recursiva debido a la llamada setTextColordentro de onDraw.
Sermilion
23

El marco admite text-shadow pero no admite text-outline. Pero hay un truco: la sombra es algo que es traslúcido y se desvanece. Vuelve a dibujar la sombra un par de veces y todo el alfa se resume y el resultado es un contorno.

Una implementación muy simple extiende TextViewy anula el draw(..)método. Cada vez que se solicita un sorteo, nuestra subclase realiza entre 5 y 10 dibujos.

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" />
Zsolt Safrany
fuente
3
Muchas gracias. Sin embargo, prefiero usar este método: '@Override protected void onDraw (Canvas canvas) {for (int i = 0; i <5; i ++) {super.onDraw (canvas); }} '
IHeartAndroid
1
Información adicional: hay que implementar al menos el ctor con Context y AttributeSet. De lo contrario, te encontrarás con. java.lang.NoSuchMethodException: <init> [class android.content.Context, interface android.util.AttributeSet
Bevor
22

Es una pregunta bastante antigua, pero todavía no veo ninguna respuesta completa. Así que estoy publicando esta solución, con la esperanza de que alguien que esté luchando con este problema la encuentre útil. La solución más simple y efectiva es anular el método onDraw de la clase TextView. La mayoría de las implementaciones que he visto usan el método drawText para dibujar el trazo, pero ese enfoque no tiene en cuenta toda la alineación del formato y el ajuste del texto que entra. Y como resultado, a menudo el trazo y el texto terminan en lugares diferentes. El siguiente enfoque usa super.onDraw para dibujar el trazo y rellenar partes del texto para que no tenga que preocuparse por el resto del material. Aquí están los pasos

  1. Extender la clase TextView
  2. Anular el método onDraw
  3. Establecer el estilo de pintura en RELLENO
  4. llame a la clase principal en Draw para representar texto en modo de relleno.
  5. guardar el color del texto actual.
  6. Establecer el color del texto actual a su color de trazo
  7. Establecer el estilo de pintura en Trazo
  8. Establecer ancho de trazo
  9. Y vuelva a llamar a la clase principal onDraw para dibujar el trazo sobre el texto renderizado anteriormente.

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

Eso es todo. Esta clase utiliza atributos XML personalizados para permitir especificar el color y el ancho del trazo de los archivos de diseño XML. Por lo tanto, debe agregar estos atributos en su archivo attr.xml en la subcarpeta 'valores' en la carpeta 'res'. Copie y pegue lo siguiente en su archivo 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>

Una vez que haya terminado con eso, puede usar la clase StrokedTextView personalizada en sus archivos de diseño XML y especificar el color y el ancho del trazo también. Aquí hay un ejemplo

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

Recuerde reemplazar el nombre del paquete con el nombre del paquete de su proyecto. También agregue el espacio de nombres xmlns en el archivo de diseño para usar atributos XML personalizados. Puede agregar la siguiente línea en el nodo raíz de su archivo de diseño.

xmlns:strokeAttrs="http://schemas.android.com/apk/res-auto"
Nouman Hanif
fuente
2
¡Qué gran y elegante solución! Implementé esto y funciona bien. Acabo de cambiar textStrokeWidth a una dimensión (y a.getDimensionPixelSize). ¡Gracias!
dgmltn
1
Funciona bien, gracias. Como el esquema se apoderó de todo el texto, cambié el orden en mi caso: primero se pinta el esquema y luego el texto.
mdiener
2
Funciona genial. No use la vista de diseño de Android Studio para probar los contornos, la representación no es lo suficientemente precisa. Acabo de pasar 2 horas depurando un problema.
llmora
7
Esta solución causará un número infinito de onDraw, porque las llamadas setTextColor invalidan.
Guliash
1
De hecho, @Guliash tiene razón. Después de la prueba, una vez que se llama a este método, provoca un bucle infinito de llamarse a sí mismo debido a la invalidate()llamada enterrada en el funcionamiento interno de setTextColor. A menos que desee copiar hasta la última línea de código de TextViewsu propia clase, la única forma de evitar esto que puedo ver es acceder a la fuerza bruta al campo privado mCurTextColor del TextViewuso de Reflection. Vea esta respuesta para ver aproximadamente cómo hacerlo. Solo use en field.set(this, colorInt)lugar de usar field.get().
VerumCH
15

He estado tratando de averiguar cómo hacer esto y no pude encontrar una buena guía en línea, pero finalmente lo descubrí. Como sugirió Steve Pomeroy, tienes que hacer algo más complicado. Para obtener el efecto de texto delineado, dibuja el texto dos veces: una vez con un contorno grueso y luego la segunda vez dibujamos el texto principal sobre el contorno. Pero, la tarea se hace más fácil porque puede adaptar muy fácilmente una de las muestras de código proporcionadas con el SDK, a saber, la que tiene este nombre en su directorio SDK: "/ samples / android- / ApiDemos / src / com / example / android /apis/view/LabelView.java ". Que también se puede encontrar en el sitio web para desarrolladores de Android aquí .

Dependiendo de lo que esté haciendo, es muy fácil ver que solo necesitará hacer modificaciones menores a ese código, como cambiarlo para extenderlo desde TextView, etc. Antes de descubrir esta muestra, olvidé anular onMeasure () (que debe hacer además de anular onDraw () como se menciona en la guía "Creación de componentes personalizados" en el sitio web para desarrolladores de Android), que es parte de la razón por la que estaba teniendo problemas.

Una vez que hayas hecho eso, puedes hacer lo que hice yo:

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

Entonces, para obtener el efecto de texto delineado, dibuja el texto dos veces: una vez con un contorno grueso y luego la segunda vez dibujamos el texto principal sobre el contorno.

sversch
fuente
8

Aquí está el truco que encontré que funciona mejor que el trazo de MagicTextView en mi opinión

@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);
}
VinZen
fuente
Veo que el lado derecho de TextView se está recortando, y el contorno no se dibuja completamente en ese lado ... como si se
quedara sin
7
Una cosa más. Sospecho que setTextColor está forzando un redibujo, lo que provoca un bucle sin fin de este onDraw que se llama una y otra vez. Se recomienda colocar un logcat u otro indicador en este método durante la prueba.
RoundSparrow hilltx
@RoundSparrowhilltx es correcto. Como mencioné en un comentario a otra respuesta similar, sospecho que la única forma de evitar esto, excepto copiar y pegar la totalidad de TextViewen su propia clase es usar Reflection para acceder directamente al campo privado mCurTextColor en TextView. Esta respuesta proporciona una guía general sobre cómo hacer esto. Si desea que la sugerencia y el texto del enlace también tengan un trazo, también tendrá que cambiar mHintTextColory mLinkTextColor. Desafortunadamente, cambiar mTextColorno hace nada, ya que solo se hace referencia a él.
VerumCH
en bucle onDraw para siempre
user924
8

Escribí una clase para realizar texto con contorno y aún admitir todos los demás atributos y dibujos de una vista de texto normal.

Básicamente usa el super.onDraw(Canves canvas)on the TextViewpero dibuja dos veces con diferentes estilos.

espero que esto ayude.

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>
YGHM
fuente
Falta a.recycle () en TypedArray
Leos Literak
8

crédito a @YGHM agregar soporte en la sombra ingrese la descripción de la imagen aquí

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 definir

<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 código a continuación

<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" />
Arturo
fuente
funciona perfecto. thx
Sanket Patel
5

Puede hacer esto mediante programación con el siguiente fragmento. Eso proporciona letras blancas con fondo negro:

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

Los parámetros del método son radio, dx, dy, color. Puede cambiarlos según sus necesidades específicas.

Espero ayudar a alguien que crea TextView mediante programación y no lo tenga dentro de xml.

¡Salud a la comunidad de stackOverflow!

Farmaker
fuente
2

He creado una biblioteca basada en la respuesta de Nouman Hanif con algunas adiciones. Por ejemplo, corregir un error que provocó un bucle infinito indirecto en las llamadas a View.invalidate ().

OTOH, la biblioteca también admite texto delineado en los widgets de EditText, ya que era mi objetivo real y necesitaba un poco más de trabajo que TextView.

Aquí está el enlace a mi biblioteca: https://github.com/biomorgoth/android-outline-textview

¡Gracias a Nouman Hanif por la idea inicial de la solución!

biomorgoth
fuente
2

Quiero agregar una solución para resolver el problema de rendimiento. Por ejemplo, la respuesta de @YGHM y algunos otros hace el trabajo, pero causa infinita de llamada onDraw, porque setTextColorlas llamadas invalidate(). Entonces, para resolverlo, también debe anular invalidate()y agregar una variable isDrawingque establecerá true, cuando onDraw()esté en progreso y dibujando con un trazo. invalidar devolverá si la variable es true.

override fun invalidate() {
    if (isDrawing) return
    super.invalidate()
  }

Tu onDraw se verá así:

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)
    }
  }
Sermilion
fuente
1

MagicTextView es muy útil para hacer una fuente de trazo, pero en mi caso, causa un error como este, este error causado por la duplicación de atributos de fondo que establece MagicTextView

por lo que necesita editar attrs.xml y 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));
}
}
Matsumoto Kazuya
fuente
1

Encontré una forma sencilla de esquematizar la vista sin la herencia de TextView . Escribí una biblioteca simple que usa Spannable de Android para delinear texto. Esta solución brinda la posibilidad de delinear solo una parte del texto.

Ya había respondido a la misma pregunta ( respuesta )

Clase:

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

}

Biblioteca: OutlineSpan

Pavel Santaev
fuente
no admite texto de varias líneas
user924
0

¿Quieres un trazo alrededor de la vista de texto? Desafortunadamente, no hay una forma sencilla de hacerlo con el estilo. Tendrá que crear otra vista y colocar su vista de texto en la parte superior, haciendo que la vista principal (la que está encima) sea solo unos pocos píxeles más grande; esto debería crear un contorno.

xil3
fuente
Hmm, eso suena más doloroso de lo que vale la pena. Todo lo que me importa es que el texto verde se pueda leer sobre un fondo blanco (ahora mismo es un poco difícil de leer) img88.imageshack.us/i/devicez.png El rojo se ve bien. Tal vez si cambio a un verde más oscuro, pero realmente desearía poder obtener algún tipo de contorno o algo así
Falmarri
¿Estás tratando de delinear el texto en sí? Eso todavía no es realmente posible, a menos que haga su propio TextView personalizado, pero probablemente sea más trabajo de lo que vale. Probablemente sea más fácil hacerlo de color verde oscuro.
xil3
2
Una solicitud menor de alguien que es daltónico rojo / verde: considere agregar una representación alternativa de la misma información rojo / verde, ya que ver el verde oscuro frente al rojo oscuro a menudo es difícil para nosotros. ¿Quizás también una flecha hacia arriba / hacia abajo?
Steve Pomeroy
Ese es un buen punto, Steve. Probablemente agregaré eso en el futuro.
Falmarri