Văn bản phác thảo textview Android


82

Có cách nào đơn giản để văn bản có thể có viền đen không? Tôi có các chế độ xem văn bản sẽ có các màu khác nhau, nhưng một số màu không hiển thị tốt trên nền của tôi, vì vậy tôi đã tự hỏi liệu có cách nào dễ dàng để có được đường viền màu đen hoặc thứ gì khác sẽ thực hiện công việc không? Tôi không muốn phải tạo một chế độ xem tùy chỉnh và tạo một canvas và những thứ như vậy.


6
Đối với bất kỳ ai đang đọc câu hỏi này và đang cân nhắc sử dụng giải pháp Paint-Stroke, xin lưu ý rằng có một lỗi với các nét vẽ trong Android 4.4 . Nếu kích thước văn bản nếu trên 256 pixel, nó dẫn đến kết xuất nét rất kỳ lạ. Một giải pháp khác là vẽ đường viền / nét vẽ bằng phương pháp thay thế được trình bày trong câu trả lời này . Tôi không muốn spam câu trả lời này trên mọi câu trả lời kiểu Stroke, vì vậy hãy đặt nó ở đây để mọi người biết và tránh cho họ nỗi đau mà tôi đã trải qua.
Tony Chan

Câu trả lời:


54

Bạn có thể đặt bóng phía sau văn bản, điều này thường có thể giúp dễ đọc. Hãy thử thử nghiệm với 50% bóng đen mờ trên văn bản màu xanh lá cây của bạn. Chi tiết về cách thực hiện điều này ở đây: Android - bóng trên văn bản?

Để thực sự thêm một nét viền xung quanh văn bản, bạn cần phải làm điều gì đó liên quan hơn một chút, như sau: Làm cách nào để bạn vẽ văn bản có viền trên MapView trong Android?


2
Xin lưu ý rằng có một lỗi với đột quỵ trong Android 4.4 . Nếu kích thước văn bản nếu trên 256 pixel, nó dẫn đến kết xuất nét rất kỳ lạ. Một giải pháp khác là vẽ đường viền / nét vẽ bằng phương pháp thay thế được trình bày trong câu trả lời này .
Tony Chan

Nhận xét này đề cập đến chế độ xem văn bản hay kích thước phông chữ?
John

bóng tối là không đủ tốt, văn bản màu trắng trên bố cục nền trắng trông vẫn thực sự tồi tệ với bóng đen đó
user924

81

Hiệu ứng phác thảo có thể đạt được bằng cách sử dụng bóng trong 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"

đây sẽ là giải pháp tốt nhất. Nó thật tuyệt! Cảm ơn
Renan Bandeira

5
Điều này không dẫn đến một phác thảo vì nó chỉ hiển thị trên hai mặt.
ban-geoengineering

Hoàn hảo với tôi !!
Ely Dantas

bóng này rất yếu đối với đường viền
user924

55

Vì vậy, hơi muộn, nhưng MagicTextView sẽ làm phác thảo văn bản, trong số những thứ khác.

nhập mô tả hình ảnh ở đây

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

Lưu ý: Tôi đã thực hiện điều này và đang đăng nhiều hơn vì lợi ích của những khách du lịch trong tương lai hơn là OP. Đó là thư rác biên giới, nhưng có chủ đề, có lẽ được chấp nhận?


1
Xin chào, làm cách nào chúng ta có thể thêm các đường viền như thế này vào văn bản đang được nhập trong EditText?
TilalHusain

Bất kỳ ý tưởng nào về EditText?
Piotr

dreamText.setStroke (4, Color.BLACK); dreamText.setTextColor (Color.WHITE); Tôi đang sử dụng các cài đặt này nhưng màu văn bản của tôi là màu trong suốt, tôi có thể nhìn thấy đường viền màu đen. Chuyện gì thế?
Muhammad Umar

không sao, nhưng nó không thực sự thêm đường viền. Nó thay vào đó lấy văn bản và sử dụng cạnh bên ngoài làm đường viền, điều này không cho kết quả trực quan giống nhau.
Warpzit

1
Giải pháp này onDrawđược gọi theo cách đệ quy vì lời gọi setTextColorbên trong của onDraw.
Sermilion

23

Khung hỗ trợ text-shadow nhưng không hỗ trợ text-outline. Nhưng có một mẹo nhỏ: bóng tối là một thứ gì đó mờ và mờ dần. Vẽ lại bóng một vài lần và tất cả alpha được tổng hợp lại và kết quả là một đường viền.

Một triển khai rất đơn giản sẽ mở rộng TextViewvà ghi đè draw(..)phương thức. Mỗi khi yêu cầu rút, lớp con của chúng tôi thực hiện 5-10 bản vẽ.

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
Cảm ơn rât nhiều. Tuy nhiên, tôi thích sử dụng phương pháp này hơn: '@Override được bảo vệ void onDraw (canvas canvas) {for (int i = 0; i <5; i ++) {super.onDraw (canvas); }} '
IHeartAndroid

1
Thông tin bổ sung: Người ta phải triển khai ít nhất ctor với Context và AttributeSet. Nếu không bạn sẽ gặp phải. java.lang.NoSuchMethodException: <init> [class android.content.Context, interface android.util.AttributeSet
Bevor

22

Đó là một câu hỏi khá cũ nhưng tôi vẫn không thấy bất kỳ câu trả lời đầy đủ nào. Vì vậy, tôi đăng giải pháp này, hy vọng rằng ai đó đang vật lộn với vấn đề này có thể thấy nó hữu ích. Giải pháp đơn giản và hiệu quả nhất là ghi đè phương thức onDraw của lớp TextView. Hầu hết các triển khai mà tôi đã thấy đều sử dụng phương thức drawText để vẽ nét nhưng cách tiếp cận đó không tính đến tất cả các căn chỉnh định dạng và gói văn bản đi kèm. Và kết quả là thường nét và văn bản kết thúc ở những vị trí khác nhau. Cách tiếp cận sau sử dụng super.onDraw để vẽ cả phần nét và phần tô của văn bản, do đó bạn không phải bận tâm về phần còn lại của nội dung. Đây là các bước

  1. Mở rộng lớp TextView
  2. Ghi đè phương thức onDraw
  3. Đặt kiểu sơn thành ĐIỀN
  4. gọi lớp cha trên Draw để hiển thị văn bản ở chế độ điền.
  5. lưu màu văn bản hiện tại.
  6. Đặt màu văn bản hiện tại thành màu nét của bạn
  7. Đặt kiểu sơn thành Stroke
  8. Đặt chiều rộng nét vẽ
  9. Và gọi lớp cha onDraw một lần nữa để vẽ nét trên văn bản được kết xuất trước đó.

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

Đó là tất cả. Lớp này sử dụng các thuộc tính XML tùy chỉnh để cho phép chỉ định màu nét và chiều rộng từ các tệp bố cục XML. Do đó, bạn cần thêm các thuộc tính này vào tệp attr.xml của mình trong 'giá trị' thư mục con trong thư mục 'res'. Sao chép và dán nội dung sau vào tệp attr.xml của bạ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>

Khi bạn đã hoàn tất việc đó, bạn có thể sử dụng lớp StrokedTextView tùy chỉnh trong các tệp bố cục XML của mình và chỉ định cả chiều rộng và màu nét. Đây là một ví dụ

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

Hãy nhớ thay thế tên gói bằng tên gói dự án của bạn. Đồng thời thêm không gian tên xmlns vào tệp bố cục để sử dụng các thuộc tính XML tùy chỉnh. Bạn có thể thêm dòng sau vào nút gốc của tệp bố cục của mình.

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

2
Thật là một giải pháp tuyệt vời, thanh lịch! Tôi đã thực hiện điều này và nó hoạt động tốt. Tôi vừa thay đổi textStrokeWidth thành một thứ nguyên (và a.getDimensionPixelSize). Cảm ơn!
dgmltn

1
Hoạt động tốt, cảm ơn. Khi họ phác thảo toàn bộ văn bản, tôi đã thay đổi thứ tự trong trường hợp của mình: đầu tiên đường viền được vẽ và sau đó là văn bản.
mdiener

2
Hoạt động tuyệt vời. Không sử dụng chế độ xem thiết kế Android Studio để kiểm tra các đường viền, hình ảnh đại diện không đủ chính xác. Chỉ cần dành 2 giờ để gỡ lỗi một vấn đề không.
llmora

7
Giải pháp này sẽ gây ra vô số onDraw, vì các lệnh gọi setTextColor không hợp lệ.
Guliash

1
Trên thực tế, @Guliash là chính xác. Sau khi thử nghiệm, một khi phương thức này được gọi, nó gây ra một vòng lặp vô hạn của việc gọi chính nó do invalidate()cuộc gọi bị chôn vùi trong hoạt động bên trong của setTextColor. Trừ khi bạn muốn sao chép tất cả các dòng cuối cùng của mã từ TextViewvào lớp của riêng bạn, cách duy nhất xung quanh này mà tôi có thể thấy là để truy cập brute-force các tin mCurTextColor lĩnh vực TextViewsử dụng Reflection. Xem câu trả lời này để biết đại khái cách thực hiện. Chỉ sử dụng field.set(this, colorInt)thay vì sử dụng field.get().
VerumCH

15

Tôi chỉ đang cố gắng tìm ra cách làm điều này và không thể tìm thấy một hướng dẫn tốt trên mạng nhưng cuối cùng đã tìm ra nó. Như Steve Pomeroy đã đề xuất, bạn phải làm điều gì đó liên quan hơn. Để có được hiệu ứng văn bản có đường viền, bạn vẽ văn bản hai lần: một lần với đường viền dày và sau đó lần thứ hai chúng ta vẽ văn bản chính trên đường viền. Tuy nhiên, nhiệm vụ được thực hiện dễ dàng hơn vì bạn có thể rất dễ dàng điều chỉnh một trong các mẫu mã được cung cấp với SDK, cụ thể là mẫu dưới tên này trong thư mục SDK của bạn: "/ samples / android- / ApiDemos / src / com / example / android /apis/view/LabelView.java ". Cũng có thể tìm thấy trên trang web của nhà phát triển Android tại đây .

Tùy thuộc vào những gì bạn đang làm, rất dễ nhận thấy rằng bạn sẽ chỉ cần thực hiện các sửa đổi nhỏ đối với mã đó, chẳng hạn như thay đổi nó để mở rộng từ TextView, v.v. Trước khi tôi phát hiện ra mẫu này, tôi đã quên ghi đè onMeasure () (mà bạn phải làm ngoài việc ghi đè onDraw () như được đề cập trong hướng dẫn "Tạo thành phần tùy chỉnh" trên trang web dành cho nhà phát triển Android), đó là một phần lý do tại sao tôi gặp sự cố.

Khi bạn đã làm điều đó, bạn có thể làm những gì tôi đã làm:

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

Vì vậy, để có được hiệu ứng văn bản có dàn ý, bạn vẽ văn bản hai lần: một lần với đường viền dày và sau đó lần thứ hai chúng ta vẽ văn bản chính trên đường viền.


8

Đây là thủ thuật tôi thấy hoạt động tốt hơn IMO đột quỵ của MagicTextView

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

Tôi loại thấy rằng phía bên phải của TextView được cắt - và đề cương không được vẽ hoàn toàn đứng về phía đó ... như nếu nó chạy ra khỏi phòng
RoundSparrow hilltx

7
Một điều nữa. Tôi nghi ngờ setTextColor đang buộc vẽ lại - điều này gây ra một vòng lặp vô tận của onDraw này được gọi đi gọi lại. Nên đặt logcat hoặc chỉ báo khác trong phương pháp này trong khi thử nghiệm.
RoundSparrow hilltx

@RoundSparrowhilltx là đúng. Như tôi đã đề cập trong một nhận xét cho một câu trả lời tương tự khác, tôi nghi ngờ cách duy nhất để sao chép và dán toàn bộ TextViewvào lớp của riêng bạn là sử dụng Reflection để truy cập trực tiếp vào trường riêng tư mCurTextColor trong TextView. Câu trả lời này cung cấp một hướng dẫn chung về cách thực hiện việc này. Nếu bạn muốn gợi ý và văn bản liên kết cũng có một nét, bạn cũng sẽ phải thay đổi mHintTextColormLinkTextColor. Thật không may, thay đổi mTextColorkhông làm gì cả, vì nó chỉ được tham khảo.
VerumCH

in will loop onDraw forever
user924

8

Tôi đã viết một lớp để thực hiện văn bản với đường viền và vẫn hỗ trợ tất cả các thuộc tính khác và bản vẽ của một dạng xem văn bản bình thường.

về cơ bản nó sử dụng super.onDraw(Canves canvas)on the TextViewnhưng vẽ hai lần với các phong cách khác nhau.

hi vọng điêu nay co ich.

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>

a.recycle () trên TypedArray là mất tích
Sư Tử Literak

7

tín dụng cho @YGHM thêm hỗ trợ bóng nhập mô tả hình ảnh ở đây

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 xác định

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

mã xml bên dưới

<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

Bạn có thể làm điều này theo chương trình với đoạn mã dưới đây. Điều đó cung cấp các chữ cái màu trắng với nền đen:

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

Các tham số của phương pháp là radius, dx, dy, color. Bạn có thể thay đổi chúng cho nhu cầu cụ thể của bạn.

Tôi hy vọng tôi sẽ giúp ai đó tạo TextView theo lập trình và không có nó bên trong xml.

Chúc mừng cộng đồng stackOverflow!


2

Tôi đã tạo một thư viện dựa trên câu trả lời của Nouman Hanif với một số bổ sung. Ví dụ: sửa lỗi gây ra vòng lặp vô hạn gián tiếp trên các lệnh gọi View.invalidate ().

OTOH, thư viện cũng hỗ trợ văn bản được phác thảo trong các widget EditText, vì đó là mục tiêu thực sự của tôi và nó cần nhiều công việc hơn TextView một chút.

Đây là liên kết đến thư viện của tôi: https://github.com/biomorgoth/android-outline-textview

Cảm ơn Nouman Hanif về ý tưởng ban đầu về giải pháp!


2

Tôi muốn thêm một giải pháp để giải quyết vấn đề hiệu suất. Ví dụ, câu trả lời của @YGHM và một vài người khác không được công việc, nhưng nó gây ra cuộc gọi vô hạn của onDrawsetTextColorcác cuộc gọi invalidate(). Vì vậy, để giải quyết nó, bạn cũng cần ghi đè invalidate()và thêm một biến isDrawingmà bạn sẽ thiết lập true, khi nào onDraw()đang được thực hiện và vẽ bằng nét. voidate sẽ trả về nếu biến là true.

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

OnDraw của bạn sẽ trông như thế này:

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 rất hữu ích để tạo phông chữ đột quỵ, nhưng trong trường hợp của tôi, nó gây ra lỗi như thế này , lỗi này do các thuộc tính nền trùng lặp do MagicTextView đặt

vì vậy bạn cần chỉnh sửa attrs.xml và 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

Tôi đã tìm thấy cách đơn giản để phác thảo chế độ xem mà không cần kế thừa từ TextView . Tôi đã viết thư viện đơn giản sử dụng Spannable của Android để phác thảo văn bản. Giải pháp này cung cấp khả năng chỉ phác thảo một phần của văn bản.

Tôi đã trả lời cho cùng một câu hỏi ( câu trả lời )

Lớp học:

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

}

Thư viện: OutlineSpan


nó không hỗ trợ văn bản nhiều dòng
user924

0

Vì vậy, bạn muốn một nét xung quanh textview? Thật không may, không có cách nào đơn giản để làm điều đó với việc tạo kiểu. Bạn sẽ phải tạo một chế độ xem khác và đặt chế độ xem văn bản của bạn lên trên, làm cho chế độ xem chính (chế độ xem ở trên cùng) chỉ lớn hơn một vài pixel - điều này sẽ tạo ra một đường viền.


Hmm, điều đó nghe có vẻ đau đớn hơn là đáng. Tất cả những gì tôi quan tâm là có văn bản màu xanh lá cây có thể đọc được trên nền trắng (hiện tại nó hơi khó đọc) img88.imageshack.us/i/devicez.png Màu đỏ trông rất ổn. Có lẽ nếu tôi chỉ đổi sang màu xanh lá cây đậm hơn, nhưng tôi thực sự ước mình có thể có được một số phác thảo hoặc một cái gì đó
Falmarri

Bạn có đang cố gắng phác thảo văn bản không? Điều đó vẫn không thực sự khả thi, trừ khi bạn thực hiện TextView tùy chỉnh của riêng mình, nhưng có lẽ nó còn nhiều việc hơn giá trị. Nó có lẽ dễ dàng hơn chỉ để làm cho nó có màu xanh đậm.
xil3

2
Một yêu cầu nhỏ từ một người mù màu đỏ / xanh lá cây: vui lòng xem xét thêm một biểu diễn thay thế của cùng một thông tin màu đỏ / xanh lá cây, vì chúng tôi thường khó nhìn thấy màu xanh lá cây đậm so với màu đỏ đậm. Có thể là một mũi tên lên / xuống?
Steve Pomeroy

Đó là một điểm tốt Steve. Tôi có thể sẽ thêm điều đó trong tương lai.
Falmarri
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.