外发光、内发光、描边、阴影的TextView
取自https://github.com/m5/MagicTextView,感谢m5
设置一个attrs.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="MagicTextView"> <attr name="innerShadowColor" format="color"/> <attr name="innerShadowRadius" format="float"/> <attr name="innerShadowDx" format="float"/> <attr name="innerShadowDy" format="float"/> <attr name="outerShadowColor" format="color"/> <attr name="outerShadowRadius" format="float"/> <attr name="outerShadowDx" format="float"/> <attr name="outerShadowDy" format="float"/> <attr name="typeface" format="string" /> <attr name="foreground" format="reference|color"/> <attr name="background" format="reference|color"/> <attr name="strokeWidth" format="float" /> <attr name="strokeMiter" format="float" /> <attr name="strokeColor" format="color" /> <attr name="strokeJoinStyle"> <enum name="miter" value="0" /> <enum name="bevel" value="1" /> <enum name="round" value="2" /> </attr> </declare-styleable> </resources>
然后自定义我们的TextView类:
import java.util.ArrayList; import java.util.WeakHashMap; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.BlurMaskFilter; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Join; import android.graphics.Paint.Style; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.Typeface; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.text.TextPaint; import android.util.AttributeSet; import android.util.Pair; import android.widget.TextView; import com.carcon.navi.uitools.ScreenMeasure; import com.carcon.ui.R; public class MagicTextView extends TextView { private ArrayList<Shadow> outerShadows; private ArrayList<Shadow> innerShadows; private WeakHashMap<String, Pair<Canvas, Bitmap>> canvasStore; private Canvas tempCanvas; private Bitmap tempBitmap; private Drawable foregroundDrawable; private float strokeWidth; private Integer strokeColor; private Join strokeJoin; private float strokeMiter; private int[] lockedCompoundPadding; private boolean frozen = false; public MagicTextView(Context context) { super(context); init(null); } public MagicTextView(Context context, AttributeSet attrs) { super(context, attrs); init(attrs); } public MagicTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(attrs); } public void init(AttributeSet attrs){ outerShadows = new ArrayList<Shadow>(); innerShadows = new ArrayList<Shadow>(); if(canvasStore == null){ canvasStore = new WeakHashMap<String, Pair<Canvas, Bitmap>>(); } if(attrs != null){ TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.MagicTextView); String typefaceName = a.getString( R.styleable.MagicTextView_typeface); if(typefaceName != null) { Typeface tf = Typeface.createFromAsset(getContext().getAssets(), String.format("fonts/%s.ttf", typefaceName)); setTypeface(tf); } if(a.hasValue(R.styleable.MagicTextView_foreground)){ Drawable foreground = a.getDrawable(R.styleable.MagicTextView_foreground); if(foreground != null){ this.setForegroundDrawable(foreground); }else{ this.setTextColor(a.getColor(R.styleable.MagicTextView_foreground, 0xff000000)); } } if(a.hasValue(R.styleable.MagicTextView_background)){ Drawable background = a.getDrawable(R.styleable.MagicTextView_background); if(background != null){ this.setBackgroundDrawable(background); }else{ this.setBackgroundColor(a.getColor(R.styleable.MagicTextView_background, 0xff000000)); } } if(a.hasValue(R.styleable.MagicTextView_innerShadowColor)){ this.addInnerShadow(a.getFloat(R.styleable.MagicTextView_innerShadowRadius, 0), a.getFloat(R.styleable.MagicTextView_innerShadowDx, 0), a.getFloat(R.styleable.MagicTextView_innerShadowDy, 0), a.getColor(R.styleable.MagicTextView_innerShadowColor, 0xff000000)); } if(a.hasValue(R.styleable.MagicTextView_outerShadowColor)){ this.addOuterShadow(a.getFloat(R.styleable.MagicTextView_outerShadowRadius, 0), a.getFloat(R.styleable.MagicTextView_outerShadowDx, 0), a.getFloat(R.styleable.MagicTextView_outerShadowDy, 0), a.getColor(R.styleable.MagicTextView_outerShadowColor, 0xff000000)); } if(a.hasValue(R.styleable.MagicTextView_strokeColor)){ float strokeWidth = a.getFloat(R.styleable.MagicTextView_strokeWidth, 1); int strokeColor = a.getColor(R.styleable.MagicTextView_strokeColor, 0xff000000); float strokeMiter = a.getFloat(R.styleable.MagicTextView_strokeMiter, 10); Join strokeJoin = null; switch(a.getInt(R.styleable.MagicTextView_strokeJoinStyle, 0)){ case(0): strokeJoin = Join.MITER; break; case(1): strokeJoin = Join.BEVEL; break; case(2): strokeJoin = Join.ROUND; break; } this.setStroke(strokeWidth, strokeColor, strokeJoin, strokeMiter); } } } public void setStroke(float width, int color, Join join, float miter){ strokeWidth = width; strokeColor = color; strokeJoin = join; strokeMiter = miter; } public void setStroke(float width, int color){ setStroke(width, color, Join.MITER, 10); } public void addOuterShadow(float r, float dx, float dy, int color){ if(r == 0){ r = 0.0001f; } outerShadows.add(new Shadow(r,dx,dy,color)); } public void addInnerShadow(float r, float dx, float dy, int color){ if(r == 0){ r = 0.0001f; } innerShadows.add(new Shadow(r,dx,dy,color)); } public void clearInnerShadows(){ innerShadows.clear(); } public void clearOuterShadows(){ outerShadows.clear(); } public void setForegroundDrawable(Drawable d){ this.foregroundDrawable = d; } public Drawable getForeground(){ return this.foregroundDrawable == null ? this.foregroundDrawable : new ColorDrawable(this.getCurrentTextColor()); } @Override public void onDraw(Canvas canvas){ super.onDraw(canvas); freeze(); Drawable restoreBackground = this.getBackground(); Drawable[] restoreDrawables = this.getCompoundDrawables(); int restoreColor = this.getCurrentTextColor(); this.setCompoundDrawables(null, null, null, null); for(Shadow shadow : outerShadows){ this.setShadowLayer(shadow.r, shadow.dx, shadow.dy, shadow.color); super.onDraw(canvas); } this.setShadowLayer(0,0,0,0); this.setTextColor(restoreColor); if(this.foregroundDrawable != null && this.foregroundDrawable instanceof BitmapDrawable){ generateTempCanvas(); super.onDraw(tempCanvas); Paint paint = ((BitmapDrawable) this.foregroundDrawable).getPaint(); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP)); this.foregroundDrawable.setBounds(canvas.getClipBounds()); this.foregroundDrawable.draw(tempCanvas); canvas.drawBitmap(tempBitmap, 0, 0, null); tempCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); } if(strokeColor != null){ TextPaint paint = this.getPaint(); // paint.setTextAlign(Paint.Align.CENTER); paint.setStyle(Style.STROKE); paint.setStrokeJoin(strokeJoin); paint.setStrokeMiter(strokeMiter); this.setTextColor(strokeColor); paint.setStrokeWidth(strokeWidth); super.onDraw(canvas); paint.setStyle(Style.FILL); this.setTextColor(restoreColor); } if(innerShadows.size() > 0){ generateTempCanvas(); TextPaint paint = this.getPaint(); for(Shadow shadow : innerShadows){ this.setTextColor(shadow.color); super.onDraw(tempCanvas); this.setTextColor(0xFF000000); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); paint.setMaskFilter(new BlurMaskFilter(shadow.r, BlurMaskFilter.Blur.NORMAL)); tempCanvas.save(); tempCanvas.translate(shadow.dx, shadow.dy); super.onDraw(tempCanvas); tempCanvas.restore(); canvas.drawBitmap(tempBitmap, 0, 0, null); tempCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); paint.setXfermode(null); paint.setMaskFilter(null); this.setTextColor(restoreColor); this.setShadowLayer(0,0,0,0); } } if(restoreDrawables != null){ this.setCompoundDrawablesWithIntrinsicBounds(restoreDrawables[0], restoreDrawables[1], restoreDrawables[2], restoreDrawables[3]); } this.setBackgroundDrawable(restoreBackground); this.setTextColor(restoreColor); unfreeze(); } private void generateTempCanvas(){ String key = String.format("%dx%d", getWidth(), getHeight()); Pair<Canvas, Bitmap> stored = canvasStore.get(key); if(stored != null){ tempCanvas = stored.first; tempBitmap = stored.second; }else{ tempCanvas = new Canvas(); tempBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); tempCanvas.setBitmap(tempBitmap); canvasStore.put(key, new Pair<Canvas, Bitmap>(tempCanvas, tempBitmap)); } } // Keep these things locked while onDraw in processing public void freeze(){ lockedCompoundPadding = new int[]{ getCompoundPaddingLeft(), getCompoundPaddingRight(), getCompoundPaddingTop(), getCompoundPaddingBottom() }; frozen = true; } public void unfreeze(){ frozen = false; } @Override public void requestLayout(){ if(!frozen) super.requestLayout(); } @Override public void postInvalidate(){ if(!frozen) super.postInvalidate(); } @Override public void postInvalidate(int left, int top, int right, int bottom){ if(!frozen) super.postInvalidate(left, top, right, bottom); } @Override public void invalidate(){ if(!frozen) super.invalidate(); } @Override public void invalidate(Rect rect){ if(!frozen) super.invalidate(rect); } @Override public void invalidate(int l, int t, int r, int b){ if(!frozen) super.invalidate(l,t,r,b); } @Override public int getCompoundPaddingLeft(){ return !frozen ? super.getCompoundPaddingLeft() : lockedCompoundPadding[0]; } @Override public int getCompoundPaddingRight(){ return !frozen ? super.getCompoundPaddingRight() : lockedCompoundPadding[1]; } @Override public int getCompoundPaddingTop(){ return !frozen ? super.getCompoundPaddingTop() : lockedCompoundPadding[2]; } @Override public int getCompoundPaddingBottom(){ return !frozen ? super.getCompoundPaddingBottom() : lockedCompoundPadding[3]; } public static class Shadow{ float r; float dx; float dy; int color; public Shadow(float r, float dx, float dy, int color){ this.r = r; this.dx = dx; this.dy = dy; this.color = color; } } }这类中的描边是内描边,如果要外描边的话
在onDraw里面画完描边字体以后加上
paint.setStrokeWidth(0); super.onDraw(canvas);
就ok了
调用方发如下:
xml:
<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" android:drawableLeft="@android:drawable/btn_star" android:textStyle="bold" android:padding="10dp" qwerjk:foreground="@drawable/fake_luxury_tiled" qwerjk:innerShadowDy="2" qwerjk:innerShadowColor="#FF000000" qwerjk:innerShadowRadius="1" qwerjk:outerShadowDy="3" qwerjk:outerShadowColor="#FF0088ff" qwerjk:outerShadowRadius="10" qwerjk:strokeColor="#FFff0000" qwerjk:strokeJoinStyle="miter" qwerjk:strokeWidth="5" android:text="Sample" />
java:
view = new MagicTextView(context); view.addInnerShadow(0, -1, 0, 0xFFffffff); view.addOuterShadow(0, -1, 0, 0xff000000); view.setStroke(4, 0xFFff0000); view.setForegroundDrawable(getResources().getDrawable(R.drawable.fake_luxury_tiled);
相关推荐
Unity HDRP 边缘发光 描边
本篇文章主要介绍了Android为TextView添加字体库和设置描边的方法,具有一定的参考价值,有兴趣的可以了解一下
UnityShader 外发光,自定义3d模型发效果,譬如模型被攻击时变色等
要求Unity 5.3.4 或更高版本...只需简单地更改材质属性,加入一些文本样式,例如放大、描边、软阴影、倾斜、纹理及发光特效等等,即可动态改变文本的显示效果,还可以通过创建材质预设来保存这些文本样式以便后续重用。
unity 2DSprite 边缘发光特效,制作unity 2D游戏很实用!很牛逼的发光效果,sprite 发光效果,点灯元
重写CCLabelTTF类,实现阴影和描边
可以自定义的描边 各种颜色选择粗细等
qt中qlabel设置字体阴影的方法,带完整的测试工程,可直接编译运行
UE4后处理材质资源,描边高亮效果。UE4后处理材质资源,描边高亮效果。
unity3d中实现物体轮廓描边的Shader
在unity中实现物体描边的效果,亲测可用。
描边资源
描边shader
通过有描边的文本和没描边的文本重叠的形式,并且两个文本为不同颜色则可达到想要的描边效果
易语言GDI文字描边源码,GDI文字描边
dw文字描边效果 span { position:absolute; padding:4px; filter: Dropshadow(offx=1,offy=0,color=white) Dropshadow(offx=0,offy=1,color=white) Dropshadow(offx=0,offy=-1,color=white) Dropshadow(offx=-1,...
物体描边高亮是游戏中常见的操作,该文档包含了两类物体描边高亮的方法,都是通过用材质进行解决,效果图可看我之前发的博客。跟着操作就能实现,仅用于学习。
一个Unity3d高亮描边插件,可以直接使用,类似游戏中人物被选中时高亮描边效果
unity 描边Shaderunity 描边Shaderunity 描边Shaderunity 描边Shaderunity 描边Shaderunity 描边Shader