Android Textansicht Gliederungstext

82

Gibt es eine einfache Möglichkeit, dass Text einen schwarzen Umriss hat? Ich habe Textansichten mit unterschiedlichen Farben, aber einige der Farben werden auf meinem Hintergrund nicht so gut angezeigt. Ich habe mich gefragt, ob es einen einfachen Weg gibt, einen schwarzen Umriss zu erhalten, oder etwas anderes, das die Aufgabe erfüllt. Ich würde es vorziehen, keine benutzerdefinierte Ansicht erstellen und eine Leinwand und dergleichen erstellen zu müssen.

Falmarri
quelle
6
Wenn Sie diese Frage lesen und die Verwendung der Paint-Stroke-Lösung in Betracht ziehen, beachten Sie bitte, dass in Android 4.4 ein Fehler mit Strichen vorliegt . Wenn die Textgröße über 256 Pixel liegt, führt dies zu einem sehr seltsamen Strich-Rendering. Eine Problemumgehung besteht darin, den Umriss / Strich mit der in dieser Antwort vorgestellten alternativen Methode zu zeichnen . Ich wollte dies nicht bei jeder Antwort vom Typ "Schlaganfall" spammen, also habe ich es hier platziert, um die Leute darauf aufmerksam zu machen und ihnen den Kummer zu ersparen, den ich durchgemacht habe.
Tony Chan

Antworten:

54

Sie können einen Schatten hinter den Text setzen, was häufig die Lesbarkeit verbessert. Versuchen Sie, mit 50% durchscheinenden schwarzen Schatten auf Ihrem grünen Text zu experimentieren. Details dazu finden Sie hier: Android - Schatten auf Text?

Um dem Text wirklich einen Strich hinzuzufügen, müssen Sie etwas Aufwändigeres tun , wie zum Beispiel: Wie zeichnen Sie Text mit einem Rand in einer MapView in Android?

Steve Pomeroy
quelle
2
Bitte beachten Sie, dass es in Android 4.4 einen Fehler mit Strichen gibt . Wenn die Textgröße über 256 Pixel liegt, führt dies zu einem sehr seltsamen Strich-Rendering. Eine Problemumgehung besteht darin, den Umriss / Strich mit der in dieser Antwort vorgestellten alternativen Methode zu zeichnen .
Tony Chan
Bezieht sich dieser Kommentar auf die Textansicht oder die Schriftgröße?
John
Schatten ist nicht gut genug, weißer Text auf weißem Hintergrund sieht mit diesem schwarzen Schatten immer noch sehr schlecht aus
user924
81

Der Gliederungseffekt kann mithilfe von Schatten in TextView erzielt werden:

    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ł
quelle
Dies sollte die beste Lösung sein. Es war toll! Vielen Dank
Renan Bandeira
5
Dies führt nicht zu einem Umriss, da er nur auf zwei Seiten angezeigt wird.
Ban-Geoengineering
Perfekt für mich!!
Ely Dantas
Dieser Schatten ist sehr schwach für Umrisse
user924
55

Also etwas spät, aber MagicTextView erstellt unter anderem Textumrisse .

Geben Sie hier die Bildbeschreibung ein

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

Hinweis: Ich habe dies gemacht und poste mehr für zukünftige Reisende als das OP. Es ist Borderline-Spam, aber thematisch, vielleicht akzeptabel?

ABentSpoon
quelle
1
Hallo, wie können wir solche Ränder zu Text hinzufügen, der in EditText eingegeben wird?
TilalHusain
Irgendwelche Ideen zu EditText?
Piotr
dreamText.setStroke (4, Color.BLACK); dreamText.setTextColor (Color.WHITE); Ich benutze diese Einstellungen, aber meine Textfarbe ist transparent, obwohl ich schwarze Umrisse sehen kann. Was ist falsch?
Muhammad Umar
Es ist okay, aber es fügt nicht wirklich einen Rand hinzu. Es nimmt eher den Text und verwendet die Außenkante als Rand, der nicht das gleiche visuelle Ergebnis liefert.
Warpzit
1
Diese Lösung führt onDrawdazu, dass der Aufruf setTextColorinnerhalb von rekursiv aufgerufen wird onDraw.
Sermilion
23

Das Framework unterstützt Textschatten, jedoch keine Textumrisse. Aber es gibt einen Trick: Schatten ist etwas, das durchscheinend ist und verblasst. Zeichnen Sie den Schatten ein paar Mal neu und das gesamte Alpha wird zusammengefasst und das Ergebnis ist ein Umriss.

Eine sehr einfache Implementierung erweitert TextViewund überschreibt die draw(..)Methode. Jedes Mal, wenn eine Auslosung angefordert wird, führt unsere Unterklasse 5-10 Zeichnungen durch.

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
quelle
3
Vielen Dank. Ich verwende jedoch lieber diese Methode: '@Override protected void onDraw (Canvas-Zeichenfläche) {for (int i = 0; i <5; i ++) {super.onDraw (Zeichenfläche); }} '
IHeartAndroid
1
Zusätzliche Infos: Man muss mindestens den ctor mit Context und AttributeSet implementieren. Sonst stößt du auf. java.lang.NoSuchMethodException: <init> [class android.content.Context, interface android.util.AttributeSet
Bevor
22

Es ist eine ziemlich alte Frage, aber ich sehe immer noch keine vollständigen Antworten. Ich poste diese Lösung in der Hoffnung, dass jemand, der mit diesem Problem zu kämpfen hat, sie nützlich finden könnte. Die einfachste und effektivste Lösung besteht darin, die onDraw-Methode der TextView-Klasse zu überschreiben. Die meisten Implementierungen, die ich gesehen habe, verwenden die drawText-Methode, um den Strich zu zeichnen, aber dieser Ansatz berücksichtigt nicht die gesamte Formatierungsausrichtung und das Umbrechen von Text. Infolgedessen landen Strich und Text häufig an verschiedenen Stellen. Der folgende Ansatz verwendet super.onDraw, um sowohl den Strich als auch die Teile des Textes zu zeichnen, sodass Sie sich nicht um den Rest des Materials kümmern müssen. Hier sind die Schritte

  1. Erweitern Sie die TextView-Klasse
  2. OnDraw-Methode überschreiben
  3. Stellen Sie den Farbstil auf FILL
  4. Rufen Sie die übergeordnete Klasse beim Zeichnen auf, um Text im Füllmodus zu rendern.
  5. Aktuelle Textfarbe speichern.
  6. Stellen Sie die aktuelle Textfarbe auf Ihre Strichfarbe ein
  7. Stellen Sie den Malstil auf Strich
  8. Hubbreite einstellen
  9. Rufen Sie die übergeordnete Klasse onDraw erneut auf, um den Strich über den zuvor gerenderten Text zu zeichnen.

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

Das ist alles. Diese Klasse verwendet benutzerdefinierte XML-Attribute, um die Angabe der Strichfarbe und -breite aus den XML-Layoutdateien zu ermöglichen. Daher müssen Sie diese Attribute in Ihrer Datei attr.xml im Unterordner 'values' unter dem Ordner 'res' hinzufügen. Kopieren Sie Folgendes und fügen Sie es in Ihre Datei attr.xml ein.

<?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>

Sobald Sie damit fertig sind, können Sie die benutzerdefinierte StrokedTextView-Klasse in Ihren XML-Layoutdateien verwenden und auch die Strichfarbe und -breite angeben. Hier ist ein Beispiel

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

Denken Sie daran, den Paketnamen durch den Paketnamen Ihres Projekts zu ersetzen. Fügen Sie außerdem den xmlns-Namespace in die Layoutdatei ein, um benutzerdefinierte XML-Attribute zu verwenden. Sie können die folgende Zeile in den Stammknoten Ihrer Layoutdatei einfügen.

xmlns:strokeAttrs="http://schemas.android.com/apk/res-auto"
Nouman Hanif
quelle
2
Was für eine großartige, elegante Lösung! Ich habe das implementiert und es funktioniert gut. Ich habe gerade textStrokeWidth in eine Dimension (und a.getDimensionPixelSize) geändert. Vielen Dank!
dgmltn
1
Funktioniert gut, danke. Da die Gliederung den gesamten Text übernahm, änderte ich in meinem Fall die Reihenfolge: Zuerst wird die Gliederung gemalt und dann der Text.
Mdiener
2
Funktioniert super. Verwenden Sie die Android Studio-Entwurfsansicht nicht zum Testen der Konturen, da die Darstellung nicht genau genug ist. Ich habe gerade 2 Stunden damit verbracht, ein Nicht-Problem zu debuggen.
llmora
7
Diese Lösung verursacht eine unendliche Anzahl von onDraw, da setTextColor-Aufrufe ungültig machen.
Guliash
1
Eigentlich ist @Guliash richtig. Nach dem Testen verursacht diese Methode nach dem invalidate()Aufruf eine Endlosschleife, in der sie sich selbst aufruft, da der Aufruf im Innenleben von vergraben ist setTextColor. Wenn Sie nicht jede letzte Codezeile TextViewin Ihre eigene Klasse kopieren möchten, kann ich dies nur umgehen, indem ich mit Gewalt auf das private mCurTextColor Feld der TextViewVerwendung von Reflection brute . In dieser Antwort erfahren Sie ungefähr, wie es geht. Verwenden Sie einfach field.set(this, colorInt)statt zu verwenden field.get().
VerumCH
15

Ich habe gerade versucht, herauszufinden, wie das geht, und konnte online keinen guten Leitfaden finden, habe es aber schließlich herausgefunden. Wie Steve Pomeroy vorgeschlagen hat, müssen Sie etwas mehr tun. Um den umrissenen Texteffekt zu erzielen, zeichnen Sie den Text zweimal: einmal mit einem dicken Umriss und dann das zweite Mal, wenn wir den Haupttext über den Umriss zeichnen. Die Aufgabe wird jedoch erleichtert, da Sie sehr einfach eines der mit dem SDK gelieferten Codebeispiele anpassen können, nämlich das unter diesem Namen in Ihrem SDK-Verzeichnis: "/ samples / android- / ApiDemos / src / com / example / android /apis/view/LabelView.java ". Was auch auf der Android-Entwickler-Website hier zu finden ist .

Je nachdem, was Sie tun, ist es sehr leicht zu erkennen, dass Sie nur geringfügige Änderungen an diesem Code vornehmen müssen, z. B. Änderungen an TextView usw. Bevor ich dieses Beispiel entdeckte, habe ich vergessen, onMeasure () zu überschreiben (was Sie müssen zusätzlich zum Überschreiben von onDraw (), wie im Handbuch "Erstellen benutzerdefinierter Komponenten" auf der Android Developer-Website erwähnt, dies tun. Dies ist ein Teil des Grundes, warum ich Probleme hatte.

Sobald Sie das getan haben, können Sie tun, was ich getan habe:

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

Um den umrissenen Texteffekt zu erzielen, zeichnen Sie den Text zweimal: einmal mit einem dicken Umriss und dann das zweite Mal, wenn wir den Haupttext über den Umriss zeichnen.

sversch
quelle
8

Hier ist der Trick, den ich gefunden habe und der besser funktioniert als MagicTextViews Strich-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);
}
VinZen
quelle
Ich sehe irgendwie, dass die rechte Seite der Textansicht beschnitten wird - und der Umriss auf dieser Seite nicht vollständig gezeichnet ist ... als ob der Raum
ausgeht
7
Eine Sache noch. Ich vermute, dass setTextColor ein Neuzeichnen erzwingt - was dazu führt, dass eine Endlosschleife dieses onDraw immer wieder aufgerufen wird. Es wird empfohlen, beim Testen einen Logcat oder einen anderen Indikator in diese Methode einzufügen.
RoundSparrow Hilltx
@RoundSparrowhilltx ist korrekt. Wie ich in einem Kommentar zu einer anderen ähnlichen Antwort erwähnt habe, besteht der einzige Weg, um dieses Problem des Kopierens und Einfügens der Gesamtheit TextViewin Ihre eigene Klasse zu umgehen, darin, Reflection zu verwenden, um direkt auf das private mCurTextColor Feld in zuzugreifen TextView. Diese Antwort enthält eine allgemeine Richtlinie dazu. Wenn Sie Hinweis und Link - Text wollen auch einen Schlaganfall haben, müssen Sie auch ändern mHintTextColorund mLinkTextColor. Leider mTextColorbewirkt das Ändern nichts, da nur darauf verwiesen wird.
VerumCH
in wird onDraw für immer
schleifen
8

Ich habe eine Klasse geschrieben, um Text mit Gliederung auszuführen und trotzdem alle anderen Attribute und Zeichnungen einer normalen Textansicht zu unterstützen.

Grundsätzlich wird das super.onDraw(Canves canvas)auf dem verwendet, TextViewaber zweimal mit verschiedenen Stilen gezeichnet.

hoffe das hilft.

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
quelle
a.recycle () auf dem TypedArray fehlt
Leos Literak
7

Gutschrift an @YGHM Schattenunterstützung hinzufügen Geben Sie hier die Bildbeschreibung ein

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 definieren

<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-Code unten

<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" />
Arthur
quelle
5

Sie können dies programmgesteuert mit dem folgenden Snippet tun. Das liefert weiße Buchstaben mit schwarzem Hintergrund:

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

Die Parameter der Methode sind Radius, dx, dy, Farbe. Sie können sie für Ihre spezifischen Bedürfnisse ändern.

Ich hoffe, ich werde jemandem helfen, der TextView programmgesteuert erstellt und es nicht in XML hat.

Prost an die stackOverflow Community!

Landwirt
quelle
2

Ich habe eine Bibliothek basierend auf Nouman Hanifs Antwort mit einigen Ergänzungen erstellt. Beheben eines Fehlers, der bei View.invalidate () -Aufrufen eine indirekte Endlosschleife verursachte.

OTOH, die Bibliothek unterstützt auch umrissenen Text in EditText-Widgets, da dies mein eigentliches Ziel war und etwas mehr Arbeit als TextView erforderlich war.

Hier ist der Link zu meiner Bibliothek: https://github.com/biomorgoth/android-outline-textview

Vielen Dank an Nouman Hanif für die erste Idee zur Lösung!

Biomorgoth
quelle
1

MagicTextView ist sehr nützlich Stroke zu machen, aber in meinem Fall, es verursacht Fehler wie diesen diesen Fehler durch Vervielfältigung Hintergrund Attribute verursacht , der Reihe von MagicTextView

Sie müssen also attrs.xml und MagicTextView.java bearbeiten

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
quelle
1

Ich habe eine einfache Möglichkeit gefunden, die Ansicht ohne Vererbung von TextView zu skizzieren . Ich hatte eine einfache Bibliothek geschrieben, die Spannable von Android zum Umreißen von Text verwendet. Diese Lösung bietet die Möglichkeit, nur einen Teil des Textes zu skizzieren.

Ich hatte bereits auf dieselbe Frage geantwortet ( Antwort )

Klasse:

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

}

Bibliothek: OutlineSpan

Pavel Santaev
quelle
Mehrzeiliger Text wird nicht unterstützt
user924
1

Ich möchte eine Lösung hinzufügen, um das Leistungsproblem zu lösen. Zum Beispiel erledigt die Antwort von @YGHM und einigen anderen den Job, aber es verursacht einen unendlichen Aufruf von onDrawweil- setTextColorAnrufen invalidate(). Um dies zu lösen, müssen Sie außerdem invalidate()eine Variable überschreiben und hinzufügen isDrawing, die Sie festlegen true, wenn sie ausgeführt onDraw()wird und mit einem Strich gezeichnet wird. invalidate wird zurückgegeben, wenn die Variable ist true.

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

Ihr onDraw sieht folgendermaßen aus:

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
quelle
0

Sie möchten also einen Strich um die Textansicht? Leider gibt es keine einfache Möglichkeit, dies mit dem Styling zu tun. Sie müssen eine andere Ansicht erstellen und Ihre Textansicht darüber platzieren, sodass die übergeordnete Ansicht (die darüber liegende) nur einige Pixel größer ist. Dadurch sollte eine Gliederung erstellt werden.

xil3
quelle
Hmm, das klingt eher nach Schmerz als nach Wert. Ich kümmere mich nur darum, dass grüner Text auf weißem Hintergrund lesbar ist (im Moment ist er schwer zu lesen). Img88.imageshack.us/i/devicez.png Das Rot sieht gut aus. Vielleicht, wenn ich nur zu einem dunkleren Grün wechsle, aber ich wünschte wirklich, ich könnte eine Art Umriss bekommen oder so
Falmarri
Versuchen Sie dann, den Text selbst zu skizzieren? Das ist immer noch nicht wirklich möglich, es sei denn, Sie erstellen Ihre eigene benutzerdefinierte Textansicht, aber es ist wahrscheinlich mehr Arbeit als es wert ist. Es ist wahrscheinlich einfacher, es nur dunkelgrün zu machen.
xil3
2
Eine kleine Anfrage von jemandem, der rot / grün farbenblind ist: Bitte erwägen Sie, eine alternative Darstellung derselben rot / grünen Informationen hinzuzufügen, da es für uns oft schwierig ist, dunkelgrün oder dunkelrot zu sehen. Vielleicht auch ein Auf / Ab-Pfeil?
Steve Pomeroy
Das ist ein guter Punkt, Steve. Ich werde das wahrscheinlich in Zukunft hinzufügen.
Falmarri