代码之家  ›  专栏  ›  技术社区  ›  Falmarri

Android文本视图大纲文本

  •  68
  • Falmarri  · 技术社区  · 14 年前

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

    15 回复  |  直到 5 年前
        1
  •  49
  •   Community Reversed Engineer    7 年前

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

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

        2
  •  49
  •   ABentSpoon Ungue    12 年前

    所以,有点晚了,但是 magictextview will do text outlines,between other things.

    <com.qwerjk.better_text.magicTextView
    xmlns:qwerjk=“http://schemas.android.com/apk/res/com.qwerjk.better_文本”
    安卓:textsize=“78dp”
    android:textcolor=“ff333333”
    android:layout_width=“填充父级”
    android:layout_height=“包装内容”
    qwerjk:strokeColor=“ffff0000”
    qwerjk:strokeJoinStyle=“斜接”
    qwerjk:strokewidth=“5”
    安卓:text=“magic”/>
    < /代码> 
    
    

    注意:我这样做了,为了未来的旅行者,我发布的信息比OP的更多。 这是边缘垃圾邮件,但在主题上,也许可以接受?

    将做文本轮廓,以及其他事情。

    enter image description here

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

        3
  •  48
  •   Rafał    9 年前

    使用文本视图中的阴影可以实现轮廓效果:

        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"
    
        4
  •  20
  •   Zsolt Safrany    11 年前

    框架支持文本阴影,但不支持文本轮廓。但有一个诀窍:阴影是半透明的,并且会褪色。重新绘制阴影几次,所有的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" />
    
        5
  •  15
  •   Nouman Hanif    9 年前

    这是一个很古老的问题,但我仍然看不到任何完整的答案。所以我发布了这个解决方案,希望在这个问题上挣扎的人能发现它的用处。最简单和最有效的解决方案是重写textView类的OnDraw方法。我见过的大多数实现都使用draw text方法来绘制笔画,但这种方法并不能解释所有的格式对齐和文本换行。因此,笔画和文字通常会出现在不同的地方。下面的方法使用super.ondraw来绘制笔画和填充部分文本,这样您就不必为其他内容操心了。步骤如下

    1. 扩展TextView类
    2. 重写OnDraw方法
    3. 将“绘制样式”设置为“填充”
    4. 在绘图时调用父类以呈现填充文本 模式。
    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"
    
        6
  •  14
  •   sversch    13 年前

    我只是想知道怎么做,在网上找不到一个好的指南,但最终找到了。正如史蒂夫波默罗伊所建议的,你必须做更多的事情。为了获得轮廓文本效果,您需要绘制两次文本:一次绘制粗轮廓,然后第二次绘制主文本。但是,任务变得更容易,因为您可以非常容易地适应SDK提供的一个代码示例,即在SDK目录下的这个名称:“/Stase/Android/ApiDemos /SRC/COM/示例/ Android / APIs/VIEW/LabelVIEW。Java”。也可以在Android开发者网站上找到。 here .

    根据您所做的,很容易看到您只需要对该代码做一些小的修改,例如将其更改为从textView扩展等。在发现此示例之前,我忘记重写onMeasure()(除了重写OnDraw()之外,您还必须这样做,如在上的“Building Custom Components”指南中所述Android开发者网站),这也是我遇到麻烦的原因之一。

    一旦你做到了,你就可以做我做的:

    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
  •  6
  •   VinZen    10 年前

    这是我发现的比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);
    }
    
        8
  •  6
  •   YGHM    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);
        }
    
    }
    

    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>
    
        9
  •  2
  •   Farmaker    6 年前

    您可以使用下面的代码片段以编程方式完成此操作。 提供黑色背景的白色字母:

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

    该方法的参数为半径、dx、dy、颜色。您可以根据您的具体需要更改它们。

    我希望我能帮助那些以编程方式创建文本视图而不将其包含在XML中的人。

    为StackOverflow社区干杯!

        10
  •  0
  •   xil3    14 年前

    所以你想在文本视图周围画一个线条?不幸的是,没有一个简单的方法来做造型。你必须创建另一个视图,并将你的文本视图放在上面,使父视图(它在上面的那个)只大几个像素-这应该创建一个轮廓。

        11
  •  0
  •   kazuwombat    8 年前

    magicTextView对于创建笔划字体非常有用,但在我的例子中,它会导致错误,例如 this 此错误是由magicTextView设置的重复背景属性引起的

    所以您需要编辑attrs.xml和magicTextView.java

    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));
    }
    }
    
        12
  •  0
  •   Community Reversed Engineer    7 年前

    我创建了一个基于 Nouman Hanif's answer 还有一些补充。例如,修复在view.invalidate()调用上导致间接无限循环的错误。

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

    以下是指向我的库的链接: https://github.com/biomorgoth/android-outline-textview

    感谢努曼哈尼夫对解决方案的初步想法!

        13
  •  0
  •   Pavel Santaev    6 年前

    我找到了一种简单的方法来勾勒出没有继承自 文本框 . 我写了一个简单的使用安卓系统的库 可纺的 用于概述文本。 此解决方案允许只概述部分文本。

    我已经回答了同样的问题( 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
  •  0
  •   Sermilion    5 年前

    我想添加一个解决方案来解决性能问题。例如,@yghm和其他一些人的答案可以完成这个任务,但是它会导致无限的调用 onDraw 因为 setTextColor 电话 invalidate() . 所以为了解决这个问题,您还需要重写 无效() 并添加变量 isDrawing 你将设置为 true onDraw() 正在进行中,正在用笔画。如果变量为 .

    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)
        }
      }
    
        15
  •  0
  •   Arthur    5 年前

    归功于@yghm添加影子支持

    package com.megvii.demo;
    导入android.content.context;
    导入android.content.res.typedarray;
    导入android.graphics.canvas;
    导入android.graphics.color;
    导入android.graphics.paint;
    导入android.util.attributeset;
    
    公共类textviewOutline扩展android.support.v7.widget.appcompattextview{
    
    / /常量
    private static final int默认_outline_size=0;
    private static final int默认_outline_color=color.transparent;
    
    /数据
    私人内部口碑大小;
    私密的内部色调;
    private int mtextcolor;
    私有浮动mshadowradius;
    私有浮动mshadowdx;
    私人浮筒;mshadowdy;
    private int mshadowcolor;
    
    公共文本视图大纲(上下文上下文){
    此(上下文,空);
    }
    
    public textviewoutline(上下文上下文,attributeset attrs){
    super(上下文、属性);
    设置属性(attrs);
    }
    
    私有void setattributes(attributeset attrs){
    /设置默认值
    moutlinesize=默认的_outline_大小;
    moutlinecolor=默认_outline_color;
    //文本颜色
    mtextcolor=getcurrentTextcolor();
    如果!{NULL){
    typedarray a=getContext().obtainStyledAttributes(attrs,r.Styleable.textViewOutline);
    /轮廓尺寸
    if(a.hasValue(r.Styleable.TextViewOutline_OutlineSize))。{
    moutlinesize=(int)a.getDimension(r.styleable.textViewOutline_outlineSize,默认_outline_size);
    }
    //大纲颜色
    if(a.hasValue(r.Styleable.TextViewOutline_OutlineColor))。{
    moutlinecolor=A.getcolor(r.styleable.textviewoutline_outline color,默认_outline_color);
    }
    //shadow(之所以从属性获取shadow,是因为我们使用了API级别15,而只有16个属性才有shadow属性的get方法)
    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.
    }
    
    }
    
    @重写
    保护无效度量(int widthmeasurespec,int heightmeasurespec){
    setpaintoOutline();
    super.onmeasure(宽度测量规范,高度测量规范);
    }
    
    private void setpaintoOutline()。{
    paint paint=getpaint();
    paint.setStyle(paint.style.stroke);
    油漆。设置行程宽度(口型尺寸);
    super.settextcolor(口红);
    super.setshadowlayer(0,0,0,color.transparent);
    
    }
    
    private void setpaintoregular()。{
    paint paint=getpaint();
    paint.setStyle(paint.style.fill);
    油漆。设置行程宽度(0);
    super.settextcolor(mtextcolor);
    super.setshadowlayer(mshadowradius、mshadowdx、mshadowdy、mshadowcolor);
    }
    
    
    @重写
    公共void settextcolor(int color){
    super.settextcolor(颜色);
    mtextcolor=颜色;
    }
    
    
    public void setoutlinesize(int大小){
    moutlinesize=尺寸;
    }
    
    公共void setoutlinecolor(int color){
    moutlinecolor=颜色;
    }
    
    @重写
    保护性空隙(帆布){
    setpaintoOutline();
    super.ondraw(画布);
    
    setpaintoregular();
    super.ondraw(画布);
    }
    
    }
    < /代码> 
    
    

    ATTR定义

    <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/产品名称”
    android:layout_width=“包装内容”
    android:layout_height=“包装内容”
    android:layout_gravity=“center_horizontal”
    android:layout_margintop=“110dp”
    android:background=“f4b222”
    android:fontfamily=“@FONT/kidsmagazine”
    安卓:padding=“10dp”
    android:shadowcolor=“D7713200”
    安卓:shadowdx=“0”
    安卓:shadowdy=“8”
    安卓:shadowRadius=“1”
    Android:文本=“口红设置”
    android:textcolor=“@android:color/white”
    安卓:textsize=“30sp”
    app:outlinecolor=“cb7800”
    app:outlinesize=“3dp”/>
    < /代码> 
    

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