【问题标题】:Android textview outline textAndroid textview 大纲文本
【发布时间】:2011-03-12 01:50:12
【问题描述】:

有没有一种简单的方法可以让文本具有黑色轮廓?我有不同颜色的文本视图,但有些颜色并没有很好地显示在我的背景上,所以我想知道是否有一种简单的方法可以获得黑色轮廓或其他可以完成这项工作的方法?我宁愿不必创建自定义视图并制作画布等。

【问题讨论】:

  • 对于阅读此问题并考虑使用 Paint-Stroke 解决方案的任何人,请注意有一个 bug with strokes in Android 4.4。如果文本大小超过 256 像素,则会导致非常奇怪的笔画渲染。一种解决方法是使用替代方法presented in this answer 绘制轮廓/笔划。我不想在每个 Stroke 类型的答案上都发这个垃圾邮件,所以把它放在这里是为了让人们意识到并拯救他们我所经历的悲伤。

标签: android colors textview


【解决方案1】:

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

【讨论】:

  • 这应该是最好的解决方案。太棒了!谢谢
  • 这不会产生轮廓,因为它只显示在两侧。
  • 非常适合我!!
  • 这个阴影对于轮廓很弱
【解决方案2】:

所以,有点晚了,但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 更多的内容。 这是边缘垃圾邮件,但在主题上,也许可以接受?

【讨论】:

  • 您好,我们如何在 EditText 中输入的文本上添加这样的边框?
  • 对 EditText 有什么想法吗?
  • dreamText.setStroke(4, Color.BLACK); dreamText.setTextColor(Color.WHITE);我正在使用这些设置,但我的文本颜色是透明的,我可以看到黑色轮廓。怎么了?
  • 没关系,但它并没有真正添加边框。而是采用文本并使用外部边缘作为边界,这不会产生相同的视觉效果。
  • 此解决方案导致onDraw 以递归方式被调用,因为在onDraw 内部调用setTextColor
【解决方案3】:

您可以在文本后面加上阴影,这通常有助于提高可读性。尝试在绿色文本上使用 50% 的半透明黑色阴影。有关如何执行此操作的详细信息,请参见此处:Android - shadow on text?

要真正在文本周围添加笔触,您需要做一些更复杂的事情,如下所示: How do you draw text with a border on a MapView in Android?

【讨论】:

  • 请注意有一个bug with strokes in Android 4.4。如果文本大小超过 256 像素,则会导致非常奇怪的笔画渲染。一种解决方法是使用替代方法presented in this answer 绘制轮廓/笔划。
  • 这个注释是指文本视图还是字体大小?
  • 阴影不够好,白色背景布局上的白色文本与黑色阴影看起来仍然很糟糕
【解决方案4】:

这是一个相当古老的问题,但我仍然没有看到任何完整的答案。因此,我发布了此解决方案,希望遇到此问题的人可能会发现它很有用。最简单有效的解决方案是重写 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" />

记得用你项目的包名替换包名。还要在布局文件中添加 xmlns 命名空间,以便使用自定义 XML 属性。您可以在布局文件的根节点中添加以下行。

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

【讨论】:

  • 多么出色、优雅的解决方案!我实现了这个并且效果很好。我只是将 textStrokeWidth 更改为一个维度(和 a.getDimensionPixelSize)。谢谢!
  • 效果很好,谢谢。当他们的大纲占据了整个文本时,我改变了我的顺序:首先绘制大纲,然后是文本。
  • 效果很好。不要使用 Android Studio 设计视图来测试轮廓,表示不够准确。刚刚花了 2 小时调试一个非问题。
  • 这个方案会导致onDraw无限次,因为setTextColor调用无效。
  • 其实@Guliash 是正确的。经测试,一旦调用此方法,由于invalidate() 调用埋在setTextColor 的内部工作原理中,会导致调用自身的无限循环。除非您想将 TextView 中的每一行代码复制到您自己的类中,否则我能看到的唯一解决方法是暴力访问 mCurTextColorprivate @987654330 字段@ 使用反射。请参阅this answer 大致了解如何操作。只需使用field.set(this, colorInt) 而不是使用field.get()
【解决方案5】:

框架支持文本阴影但不支持文本轮廓。但是有一个技巧:阴影是半透明的并且会褪色的东西。重绘阴影几次,所有的 alpha 都被总结出来,结果就是一个轮廓。

一个非常简单的实现扩展了TextView 并覆盖了draw(..) 方法。每次请求抽签时,我们的子类都会绘制 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" />

【讨论】:

  • 非常感谢。但是我宁愿使用这种方法:'@Override protected void onDraw(Canvas canvas) { for (int i = 0; i
  • 附加信息:必须至少实现具有 Context 和 AttributeSet 的 ctor。否则你会碰到。 java.lang.NoSuchMethodException: &lt;init&gt; [class android.content.Context, interface android.util.AttributeSet
【解决方案6】:

我一直在尝试弄清楚如何做到这一点,但在网上找不到好的指南,但最终想通了。正如 Steve Pomeroy 所建议的那样,您确实必须做一些更多的事情。为了获得轮廓文本效果,您绘制了两次文本:一次使用粗轮廓,然后第二次我们在轮廓上绘制主要文本。但是,任务变得更容易了,因为您可以非常轻松地调整随 SDK 提供的代码示例之一,即您的 SDK 目录中此名称下的代码示例:“/samples/android-/ApiDemos/src/com/example/android /apis/view/LabelView.java”。也可以在 Android 开发者网站here 上找到。

根据您正在做的事情,很容易看出您只需要对该代码进行少量修改,例如将其更改为从 TextView 扩展等。在我发现此示例之前,我忘记覆盖 onMeasure( )(除了在 Android 开发者网站上的“构建自定义组件”指南中提到的覆盖 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);
   }

因此,为了获得轮廓文本效果,您需要绘制两次文本:一次使用粗轮廓,然后第二次我们在轮廓上绘制主要文本。

【讨论】:

    【解决方案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" />
    

    【讨论】:

    • 它适用于 textview,我们如何为 edittext 做呢?
    • onDraw() 调用super.setTextColor() 将导致视图失效,这将导致再次调用onDraw()。这将是一个无限循环。
    【解决方案8】:

    这是我发现比 MagicTextView 的 stroke 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);
    }
    

    【讨论】:

    • 我看到 TextView 的右侧正在被裁剪 - 并且轮廓没有完全绘制在该侧......好像它用完了空间
    • 还有一件事。我怀疑 setTextColor 正在强制重绘 - 这会导致这个 onDraw 的无限循环被一遍又一遍地调用。建议在测试时在此方法中放置 logcat 或其他指标。
    • @RoundSparrowhilltx 是正确的。正如我在对另一个类似答案的评论中提到的那样,我怀疑将整个 TextView 复制并粘贴到您自己的类中的唯一方法是使用反射直接访问 private @ TextView 中的 987654324@ 字段。 This answer 提供了如何执行此操作的一般指南。如果您希望提示和链接文本也有笔划,您还必须更改mHintTextColormLinkTextColor。不幸的是,更改 mTextColor 没有任何作用,因为它只是被引用。
    • in 将永远循环 onDraw
    【解决方案9】:

    我编写了一个类来执行带有轮廓的文本,并且仍然支持所有其他属性和普通文本视图的绘制。

    它基本上使用TextView上的super.onDraw(Canves canvas),但使用不同的样式绘制了两次。

    希望这会有所帮助。

    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() 丢失
    【解决方案10】:

    您可以使用以下 sn-p 以编程方式执行此操作。 这提供了黑色背景的白色字母:

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

    方法的参数是radius,dx,dy,color。您可以根据自己的特定需求进行更改。

    我希望我能帮助那些以编程方式创建 TextView 而不是在 xml 中的人。

    为 stackOverflow 社区干杯!

    【讨论】:

      【解决方案11】:

      我想添加一个解决方案来解决性能问题。例如,@YGHM 和其他一些人的答案可以完成这项工作,但它会导致onDraw 的无限调用,因为setTextColor 调用invalidate()。因此,为了解决它,您还需要覆盖invalidate() 并添加一个变量isDrawing,当onDraw() 正在进行并用笔画绘制时,您将设置为true。如果变量是true,invalidate 将返回。

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

      【讨论】:

      • 为什么点赞这么少?对于所有这些“无限循环”和“隐藏的 api 反射”问题,这是一个非常好的解决方案!
      【解决方案12】:

      我创建了一个基于Nouman Hanif's answer 的库,并添加了一些内容。例如,修复导致 View.invalidate() 调用出现间接无限循环的错误。

      OTOH,该库还支持 EditText 小部件中的轮廓文本,因为这是我的真正目标,它需要比 TextView 更多的工作。

      这是我的图书馆的链接:https://github.com/biomorgoth/android-outline-textview

      感谢 Nouman Hanif 提出解决方案的初步想法!

      【讨论】:

        【解决方案13】:

        我找到了无需从 TextView 继承的简单方法来概述视图。 我编写了简单的库,使用 Android 的 Spannable 来概述文本。 此解决方案提供了仅勾勒部分文本的可能性。

        我已经回答过同样的问题 (answer)

        类:

        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

        【讨论】:

        • 不支持多行文字
        【解决方案14】:

        MagicTextView 对于制作笔画字体非常有用,但在我的情况下,它会导致错误,例如 this 此错误是由 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));
        }
        }
        

        【讨论】:

          【解决方案15】:

          所以你想在文本视图周围划一下?不幸的是,没有简单的方法来使用样式。您必须创建另一个视图并将您的 textview 置于顶部,使父视图(它位于其顶部的视图)仅大几个像素 - 这应该会创建一个轮廓。

          【讨论】:

          • 嗯,这听起来更像是一种痛苦而不是它的价值。我只关心在白色背景上可读的绿色文本(现在有点难以阅读)img88.imageshack.us/i/devicez.png红色看起来不错。也许如果我只是换成深绿色,但我真的希望我能得到某种轮廓或其他东西
          • 那你是想勾勒出文本本身吗?这仍然不可能,除非您执行自己的自定义 TextView,但这可能比它的价值更多。把它变成深绿色可能更容易。
          • 红/绿色盲者的一个小要求:请考虑添加相同红/绿信息的替代表示,因为我们通常很难看到深绿色和深红色。也许还有一个向上/向下箭头?
          • 史蒂夫说得好。我以后可能会添加它。
          【解决方案16】:

          这是我可以通过扩展 TextView 找到的最简单的方法

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

          那么你只需要在 attrs.xml 文件中添加以下内容

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

          现在您将能够通过app:stroke 设置笔划宽度,同时保留TextView 的所有其他所需属性。我的解决方案只绘制没有填充的笔触。这使它比其他的更简单。在为我的 customtextview 设置自定义字体时,在屏幕截图中显示结果。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-03-21
            • 2014-08-09
            • 1970-01-01
            • 1970-01-01
            • 2021-08-20
            • 2020-11-25
            相关资源
            最近更新 更多