Android Textview 개요 텍스트


82

텍스트가 검은 색 윤곽을 가질 수있는 간단한 방법이 있습니까? 다른 색상의 textview가 있지만 일부 색상이 내 배경에 잘 나타나지 않아서 검은 윤곽선을 쉽게 얻을 수있는 방법이 있는지 아니면 다른 작업을 수행 할 다른 방법이 있는지 궁금합니다. 사용자 정의보기를 만들고 캔버스 등을 만들 필요가 없습니다.


6
이 질문을 읽고 Paint-Stroke 솔루션 사용을 고려중인 모든 사람 은 Android 4.4에 스트로크 관련 버그 가 있음을 유의하십시오 . 텍스트 크기가 256 픽셀 이상이면 매우 이상한 획 렌더링이 발생합니다. 해결 방법은 이 답변에 제시된 대체 방법으로 윤곽선 / 획을 그리는 입니다. 나는 모든 Stroke 유형의 답변에 이것을 스팸으로 보내고 싶지 않았기 때문에 사람들에게 알리고 내가 겪은 슬픔을 저장하기 위해 여기에 넣었습니다.
Tony Chan

답변:


54

텍스트 뒤에 그림자를 두어 가독성을 높일 수 있습니다. 녹색 텍스트에 50 % 반투명 검은 그림자로 실험 해보십시오. 이를 수행하는 방법에 대한 자세한 내용은 여기에 있습니다. Android-텍스트에 그림자?

실제로 텍스트 주위에 획을 추가하려면 다음과 같이 좀 더 복잡한 작업을 수행해야합니다. Android의 MapView에서 테두리가있는 텍스트를 어떻게 그리나요?


2
Android 4.4에는 획과 관련된 버그가 있습니다. 텍스트 크기가 256 픽셀 이상이면 매우 이상한 획 렌더링이 발생합니다. 해결 방법은 이 답변에 제시된 대체 방법을 사용하여 윤곽선 / 획을 그리는 입니다.
Tony Chan

이 주석은 textview 또는 글꼴 크기를 참조합니까?
John

그림자는 충분하지 않습니다. 흰색 배경 레이아웃의 흰색 텍스트는 여전히 그 검은 그림자와 함께 정말 나빠 보입니다
user924

81

윤곽 효과는 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"

이것이 최선의 해결책이되어야합니다. 그것은 훌륭했다! 감사합니다
Renan Bandeira

5
양면에만 표시되므로 윤곽선이 생성되지 않습니다.
ban-geoengineering

나에게 완벽 해 !!
Ely Dantas

이 그림자는 윤곽선에 대해 매우 약합니다
user924

55

그래서 조금 늦었지만 MagicTextView 는 무엇 보다도 텍스트 윤곽선을 수행합니다.

여기에 이미지 설명 입력

<com.qwerjk.better_text.MagicTextView
    xmlns:qwerjk="http://schemas.android.com/apk/res/com.qwerjk.better_text"
    android:textSize="78dp"
    android:textColor="#ff333333"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    qwerjk:strokeColor="#FFff0000"
    qwerjk:strokeJoinStyle="miter"
    qwerjk:strokeWidth="5"
    android:text="Magic" />

참고 : 나는 이것을 만들고 OP보다 미래의 여행자를 위해 더 많은 것을 게시하고 있습니다. 경계선 스팸이지만 주제와 관련이 있습니다.


1
안녕하세요, EditText에 입력되는 텍스트에 이와 같은 테두리를 어떻게 추가 할 수 있습니까?
TilalHusain 2014 년

EditText에 대한 아이디어가 있습니까?
Piotr

dreamText.setStroke (4, Color.BLACK); dreamText.setTextColor (Color.WHITE); 이 설정을 사용하고 있지만 텍스트 색상이 투명 해 검은 윤곽선을 볼 수 있습니다. 뭐가 잘못 되었 니?
Muhammad Umar 2015

괜찮지 만 실제로 테두리를 추가하지는 않습니다. 오히려 텍스트를 취하고 동일한 시각적 결과를 제공하지 않는 테두리로 바깥 쪽 가장자리를 사용합니다.
Warpzit

1
이 솔루션은 내부 onDraw호출로 인해 재귀 방식으로 호출 setTextColor됩니다 onDraw.
Sermilion

23

프레임 워크는 text-shadow를 지원하지만 text-outline은 지원하지 않습니다. 그러나 트릭이 있습니다. 그림자는 반투명하고 희미 해지는 것입니다. 그림자를 몇 번 다시 그리면 모든 알파가 합산되고 결과가 윤곽선이됩니다.

매우 간단한 구현 TextViewdraw(..)메서드를 확장 하고 재정의합니다 . 추첨이 요청 될 때마다 우리의 서브 클래스는 5-10 개의 그림을 그립니다.

public class OutlineTextView extends TextView {

    // Constructors

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

}


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

3
대단히 감사합니다. 그러나 차라리이 방법을 사용합니다. '@Override protected void onDraw (Canvas canvas) {for (int i = 0; i <5; i ++) {super.onDraw (canvas); }} '
IHeartAndroid

1
추가 정보 : 최소한 Context 및 AttributeSet을 사용하여 ctor를 구현해야합니다. 그렇지 않으면 실행됩니다. java.lang.NoSuchMethodException: <init> [class android.content.Context, interface android.util.AttributeSet
Bevor

22

꽤 오래된 질문이지만 여전히 완전한 답을 볼 수 없습니다. 그래서 나는이 문제로 고군분투하는 누군가가 유용하다고 생각하기를 바라 면서이 솔루션을 게시하고 있습니다. 가장 간단하고 효과적인 솔루션은 TextView 클래스의 onDraw 메서드를 재정의하는 것입니다. 필자가 본 대부분의 구현에서는 drawText 메서드를 사용하여 획을 그렸지만이 방법은 들어오는 모든 서식 정렬 및 텍스트 줄 바꿈을 고려하지 않습니다. 결과적으로 획과 텍스트가 다른 위치에서 끝나는 경우가 많습니다. 다음 접근 방식은 super.onDraw를 사용하여 텍스트의 획과 채우기 부분을 모두 그리므로 나머지 부분에 대해 신경 쓸 필요가 없습니다. 단계는 다음과 같습니다.

  1. TextView 클래스 확장
  2. onDraw 메서드 재정의
  3. 페인트 스타일을 FILL로 설정
  4. 채우기 모드에서 텍스트를 렌더링하려면 Draw에서 부모 클래스를 호출합니다.
  5. 현재 텍스트 색상을 저장합니다.
  6. 현재 텍스트 색상을 획 색상으로 설정
  7. 페인트 스타일을 스트로크로 설정
  8. 획 너비 설정
  9. 그리고 부모 클래스 onDraw를 다시 호출하여 이전에 렌더링 된 텍스트 위에 획을 그립니다.

    package com.example.widgets;
    
    import android.content.Context;
    import android.content.res.TypedArray;
    import android.graphics.Canvas;
    import android.graphics.Paint;
    import android.graphics.Typeface;
    import android.util.AttributeSet;
    import android.widget.Button;
    
    public class StrokedTextView extends Button {
    
        private static final int DEFAULT_STROKE_WIDTH = 0;
    
        // fields
        private int _strokeColor;
        private float _strokeWidth;
    
        // constructors
        public StrokedTextView(Context context) {
            this(context, null, 0);
        }
    
        public StrokedTextView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public StrokedTextView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
    
            if(attrs != null) {
                TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.StrokedTextAttrs);
                _strokeColor = a.getColor(R.styleable.StrokedTextAttrs_textStrokeColor,
                        getCurrentTextColor());         
                _strokeWidth = a.getFloat(R.styleable.StrokedTextAttrs_textStrokeWidth,
                        DEFAULT_STROKE_WIDTH);
    
                a.recycle();
            }
            else {          
                _strokeColor = getCurrentTextColor();
                _strokeWidth = DEFAULT_STROKE_WIDTH;
            } 
            //convert values specified in dp in XML layout to
            //px, otherwise stroke width would appear different
            //on different screens
            _strokeWidth = dpToPx(context, _strokeWidth);           
        }    
    
        // getters + setters
        public void setStrokeColor(int color) {
            _strokeColor = color;        
        }
    
        public void setStrokeWidth(int width) {
            _strokeWidth = width;
        }
    
        // overridden methods
        @Override
        protected void onDraw(Canvas canvas) {
            if(_strokeWidth > 0) {
                //set paint to fill mode
                Paint p = getPaint();
                p.setStyle(Paint.Style.FILL);        
                //draw the fill part of text
                super.onDraw(canvas);       
                //save the text color   
                int currentTextColor = getCurrentTextColor();    
                //set paint to stroke mode and specify 
                //stroke color and width        
                p.setStyle(Paint.Style.STROKE);
                p.setStrokeWidth(_strokeWidth);
                setTextColor(_strokeColor);
                //draw text stroke
                super.onDraw(canvas);      
               //revert the color back to the one 
               //initially specified
               setTextColor(currentTextColor);
           } else {
               super.onDraw(canvas);
           }
       }
    
       /**
        * Convenience method to convert density independent pixel(dp) value
        * into device display specific pixel value.
        * @param context Context to access device specific display metrics 
        * @param dp density independent pixel value
        * @return device specific pixel value.
        */
       public static int dpToPx(Context context, float dp)
       {
           final float scale= context.getResources().getDisplayMetrics().density;
           return (int) (dp * scale + 0.5f);
       }            
    }
    

그게 다입니다. 이 클래스는 사용자 정의 XML 특성을 사용하여 XML 레이아웃 파일에서 획 색상 및 너비를 지정할 수 있습니다. 따라서 'res'폴더 아래의 'values'하위 폴더에있는 attr.xml 파일에 이러한 속성을 추가해야합니다. 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>

이 작업을 마치면 XML 레이아웃 파일에서 사용자 정의 StrokedTextView 클래스를 사용하고 획 색상과 너비도 지정할 수 있습니다. 다음은 예입니다.

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

패키지 이름을 프로젝트의 패키지 이름으로 바꾸는 것을 잊지 마십시오. 또한 사용자 정의 XML 속성을 사용하려면 레이아웃 파일에 xmlns 네임 스페이스를 추가하십시오. 레이아웃 파일의 루트 노드에 다음 줄을 추가 할 수 있습니다.

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

2
얼마나 훌륭하고 우아한 솔루션입니까! 나는 이것을 구현했고 잘 작동합니다. 방금 textStrokeWidth를 차원 (및 a.getDimensionPixelSize)으로 변경했습니다. 감사!
dgmltn

1
감사합니다. 윤곽선이 전체 텍스트를 차지함에 따라 필자의 경우 순서를 변경했습니다. 먼저 윤곽선을 그린 다음 텍스트를 그렸습니다.
mdiener 2016

2
잘 작동합니다. 외곽선을 테스트하기 위해 Android Studio 디자인보기를 사용하지 마십시오. 표현이 충분히 정확하지 않습니다. 2 시간 동안 문제가없는 디버깅을했습니다.
llmora

7
이 솔루션은 setTextColor 호출이 무효화되기 때문에 무한한 수의 onDraw를 유발합니다.
Guliash

1
실제로 @Guliash가 맞습니다. 테스트 후이 메서드가 호출되면 invalidate()의 내부 작업에 묻힌 호출 로 인해 무한 루프가 발생합니다 setTextColor. 코드의 마지막 줄을 모두 TextView자신의 클래스로 복사하지 않는 한 , 제가 ​​볼 수있는 유일한 방법은 Reflection 을 사용 하는 개인 mCurTextColor 필드에 무차별 대입 액세스하는 것 TextView입니다. 참조 이 답변 을하는 방법을 대략적으로 볼 수 있습니다. 을 사용하는 field.set(this, colorInt)대신 사용하십시오 field.get().
VerumCH

15

나는 이것을하는 방법을 알아 내려고 노력했지만 온라인에서 좋은 가이드를 찾을 수 없었지만 결국 알아 냈습니다. Steve Pomeroy가 제안했듯이, 더 많은 작업을해야합니다. 윤곽선이있는 텍스트 효과를 얻으려면 텍스트를 두 번 그립니다. 한 번은 두꺼운 윤곽선으로 한 다음 두 번째는 윤곽선 위에 주 텍스트를 그립니다. 그러나 SDK와 함께 제공되는 코드 샘플 중 하나, 즉 SDK 디렉토리에서 다음 이름 아래에있는 코드 샘플을 매우 쉽게 수정할 수 있으므로 작업이 더 쉬워집니다. "/ samples / android- / ApiDemos / src / com / example / android /apis/view/LabelView.java "입니다. 여기 Android 개발자 웹 사이트에서도 찾을 수 있습니다. .

수행중인 작업에 따라 TextView에서 확장하도록 변경하는 등 해당 코드를 약간만 수정하면됩니다.이 샘플을 발견하기 전에 onMeasure ()를 재정의하는 것을 잊었습니다. Android 개발자 웹 사이트의 "Building Custom Components"가이드에 언급 된대로 onDraw ()를 재정의하는 것 외에도 수행해야합니다. 이것이 문제가 발생한 이유 중 일부입니다.

일단 완료하면 내가 한 일을 할 수 있습니다.

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

따라서 윤곽선이있는 텍스트 효과를 얻으려면 텍스트를 두 번 그립니다. 한 번은 두꺼운 윤곽선으로 한 다음 두 번째는 윤곽선 위에 주 텍스트를 그립니다.


8

MagicTextView의 스트로크 IMO보다 더 잘 작동하는 트릭이 있습니다.

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

나는 종류의 참조의 텍스트 뷰의 오른쪽이 잘립니다되고 있음 - 그것은 방에서 실행되는 것처럼 윤곽이 완전히 ... 그 측면에 그릴되지 않는다
RoundSparrow는 hilltx

7
하나 더. setTextColor가 다시 그리기를 강제하는 것으로 의심됩니다. 이로 인해이 onDraw의 무한 루프가 계속해서 호출됩니다. 이 방법에 logcat 또는 기타 표시기를 넣는 것이 테스트 중에 권장됩니다.
RoundSparrow hilltx

@RoundSparrowhilltx가 정확합니다. 다른 유사한 답변에 대한 주석에서 언급했듯이 전체를 복사 TextView하여 자신의 클래스에 붙여 넣는 유일한 방법 은 Reflection을 사용하여 .NET의 개인 mCurTextColor 필드에 직접 액세스하는 것 입니다 TextView. 이 답변 은이를 수행하는 방법에 대한 일반적인 지침을 제공합니다. 힌트 및 링크 텍스트에도 획이 포함되도록하려면 mHintTextColor및 을 변경해야합니다 mLinkTextColor. 불행히도 변경 mTextColor은 참조 만하므로 아무것도하지 않습니다.
VerumCH

in will loop onDraw forever
user924

8

개요가있는 텍스트를 수행하고 다른 모든 속성과 일반 텍스트보기의 그리기를 지원하는 클래스를 작성했습니다.

그것은 기본적으로 사용 super.onDraw(Canves canvas)TextView 하지만 다른 스타일로 두 번 그립니다.

도움이 되었기를 바랍니다.

public class TextViewOutline extends TextView {

    // constants
    private static final int DEFAULT_OUTLINE_SIZE = 0;
    private static final int DEFAULT_OUTLINE_COLOR = Color.TRANSPARENT;

    // data
    private int mOutlineSize;
    private int mOutlineColor;
    private int mTextColor;
    private float mShadowRadius;
    private float mShadowDx;
    private float mShadowDy;
    private int mShadowColor;

    public TextViewOutline(Context context) {
        this(context, null);
    }

    public TextViewOutline(Context context, AttributeSet attrs) {
        super(context, attrs);
        setAttributes(attrs);
    }

    private void setAttributes(AttributeSet attrs){ 
        // set defaults
        mOutlineSize = DEFAULT_OUTLINE_SIZE;
        mOutlineColor = DEFAULT_OUTLINE_COLOR;   
        // text color   
        mTextColor = getCurrentTextColor();
        if(attrs != null) {
            TypedArray a = getContext().obtainStyledAttributes(attrs,R.styleable.TextViewOutline);
            // outline size
            if (a.hasValue(R.styleable.TextViewOutline_outlineSize)) {
                mOutlineSize = (int) a.getDimension(R.styleable.TextViewOutline_outlineSize, DEFAULT_OUTLINE_SIZE);
            }
            // outline color
            if (a.hasValue(R.styleable.TextViewOutline_outlineColor)) {
                mOutlineColor = a.getColor(R.styleable.TextViewOutline_outlineColor, DEFAULT_OUTLINE_COLOR);
            }
            // shadow (the reason we take shadow from attributes is because we use API level 15 and only from 16 we have the get methods for the shadow attributes)
            if (a.hasValue(R.styleable.TextViewOutline_android_shadowRadius) 
                    || a.hasValue(R.styleable.TextViewOutline_android_shadowDx)
                    || a.hasValue(R.styleable.TextViewOutline_android_shadowDy) 
                    || a.hasValue(R.styleable.TextViewOutline_android_shadowColor)) {
                mShadowRadius = a.getFloat(R.styleable.TextViewOutline_android_shadowRadius, 0);
                mShadowDx = a.getFloat(R.styleable.TextViewOutline_android_shadowDx, 0);
                mShadowDy = a.getFloat(R.styleable.TextViewOutline_android_shadowDy, 0);
                mShadowColor = a.getColor(R.styleable.TextViewOutline_android_shadowColor, Color.TRANSPARENT);
            }

            a.recycle();
        }

        PFLog.d("mOutlineSize = " + mOutlineSize);
        PFLog.d("mOutlineColor = " + mOutlineColor);
    }

    private void setPaintToOutline(){
        Paint paint = getPaint();
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(mOutlineSize);
        super.setTextColor(mOutlineColor);
        super.setShadowLayer(mShadowRadius, mShadowDx, mShadowDy,  mShadowColor);
    }

    private void setPaintToRegular() {
        Paint paint = getPaint();
        paint.setStyle(Paint.Style.FILL);
        paint.setStrokeWidth(0);
        super.setTextColor(mTextColor);
        super.setShadowLayer(0, 0, 0, Color.TRANSPARENT);
    } 

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setPaintToOutline();
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    public void setTextColor(int color) {
        super.setTextColor(color);
        mTextColor = color;
    } 

    @Override
    public void setShadowLayer(float radius, float dx, float dy, int color) {
        super.setShadowLayer(radius, dx, dy, color);
        mShadowRadius = radius;
        mShadowDx = dx;
        mShadowDy = dy;
        mShadowColor = color;
    }

    public void setOutlineSize(int size){
        mOutlineSize = size;
    }

    public void setOutlineColor(int color){
       mOutlineColor = color;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        setPaintToOutline();
        super.onDraw(canvas);
        setPaintToRegular();
        super.onDraw(canvas);
    }

}

attr.xml

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

하는 TypedArray에 a.recycle ()이없는
레오 Literak

7

@YGHM에 대한 크레딧 그림자 지원 추가 여기에 이미지 설명 입력

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

}

속성 정의

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

아래 xml 코드

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

5

아래 스 니펫을 사용하여 프로그래밍 방식으로이를 수행 할 수 있습니다. 검정색 배경에 흰색 글자를 제공합니다.

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

메서드의 매개 변수는 radius, dx, dy, color입니다. 특정 요구에 맞게 변경할 수 있습니다.

프로그래밍 방식으로 TextView를 만들고 xml에 포함하지 않는 사람을 도울 수 있기를 바랍니다.

stackOverflow 커뮤니티를 응원합니다!


2

Nouman Hanif의 답변 을 기반으로 몇 가지 추가 사항을 기반으로 라이브러리를 만들었습니다 . 예를 들어 View.invalidate () 호출에서 간접 무한 루프를 발생시킨 버그를 수정했습니다.

OTOH, 라이브러리는 또한 EditText 위젯에서 윤곽선이있는 텍스트를 지원합니다. 이것이 저의 실제 목표 였고 TextView보다 약간 더 많은 작업이 필요했기 때문입니다.

다음은 내 라이브러리 링크입니다. https://github.com/biomorgoth/android-outline-textview

솔루션에 대한 초기 아이디어에 대해 Nouman Hanif에게 감사드립니다!


2

성능 문제를 해결하기 위해 솔루션을 추가하고 싶습니다. 예를 들어, @YGHM 몇 다른 사람의 대답은 일을하지만, 무한한 호출 원인 onDraw때문에 setTextColor전화를 invalidate(). 따라서이 문제를 해결하려면 진행 중일 때 획을 사용하여 그릴 때로 설정할 invalidate()변수 를 재정의 하고 추가 해야합니다 . 변수가이면 invalidate가 반환됩니다 .isDrawingtrueonDraw()true

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

onDraw는 다음과 같습니다.

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는 획 글꼴을 만드는 데 매우 유용하지만 제 경우에는 다음과 같은 오류가 발생 합니다. MagicTextView에서 설정 한 중복 배경 속성으로 인해 같은 오류가 발생합니다.

따라서 attrs.xml 및 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));
}
}

1

TextView 에서 상속하지 않고보기를 개요하는 간단한 방법을 찾았습니다 . Android의 Spannable 을 사용하는 간단한 라이브러리를 작성 했습니다. 을 텍스트를 설명 . 이 솔루션은 텍스트의 일부만 윤곽을 그릴 수있는 가능성을 제공합니다.

나는 이미 같은 질문에 대해 대답했습니다 ( 대답 )

수업:

class OutlineSpan(
        @ColorInt private val strokeColor: Int,
        @Dimension private val strokeWidth: Float
): ReplacementSpan() {

    override fun getSize(
            paint: Paint,
            text: CharSequence,
            start: Int,
            end: Int,
            fm: Paint.FontMetricsInt?
    ): Int {
        return paint.measureText(text.toString().substring(start until end)).toInt()
    }


    override fun draw(
            canvas: Canvas,
            text: CharSequence,
            start: Int,
            end: Int,
            x: Float,
            top: Int,
            y: Int,
            bottom: Int,
            paint: Paint
    ) {
        val originTextColor = paint.color

        paint.apply {
            color = strokeColor
            style = Paint.Style.STROKE
            this.strokeWidth = this@OutlineSpan.strokeWidth
        }
        canvas.drawText(text, start, end, x, y.toFloat(), paint)

        paint.apply {
            color = originTextColor
            style = Paint.Style.FILL
        }
        canvas.drawText(text, start, end, x, y.toFloat(), paint)
    }

}

라이브러리 : OutlineSpan


여러 줄 텍스트를 지원하지 않습니다
user924

0

그래서 textview 주위에 스트로크를 원하십니까? 안타깝게도 스타일링을 할 수있는 간단한 방법은 없습니다. 다른 뷰를 만들고 텍스트 뷰를 위에 배치하여 상위 뷰 (위에있는 뷰)를 몇 픽셀 더 크게 만들어야합니다. 이렇게하면 윤곽선이 만들어집니다.


흠, 그만한 가치가있는 것보다 더 고통스러운 것 같습니다. 내가 신경 쓰는 것은 흰색 배경에 녹색 텍스트를 읽을 수 있도록하는 것입니다 (지금은 읽기가 어렵습니다). img88.imageshack.us/i/devicez.png 빨간색은 괜찮아 보입니다. 어쩌면 난 그냥 어두운 녹색으로 변경하는 경우,하지만 난 정말 내가 윤곽 또는 무언가의 일종 얻을 수 있으면 좋겠다
Falmarri

그렇다면 텍스트 자체의 윤곽을 잡으려고합니까? 사용자 정의 TextView를 수행하지 않는 한 여전히 실제로 가능하지 않지만 가치가있는 것보다 더 많은 작업을 수행 할 수 있습니다. 짙은 녹색으로 만드는 것이 더 쉬울 것입니다.
xil3

2
적색 / 녹색 색맹 인 사람의 사소한 요청 : 짙은 녹색과 짙은 적색을 보는 것이 종종 어렵 기 때문에 동일한 적색 / 녹색 정보의 대체 표현을 추가하는 것을 고려하십시오. 아마도 위쪽 / 아래쪽 화살표일까요?
Steve Pomeroy

좋은 지적 스티브입니다. 나는 아마 나중에 그것을 추가 할 것입니다.
Falmarri 2010
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.