TextView
,我想添加一个阴影.应该看起来像
OsmAnd(100%不透明):
但它看起来像这样:
你可以看到当前的阴影模糊不清了.我想要一个坚实,不透明的阴影.但是怎么样
我当前的代码是:
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/speedTextView" android:text="25 km/h" android:textSize="24sp" android:textStyle="bold" android:textColor="#000000" android:shadowColor="#ffffff" android:shadowDx="0" android:shadowDy="0" android:shadowRadius="6" />
解决方法
使用它,您一次只需处理一个视图,因此在运行时更改某些内容不需要在两个单独的TextView上进行调用.这也使得更容易利用TextView的其他优点 – 像复合绘图 – 并保持一切正方形,无需冗余设置.
反射用于避免调用TextView的setTextColor()方法,这会使视图无效,并导致无限的绘制循环,我相信这很可能为什么solutions like this不适合您.由于TextView在其onDraw()方法中处理它的方式,因此在Paint对象上直接设置颜色不起作用,因此反射.
import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; import android.view.View.BaseSavedState; import android.widget.TextView; import java.lang.reflect.Field; public class OutlineTextView extends TextView { private Field colorField; private int textColor; private int outlineColor; public OutlineTextView(Context context) { this(context,null); } public OutlineTextView(Context context,AttributeSet attrs) { this(context,attrs,android.R.attr.textViewStyle); } public OutlineTextView(Context context,AttributeSet attrs,int defStyleAttr) { super(context,defStyleAttr); try { colorField = TextView.class.getDeclaredField("mCurTextColor"); colorField.setAccessible(true); // If the reflection fails (which really shouldn't happen),we // won't need the rest of this stuff,so we keep it in the try-catch textColor = getTextColors().getDefaultColor(); // These can be changed to hard-coded default // values if you don't need to use XML attributes TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.OutlineTextView); outlineColor = a.getColor(R.styleable.OutlineTextView_outlineColor,Color.TRANSPARENT); setOutlineStrokeWidth(a.getDimensionPixelSize(R.styleable.OutlineTextView_outlineWidth,0)); a.recycle(); } catch (NoSuchFieldException e) { // Optionally catch Exception and remove print after testing e.printStackTrace(); colorField = null; } } @Override public void setTextColor(int color) { // We want to track this ourselves // The super call will invalidate() textColor = color; super.setTextColor(color); } public void setOutlineColor(int color) { outlineColor = color; invalidate(); } public void setOutlineWidth(float width) { setOutlineStrokeWidth(width); invalidate(); } private void setOutlineStrokeWidth(float width) { getPaint().setStrokeWidth(2 * width + 1); } @Override protected void onDraw(Canvas canvas) { // If we couldn't get the Field,then we // need to skip this,and just draw as usual if (colorField != null) { // Outline setColorField(outlineColor); getPaint().setStyle(Paint.Style.STROKE); super.onDraw(canvas); // Reset for text setColorField(textColor); getPaint().setStyle(Paint.Style.FILL); } super.onDraw(canvas); } private void setColorField(int color) { // We did the null check in onDraw() try { colorField.setInt(this,color); } catch (IllegalAccessException | IllegalArgumentException e) { // Optionally catch Exception and remove print after testing e.printStackTrace(); } } // Optional saved state stuff @Override public Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); SavedState ss = new SavedState(superState); ss.textColor = textColor; ss.outlineColor = outlineColor; ss.outlineWidth = getPaint().getStrokeWidth(); return ss; } @Override public void onRestoreInstanceState(Parcelable state) { SavedState ss = (SavedState) state; super.onRestoreInstanceState(ss.getSuperState()); textColor = ss.textColor; outlineColor = ss.outlineColor; getPaint().setStrokeWidth(ss.outlineWidth); } private static class SavedState extends BaseSavedState { int textColor; int outlineColor; float outlineWidth; SavedState(Parcelable superState) { super(superState); } private SavedState(Parcel in) { super(in); textColor = in.readInt(); outlineColor = in.readInt(); outlineWidth = in.readFloat(); } @Override public void writeToParcel(Parcel out,int flags) { super.writeToParcel(out,flags); out.writeInt(textColor); out.writeInt(outlineColor); out.writeFloat(outlineWidth); } public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() { public SavedState createFromParcel(Parcel in) { return new SavedState(in); } public SavedState[] newArray(int size) { return new SavedState[size]; } }; } }
如果使用自定义XML属性,则需要在您的< resources>中进行以下操作,您可以通过将此文件粘贴到res / values /文件夹中,或者添加到已经存在的文件中.如果您不希望使用自定义属性,则应从View的第三个构造函数中删除相关的属性处理.
attrs.xml
<resources> <declare-styleable name="OutlineTextView" > <attr name="outlineColor" format="color" /> <attr name="outlineWidth" format="dimension" /> </declare-styleable> </resources>
使用自定义属性,可以在布局XML中设置所有内容.注意附加的XML命名空间,这里命名为app,并在根LinearLayout元素上指定.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#445566"> <com.example.testapp.OutlineTextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="123 ABC" android:textSize="36sp" android:textColor="#000000" app:outlineColor="#ffffff" app:outlineWidth="2px" /> </LinearLayout>
结果:
笔记:
>目前我有一个非常有限的测试台,但是通过源代码版本,我认为这应该从API 8(Froyo)一直运行起来,至少通过API 23(棉花糖).>如果与文本大小相比,轮廓宽度相对较大,则可能需要在“视图”上设置附加填充,以使其保持在其范围内,特别是如果包围宽度和/或高度.这也是覆盖的TextViews的关注点.>相对较大的外形宽度也可能导致对某些字符(如“A”和“2”)的不利影响,因为笔触风格.覆盖的TextView也会发生这种情况.>只是为了好玩:我会指出,您可以使用半透明的颜色为文本和/或轮廓,并使用填充/笔触/填充和笔触样式使用一些漂亮的漂亮效果.当然,也可以使用叠加的TextViews解决方案.