Android metin görünümü anahat metni


83

Metnin siyah bir çerçeveye sahip olmasının basit bir yolu var mı? Farklı renkler olacak metin görünümlerim var, ancak bazı renkler arka planımda o kadar iyi görünmüyor, bu yüzden siyah bir taslak elde etmenin kolay bir yolu veya işi yapacak başka bir şey olup olmadığını merak ediyordum. Özel bir görünüm oluşturmak ve bir tuval yapmak zorunda kalmamayı tercih ederim.


7
Bu soruyu okuyan ve Paint-Stroke çözümünü kullanmayı düşünen herkes için, lütfen Android 4.4'te vuruşlarla ilgili bir hata olduğunu unutmayın . Metin boyutu 256 pikselin üzerindeyse çok garip kontur oluşturma ile sonuçlanır. Geçici bir çözüm, bu cevapta sunulan alternatif yöntemle anahat / kontur çizmektir . Bunu her Stroke türü yanıtta spam olarak göndermek istemedim, bu yüzden insanları yaşadığım kederden haberdar etmek ve onları kurtarmak için buraya koymak.
Tony Chan

Yanıtlar:


56

Metnin arkasına, genellikle okunabilirliğe yardımcı olabilecek bir gölge koyabilirsiniz. Yeşil metniniz üzerinde% 50 yarı saydam siyah gölgeler kullanmayı deneyin. Bunun nasıl yapılacağına dair ayrıntılar burada: Android - metin üzerinde gölge?

Metnin etrafına gerçekten bir çizgi eklemek için, biraz daha kapsamlı bir şey yapmanız gerekir, örneğin: Android'de bir MapView'da bir kenarlıkla metin nasıl çizilir?


3
Lütfen Android 4.4'te vuruşlarla ilgili bir hata olduğunu unutmayın . Metin boyutu 256 pikselin üzerindeyse çok garip kontur oluşturma ile sonuçlanır. Geçici bir çözüm, bu cevapta sunulan alternatif yöntemle anahat / kontur çizmektir .
Tony Chan

Bu yorum metin görünümüne mi yoksa yazı tipi boyutuna mı atıfta bulunuyor?
John

gölge yeterince iyi değil, beyaz arka plan düzenindeki beyaz metin o siyah gölgeyle hala gerçekten kötü görünüyor
user924

81

TextView'da gölge kullanılarak anahat efekti elde edilebilir:

    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"

bu en iyi çözüm olmalı. Harikaydı! Teşekkürler
Renan Bandeira

5
Bu, yalnızca iki tarafta gösterildiği için bir taslak oluşturmaz.
ban-jeomühendislik

Bana göre mükemmel!!
Ely Dantas

bu gölge anahat için çok zayıf
user924

55

Bu yüzden, biraz geç, ancak MagicTextView diğer şeylerin yanı sıra metin ana hatları yapacak.

görüntü açıklamasını buraya girin

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

Not: Bunu ben yaptım ve OP'den daha çok gelecek yolcular için gönderiyorum. Sınırda spam, ancak konu ile ilgili, belki de kabul edilebilir mi?


1
Merhaba, EditText'e girilen metne bunun gibi kenarlıkları nasıl ekleyebiliriz?
TilalHusain

EditText hakkında herhangi bir fikriniz var mı?
Piotr

dreamText.setStroke (4, Color.BLACK); dreamText.setTextColor (Color.WHITE); i Bu ayarları kullanıyorum ancak metin rengim şeffaf, ancak siyah anahatları görebiliyorum. Yanlış olan ne?
Muhammad Umar

sorun değil, ama gerçekten bir sınır eklemiyor. Daha ziyade metni alır ve aynı görsel sonucu vermeyen kenarlık olarak dış kenarı kullanır.
Warpzit

1
Bu çözüm neden onDrawçünkü çağrı bir özyinelemeli şekilde çağrılacak setTextColoronDraw.
Sermilion

23

Oldukça eski bir soru ama yine de tam bir cevap göremiyorum. Bu yüzden, bu sorunla mücadele eden birinin yararlı bulabileceğini umarak bu çözümü gönderiyorum. En basit ve en etkili çözüm, TextView sınıfının onDraw yöntemini geçersiz kılmaktır. Gördüğüm uygulamaların çoğu, konturu çizmek için drawText yöntemini kullanıyor, ancak bu yaklaşım, içeri giren tüm biçimlendirme hizalamasını ve metin kaydırmayı hesaba katmıyor. Ve sonuç olarak, genellikle kontur ve metin farklı yerlerde sona eriyor. Aşağıdaki yaklaşım, metnin hem konturunu hem de dolgu kısımlarını çizmek için super.onDraw'ı kullanır, böylece diğer şeylerle uğraşmanıza gerek kalmaz. İşte adımlar

  1. TextView sınıfını genişletin
  2. OnDraw yöntemini geçersiz kıl
  3. Boya stilini DOLGU olarak ayarla
  4. Doldurma modunda metin oluşturmak için Draw'da ebeveyn sınıfı çağırın.
  5. mevcut metin rengini kaydedin.
  6. Mevcut metin rengini kontur renginize ayarlayın
  7. Boya stilini Kontur olarak ayarla
  8. Kontur genişliğini ayarla
  9. Konturu önceden oluşturulmuş metnin üzerine çizmek için onDraw üst sınıfını yeniden çağırın.

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

Hepsi bu. Bu sınıf, XML mizanpaj dosyalarından kontur rengini ve genişliğini belirtmeyi etkinleştirmek için özel XML nitelikleri kullanır. Bu nedenle, bu öznitelikleri attr.xml dosyanıza 'res' klasörü altındaki 'değerler' alt klasörüne eklemeniz gerekir. Aşağıdakileri kopyalayıp attr.xml dosyanıza yapıştırın.

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

Bunu yaptıktan sonra, XML mizanpaj dosyalarınızda özel StrokedTextView sınıfını kullanabilir ve kontur rengini ve genişliğini de belirtebilirsiniz. İşte bir örnek

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

Paket adını, projenizin paket adıyla değiştirmeyi unutmayın. Ayrıca, özel XML niteliklerini kullanmak için xmlns ad alanını düzen dosyasına ekleyin. Düzen dosyanızın kök düğümüne aşağıdaki satırı ekleyebilirsiniz.

xmlns:strokeAttrs="http://schemas.android.com/apk/res-auto"

2
Ne harika, zarif bir çözüm! Bunu uyguladım ve iyi çalışıyor. TextStrokeWidth'i bir boyuta (ve a.getDimensionPixelSize) değiştirdim. Teşekkürler!
dgmltn

1
İyi çalışıyor, teşekkürler. Taslak metnin tamamını devraldıkça, benim durumumda sırayı değiştirdim: önce taslak boyanır, sonra metin.
mdiener

2
Harika çalışıyor. Ana hatları test etmek için Android Studio tasarım görünümünü kullanmayın, temsil yeterince doğru değildir. Sadece 2 saat boyunca olmayan bir sorunu ayıklamak için harcadım.
llmora

7
SetTextColor çağrıları geçersiz kıldığından, bu çözüm sonsuz sayıda onDraw'a neden olur.
Guliash

2
Aslında @ Guliash doğru. Testten sonra, bu yöntem bir kez çağrıldığında invalidate(), iç işleyişine gömülü çağrı nedeniyle sonsuz bir çağrı döngüsüne neden olur setTextColor. Her son kod satırını TextViewkendi sınıfınıza kopyalamak istemiyorsanız , bununla ilgili görebildiğim tek yol, Reflection kullanmanın özel mCurTextColor alanına kaba kuvvetle erişmek TextView. Bkz Bu cevap nasıl yapılacağını kabaca görmek için. field.set(this, colorInt)Kullanmak yerine kullanın field.get().
VerumCH

23

Çerçeve metin gölgesini destekler ancak metin anahatlarını desteklemez. Ancak bir numara var: gölge, yarı saydam ve solan bir şeydir. Gölgeyi birkaç kez yeniden çizin ve tüm alfa özetlenir ve sonuç bir taslaktır.

Çok basit bir uygulama yöntemi genişletir TextViewve geçersiz kılar draw(..). Her çekilişte alt sınıfımız 5-10 çizim yapar.

public class OutlineTextView extends TextView {

    // Constructors

    @Override
    public void draw(Canvas canvas) {
        for (int i = 0; i < 5; i++) {
            super.draw(canvas);
        }
    }

}


<OutlineTextView
    android:shadowColor="#000"
    android:shadowRadius="3.0" />

3
Çok teşekkür ederim. Ancak şu yöntemi kullanmayı tercih ederim: '@Override korumalı void onDraw (Canvas canvas) {for (int i = 0; i <5; i ++) {super.onDraw (canvas); }} '
IHeartAndroid

1
Ek bilgi: En azından ctor'u Context ve AttributeSet ile uygulamak gerekir. Aksi takdirde karşılaşırsınız. java.lang.NoSuchMethodException: <init> [class android.content.Context, interface android.util.AttributeSet
Bevor

15

Sadece bunu nasıl yapacağımı anlamaya çalışıyordum ve çevrimiçi olarak iyi bir rehber bulamadım ama sonunda anladım. Steve Pomeroy'un önerdiği gibi, daha kapsamlı bir şey yapmanız gerekiyor. Ana hatları çizilen metin efektini elde etmek için, metni iki kez çizersiniz: biri kalın bir taslakla ve sonra ikinci kez ana metni ana hattın üzerine çizeriz. Ancak, SDK ile sağlanan kod örneklerinden birini, yani SDK dizininizdeki bu ad altında olanı çok kolay bir şekilde uyarlayabileceğiniz için görev daha kolay hale getirilmiştir: "/ samples / android- / ApiDemos / src / com / example / android /apis/view/LabelView.java ". Burada ayrıca Android geliştirici web sitesinde de bulunabilir .

Yaptığınız şeye bağlı olarak, o kodda yalnızca küçük değişiklikler yapmanız gerekeceğini görmek çok kolay, örneğin onu TextView'dan genişletmek için değiştirmek gibi. Bu örneği keşfetmeden önce onMeasure () 'ı geçersiz kılmayı unuttum (ki Android Developer web sitesindeki "Özel Bileşenler Oluşturma" kılavuzunda belirtildiği gibi onDraw () öğesini geçersiz kılmaya ek olarak yapmanız gerekir, bu da neden sorun yaşadığımın bir parçasıdır.

Bunu yaptıktan sonra, benim yaptığımı yapabilirsiniz:

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

Bu nedenle, anahatlı metin efektini elde etmek için metni iki kez çizersiniz: bir kez kalın bir taslakla ve sonra ikinci kez ana metni ana hattın üzerine çizeriz.


9

@YGHM'ye kredi ekleme gölge desteği görüntü açıklamasını buraya girin

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 tanımla

<declare-styleable name="TextViewOutline">
    <attr name="outlineSize" format="dimension"/>
    <attr name="outlineColor" format="color|reference"/>
    <attr name="android:shadowRadius"/>
    <attr name="android:shadowDx"/>
    <attr name="android:shadowDy"/>
    <attr name="android:shadowColor"/>
</declare-styleable>

Aşağıdaki xml kodu

<com.megvii.demo.TextViewOutline
    android:id="@+id/product_name"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:layout_marginTop="110dp"
    android:background="#f4b222"
    android:fontFamily="@font/kidsmagazine"
    android:padding="10dp"
    android:shadowColor="#d7713200"
    android:shadowDx="0"
    android:shadowDy="8"
    android:shadowRadius="1"
    android:text="LIPSTICK SET"
    android:textColor="@android:color/white"
    android:textSize="30sp"
    app:outlineColor="#cb7800"
    app:outlineSize="3dp" />

8

İşte MagicTextView'ün inme IMO'sundan daha iyi çalışan bulduğum numara

@Override
protected void onDraw(Canvas pCanvas) {
    int textColor = getTextColors().getDefaultColor();
    setTextColor(mOutlineColor); // your stroke's color
    getPaint().setStrokeWidth(10);
    getPaint().setStyle(Paint.Style.STROKE);
    super.onDraw(pCanvas);
    setTextColor(textColor);
    getPaint().setStrokeWidth(0);
    getPaint().setStyle(Paint.Style.FILL);
    super.onDraw(pCanvas);
}

TextView'in sağ tarafının kırpıldığını görüyorum - ve ana hat o tarafa tam olarak
çizilmiyor

7
Bir şey daha. SetTextColor'un yeniden çizim yapmaya zorladığından şüpheleniyorum - bu da bu onDraw'un sonsuz döngüsünün tekrar tekrar çağrılmasına neden oluyor. Test sırasında bu yönteme bir logcat veya başka bir gösterge koymak tavsiye edilir.
RoundSparrow hilltx

@RoundSparrowhilltx doğru. Benzer başka bir cevaba yapılan bir yorumda da belirttiğim gibi, bu eksikliğin tamamını TextViewkendi sınıfınıza kopyalayıp yapıştırmanın tek yolunun , içindeki özel mCurTextColor alana doğrudan erişmek için Yansıtma'yı kullanmak olduğundan şüpheleniyorum TextView. Bu cevap , bunun nasıl yapılacağına dair genel bir kılavuz sağlar. İpucu ve bağlantı metninin de bir kontur içermesini istiyorsanız, ayrıca mHintTextColorve mLinkTextColor. Ne yazık ki mTextColor, yalnızca referans verildiği için değişiklik hiçbir şey yapmaz.
VerumCH

in will loop
onDraw

8

Metni ana hatlarıyla gerçekleştirmek için bir sınıf yazdım ve normal bir metin görünümünün diğer tüm özelliklerini ve çizimini hala destekledim.

temelde super.onDraw(Canves canvas)on 'u kullanır, TextViewancak iki kez farklı stillerle çizer.

Bu yardımcı olur umarım.

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>

TypedArray'de a.recycle () eksik
Leos Literak

5

Bunu aşağıdaki kod parçacığı ile programlı olarak yapabilirsiniz. Bu, siyah arkaplanlı beyaz harfler sağlar:

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

Yöntemin parametreleri yarıçap, dx, dy, renktir. Bunları özel ihtiyaçlarınız için değiştirebilirsiniz.

Umarım TextView programını programlı olarak oluşturan ve xml içinde bulundurmayan birine yardımcı olurum.

StackOverflow topluluğuna teşekkürler!


2

Nouman Hanif'in cevabına göre bazı eklemelerle bir kütüphane oluşturdum . Örneğin, View.invalidate () çağrılarında dolaylı sonsuz döngüye neden olan bir hatayı düzeltmek.

OTOH, kütüphane aynı zamanda EditText widget'larında özetlenen metni de destekliyor, çünkü bu benim gerçek hedefimdi ve TextView'den biraz daha fazla çalışmaya ihtiyaç duyuyordu.

Kitaplığımın bağlantısı: https://github.com/biomorgoth/android-outline-textview

Çözümle ilgili ilk fikir için Nouman Hanif'e teşekkürler!


2

Performans sorununu çözmek için bir çözüm eklemek istiyorum. Örneğin, @YGHM ve diğer birkaç kişinin cevabı işi yapar, ancak sonsuz çağrıya neden olur onDrawçünkü setTextColorçağrılar invalidate(). Bu yüzden, bunu çözmek için, aynı zamanda devam ederken ve vuruşla çizim yaparken ayarlayacağınız invalidate()bir değişkeni geçersiz kılmanız ve eklemeniz gerekir . değişken ise geçersiz kılma döndürür .isDrawingtrueonDraw()true

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

OnDraw'ınız şöyle görünecek:

override fun onDraw(canvas: Canvas) {
    if (strokeWidth > 0) {
      isDrawing = true
      val textColor = textColors.defaultColor
      setTextColor(strokeColor)
      paint.strokeWidth = strokeWidth
      paint.style = Paint.Style.STROKE
      super.onDraw(canvas)
      setTextColor(textColor)
      paint.strokeWidth = 0f
      paint.style = Paint.Style.FILL
      isDrawing = false
      super.onDraw(canvas)
    } else {
      super.onDraw(canvas)
    }
  }

1

MagicTextView inme yazı yapmak çok yararlıdır, ancak benim durumumda, gibi hataya neden bu MagicTextView tarafından hangi seti çoğaltılması arka plan öznitelikleri neden bu hatanın

bu yüzden attrs.xml ve MagicTextView.java'yı düzenlemeniz gerekir

attrs.xml

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

MagicTextView.java 88:95

if (a.hasValue(R.styleable.MagicTextView_mBackground)) {
Drawable background = a.getDrawable(R.styleable.MagicTextView_mBackground);
if (background != null) {
    this.setBackgroundDrawable(background);
} else {
    this.setBackgroundColor(a.getColor(R.styleable.MagicTextView_mBackground, 0xff000000));
}
}

1

TextView'dan devralma olmadan görünümü özetlemenin basit bir yolunu buldum . Metnin ana hatlarını çizmek için Android'in Spannable'ını kullanan basit bir kitaplık yazmıştım . Bu çözüm, metnin yalnızca bir kısmının ana hatlarını çizme olanağı sağlar.

Aynı soruya zaten cevap vermiştim ( cevap )

Sınıf:

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

}

Kitaplık: OutlineSpan


1
çok satırlı metni desteklemez
user924

0

Yani metin görünümünün etrafında bir vuruş mu istiyorsunuz? Ne yazık ki bunu stil ile yapmanın basit bir yolu yok. Başka bir görünüm oluşturmanız ve metin görünümünüzü üst tarafa yerleştirmeniz, üst görünümü (üstündeki) yalnızca birkaç piksel büyütmeniz gerekir - bu bir taslak oluşturmalıdır.


Hmm, kulağa değerinden daha çok acı veriyor. Tek umursadığım, beyaz bir arka plan üzerinde yeşil metnin okunabilir olması (şu anda okumak biraz zor) img88.imageshack.us/i/devicez.png Kırmızı iyi görünüyor. Belki sadece koyu yeşil değiştirirseniz, ama gerçekten ben anahat veya ona benzer bir şey almak isterdim
Falmarri

O halde metnin ana hatlarını çıkarmaya mı çalışıyorsunuz? Kendi özel TextView'unuzu yapmadığınız sürece bu hala mümkün değil, ancak muhtemelen değerinden daha fazla iş. Koyu yeşil yapmak muhtemelen daha kolay.
xil3

2
Kırmızı / yeşil renk körü olan birinden küçük bir istek: Lütfen aynı kırmızı / yeşil bilginin alternatif bir temsilini eklemeyi düşünün, çünkü koyu yeşil ve koyu kırmızı görmek bizim için genellikle zor. Belki bir yukarı / aşağı ok da?
Steve Pomeroy

Bu iyi bir nokta Steve. Muhtemelen bunu gelecekte ekleyeceğim.
Falmarri

0

TextView'i genişleterek bulabildiğim en basit yol

public class CustomTextView extends androidx.appcompat.widget.AppCompatTextView {

float mStroke;

public CustomTextView(Context context, @Nullable AttributeSet attrs) {
    super(context, attrs);
    TypedArray a = context.obtainStyledAttributes(attrs,
            R.styleable.CustomTextView);
    mStroke=a.getFloat(R.styleable.CustomTextView_stroke,1.0f);
    a.recycle();
}

@Override
protected void onDraw(Canvas canvas) {
    TextPaint paint = this.getPaint();
    paint.setStyle(Paint.Style.STROKE);
    paint.setStrokeWidth(mStroke);
    
    super.onDraw(canvas);
}
}

o zaman sadece aşağıdakileri attrs.xml dosyasına eklemeniz gerekir

<declare-styleable name="CustomTextView">
    <attr name="stroke" format="float"/>
</declare-styleable>

ve şimdi app:strokeTextView'in diğer tüm istenen özelliklerini korurken kontur genişliğini ayarlayabileceksiniz . benim çözümüm sadece dolgu olmadan konturu çiziyor. bu onu diğerlerinden biraz daha basit hale getiriyor. özel metin görünümüm için özel bir yazı tipi ayarlarken sonuçla birlikte bir ekran görüntüsü alıyorum.

görüntü açıklamasını buraya girin

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.