自定义View之文字图形图片的阴影
Posted LQS_Android
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自定义View之文字图形图片的阴影相关的知识,希望对你有一定的参考价值。
一、setShadowLayer与阴影效果
Android 开发了一个添加阴影效果的函数 setShadowLayer(), 它可以为文字、图形、图片添加阴影效果。
setShadowLayer()函数能够实现如下效果:
- 定制阴影模糊程度,通过模糊半径控制模糊与清晰的程度。
- 定制影偏移距离,X轴方向可以向左偏移或者向右偏移,Y轴方向可以向上偏移或向下偏移。
- 清除和显示阴影
/**
* This draws a shadow layer below the main layer, with the specified
* offset and color, and blur radius. If radius is 0, then the shadow
* layer is removed.
* <p>
* Can be used to create a blurred shadow underneath text. Support for use
* with other drawing operations is constrained to the software rendering
* pipeline.
* <p>
* The alpha of the shadow will be the paint's alpha if the shadow color is
* opaque, or the alpha from the shadow color if not.
*/
public void setShadowLayer(float radius, float dx, float dy, @ColorInt int shadowColor) {
setShadowLayer(radius, dx, dy, Color.pack(shadowColor));
}
/**
* This draws a shadow layer below the main layer, with the specified
* offset and color, and blur radius. If radius is 0, then the shadow
* layer is removed.
* <p>
* Can be used to create a blurred shadow underneath text. Support for use
* with other drawing operations is constrained to the software rendering
* pipeline.
* <p>
* The alpha of the shadow will be the paint's alpha if the shadow color is
* opaque, or the alpha from the shadow color if not.
*
* @throws IllegalArgumentException if the color space encoded in the
* {@code ColorLong} is invalid or unknown.
*/
public void setShadowLayer(float radius, float dx, float dy, @ColorLong long shadowColor) {
ColorSpace cs = Color.colorSpace(shadowColor);
nSetShadowLayer(mNativePaint, radius, dx, dy, cs.getNativeInstance(), shadowColor);
mShadowLayerRadius = radius;
mShadowLayerDx = dx;
mShadowLayerDy = dy;
mShadowLayerColor = shadowColor;
}
float radius:模糊半径,radius越大越模糊、越小越清晰。如果 radius设置为0 ,则阴影消失不可见。
float dx:阴影的横向偏移距离,正值向右偏移,负值向左偏移。
float dy:阴影的纵向偏移距离,正值向下偏移,负值向上偏移。
设置阴影的其他API
/**
* Clear the shadow layer.
*/
public void clearShadowLayer() {
setShadowLayer(0, 0, 0, 0);
}
/**
* Checks if the paint has a shadow layer attached
*
* @return true if the paint has a shadow layer attached and false otherwise
* @hide
*/
public boolean hasShadowLayer() {
return nHasShadowLayer(mNativePaint);
}
/**
* Returns the blur radius of the shadow layer.
* @see #setShadowLayer(float,float,float,int)
* @see #setShadowLayer(float,float,float,long)
*/
public float getShadowLayerRadius() {
return mShadowLayerRadius;
}
/**
* Returns the x offset of the shadow layer.
* @see #setShadowLayer(float,float,float,int)
* @see #setShadowLayer(float,float,float,long)
*/
public float getShadowLayerDx() {
return mShadowLayerDx;
}
/**
* Returns the y offset of the shadow layer.
* @see #setShadowLayer(float,float,float,int)
* @see #setShadowLayer(float,float,float,long)
*/
public float getShadowLayerDy() {
return mShadowLayerDy;
}
/**
* Returns the color of the shadow layer.
* @see #setShadowLayer(float,float,float,int)
* @see #setShadowLayer(float,float,float,long)
*/
public @ColorInt int getShadowLayerColor() {
return Color.toArgb(mShadowLayerColor);
}
看函数名就知道这几个API的作用了,这里不再说明。
清除阴影
/**
* Clear the shadow layer.
*/
public void clearShadowLayer() {
setShadowLayer(0, 0, 0, 0);
}
看代码,四个参数全设置为0了。自然阴影也就没有了。
代码实例:
package com.xw.shadowlayerview.view;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.Nullable;
import com.xw.shadowlayerview.R;
/**
* Copyright (c)2021 网络科技有限公司
*
* @author: LQS
* @date: 2021/6/15
* @description:com.xw.shadowlayerview.view.ShadowLayerView
*/
public class ShadowLayerView extends View {
private Paint mPaint = new Paint();
private Bitmap mBitmap;
public ShadowLayerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
setLayerType(LAYER_TYPE_SOFTWARE,null);
mPaint.setColor(Color.BLACK);
mPaint.setTextSize(25);
mPaint.setShadowLayer(1,10,10,Color.GRAY);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize=2;
mBitmap= BitmapFactory.decodeResource(getResources(), R.drawable.avatar,options);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawText("Good Morning!",100,100,mPaint);
canvas.drawCircle(200,200,50,mPaint);
canvas.drawBitmap(mBitmap,null,new RectF(50,300,200+mBitmap.getWidth(),300+mBitmap.getHeight()),mPaint);
}
}
布局使用:
<?xml version="1.0" encoding="utf-8"?>
<com.xw.shadowlayerview.view.ShadowLayerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
</com.xw.shadowlayerview.view.ShadowLayerView>
运行效果图:
二、BlurMaskFilter 发光效果与图片阴影
与阴影效果类似的还有一个发光效果,发光效果是无法指定发光颜色的,采用边缘部分的颜色取样来进行模糊发光。 所以,边缘是什么颜色的,发出的光就是什么颜色的。与 setShadowLayer() 函数一样, 发光效果使用的也是高斯模糊算法 ,并且只会影响边缘部分图像,内部图像是不受影响的。
setMaskFilter()函数中的MaskFilter是没有具体实现的, 是通过派生子类来实现具体的不同功能的。 MaskFilter有两个派生类:BlurMaskFilter和EmbossMaskFilter 。其中,BlurMaskFilter能够实现发光效果;而EmbossMaskFilter则可以用于实现浮雕效果。
setMaskFilter()函数源码定义如下:
/**
* Set or clear the maskfilter object.
* <p />
* Pass null to clear any previous maskfilter.
* As a convenience, the parameter passed is also returned.
*
* @param maskfilter May be null. The maskfilter to be installed in the
* paint
* @return maskfilter
*/
public MaskFilter setMaskFilter(MaskFilter maskfilter) {
long maskfilterNative = 0;
if (maskfilter != null) {
maskfilterNative = maskfilter.native_instance;
}
nSetMaskFilter(mNativePaint, maskfilterNative);
mMaskFilter = maskfilter;
return maskfilter;
}
其中函数参数MaskFilter类的子类BlurMaskFilter的构造函数定义如下:
/**
* Create a blur maskfilter.
*
* @param radius The radius to extend the blur from the original mask. Must be > 0.
* @param style The Blur to use
* @return The new blur maskfilter
*/
public BlurMaskFilter(float radius, Blur style) {
native_instance = nativeConstructor(radius, style.native_int);
}
参数
/**
* This takes a mask, and blurs its edge by the specified radius. Whether or
* or not to include the original mask, and whether the blur goes outside,
* inside, or straddles, the original mask's border, is controlled by the
* Blur enum.
*/
public class BlurMaskFilter extends MaskFilter {
public enum Blur {
/**
* Blur inside and outside the original border.
*/
NORMAL(0),
/**
* Draw solid inside the border, blur outside.
*/
SOLID(1),
/**
* Draw nothing inside the border, blur outside.
*/
OUTER(2),
/**
* Blur inside the border, draw nothing outside.
*/
INNER(3);
Blur(int value) {
native_int = value;
}
final int native_int;
}
四种发光效果示意图:
使用实例代码,在上面的代码基础上添加三个发光的圆形:
package com.xw.shadowlayerview.view;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.Nullable;
import com.xw.shadowlayerview.R;
/**
* Copyright (c)2021 网络科技有限公司
*
* @author: LQS
* @date: 2021/6/15
* @description:com.xw.shadowlayerview.view.ShadowLayerView
*/
public class ShadowLayerView extends View {
private Paint mPaint = new Paint();
//定义发光效果的画笔
private Paint mMaskPaint = new Paint();
private Bitmap mBitmap;
public ShadowLayerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
setLayerType(LAYER_TYPE_SOFTWARE,null);
mPaint.setColor(Color.BLACK);
mPaint.setTextSize(25);
mMaskPaint.setColor(Color.BLUE);
//设置画笔的发光效果为:内发光,模糊半径为50
mMaskPaint.setMaskFilter(new BlurMaskFilter(50, BlurMaskFilter.Blur.INNER));
//设置阴影效果
mPaint.setShadowLayer(1,10,10,Color.GRAY);
//设置图片的缩放效果参数
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize=2;
mBitmap= BitmapFactory.decodeResource(getResources(), R.drawable.avatar,options);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawText("Good Morning!",100,100,mPaint);
canvas.drawCircle(200,200,50,mPaint);
//画一个内发光的圆形
canvas.drawCircle(200,400,120,mMaskPaint);
//改变画笔的发光模式为内外发光
mMaskPaint.setMaskFilter(new BlurMaskFilter(20, BlurMaskFilter.Blur.NORMAL));
//画一个内外发光的圆形
canvas.drawCircle(480,400,120,mMaskPaint);
//改变画笔的发光模式为仅显示发光效果
mMaskPaint.setMaskFilter(new BlurMaskFilter(20, BlurMaskFilter.Blur.OUTER));
//画一个仅显示发光效果的圆形
canvas.drawCircle(760,400,120,mMaskPaint);
canvas.drawBitmap(mBitmap,null,new RectF(50,600,200+mBitmap.getWidth(),600+mBitmap.getHeight()),mPaint);
}
}
效果图,说明都在代码注释中,相信很好理解就不多说:
给图片添加纯色阴影
首先来看如何绘制出一张位图所对应的灰色图像?
canvas.drawBitmap()中的画笔颜色对画出来的位图是没有任何影响的,所以如果我们需要画一张位图所对应的灰色图像,就需要新建一张一样大小的空白图片,而且,新图片的透明度要与原图片保持一致,这样一来,如何从原图片中抽出 Alpha值成为关键。也就是说,只需要创建一张与原图片一样大小并且Alpha值相同的图片即可。
其实Bitmap中己经存在抽取出只具有Alpha值图片的函数,其声明如下:
/**
* Returns a new bitmap that captures the alpha values of the original.
* This may be drawn with Canvas.drawBitmap(), where the color(s) will be
* taken from the paint that is passed to the draw call.
*
* @return new bitmap containing the alpha channel of the original bitmap.
*/
@CheckResult
public Bitmap extractAlpha() {
return extractAlpha(null, null);
}
/**
* Returns a new bitmap that captures the alpha values of the original.
* These values may be affected by the optional Paint parameter, which
* can contain its own alpha, and may also contain a MaskFilter which
* could change the actual dimensions of the resulting bitmap (e.g.
* a blur maskfilter might enlarge the resulting bitmap). If offsetXY
* is not null, it returns the amount to offset the returned bitmap so
* that it will logically align with the original. For example, if the
* paint contains a blur of radius 2, then offsetXY[] would contains
* -2, -2, so that drawing the alpha bitmap offset by (-2, -2) and then
* drawing the original would result in the blur visually aligning with
* the original.
*
* <p>The initial density of the returned bitmap is the same as the original's.
*
* @param paint Optional paint used to modify the alpha values in the
* resulting bitmap. Pass null for default behavior.
* @param offsetXY Optional array that returns the X (index 0) and Y
* (index 1) offset needed to position the returned bitmap
* so that it visually lines up with the original.
* @return new bitmap containing the (optionally modified by paint) alpha
* channel of the original bitmap. This may be drawn with
* Canvas.drawBitmap(), where the color(s) will be taken from the
* paint that is passed to the draw call.
*/
@CheckResult
public Bitmap extractAlpha(Paint paint, int[] offsetXY) {
checkRecycled("Can't extractAlpha on a recycled bitmap");
long nativePaint = paint != null ? paint.getNativeInstance() : 0;
noteHardwareBitmapSlowCall();
Bitmap bm = nativeExtractAlpha(mNativePtr, nativePaint, offsetXY);
if (bm == null) {
throw new RuntimeException("Failed to extractAlpha on Bitmap");
}
bm.mDensity = mDensity;
return bm;
}
这个函数的功能是:新建一张空白图片,该图片具有与原图片一样的Alpha值,把这个新建的Bitmap作为结果返回。在这张空白图片中,每个像素都具有与原图片一样的 Alpha值。而且具体的颜色是在使用 canvas.drawBitmap()函数绘制时由传入的画笔颜色指定的。
实例:先在手机屏幕左边绘制抽取出来仅具有源图像透明度的透明图像,这是对源图像Bitmap对象执行extractAlpha()函数后,得到的图像的效果,通过灰色画笔绘制可以看得出来效果,它用于模仿阴影效果,仅作为参考图。在右侧绘制一个一模一样的这个阴影效果,再在这个阴影效果上绘制彩色的源图像。就得到了为彩色图像绘制纯色阴影背景的完美效果了。
package com.xw.shadowlayerview.view;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.Nullable;
import com.xw.shadowlayerview.R;
/**
* Copyright (c)2021 网络科技有限公司
*
* @author: LQS
* @date: 2021/6/16
* @description:
*/
public class ExtractAlphaView extends View {
private final Paint mPaint;
private final Bitmap mBitmap;
private final Bitmap mAlphaBmp;
public ExtractAlphaView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
setLayerType(LAYER_TYPE_SOFTWARE,null);
mPaint = new Paint();
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.cat_dog);
//利用extractAlpha()函数来生成新的源图像的仅具有透明度的空白图像对象
mAlphaBmp = mBitmap.extractAlpha();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = 500;
int height = width*mAlphaBmp.getWidth()/mAlphaBmp.getHeight();
mPaint.setColor(Color.GRAY);
//设置发光效果
mPaint.setMaskFilter(new BlurMaskFilter(10, BlurMaskFilter.Blur.NORMAL));
//绘制仅具有透明度的空白图像,并用灰色画笔绘制填充这个图像
canvas.drawBitmap(mAlphaBmp,null, new Rect(10,10,width,height),mPaint);
//向右平移width宽度
canvas.translate(width,0);
mPaint.setColor(Color.GRAY);
//绘制仅具有透明度的空白图像,并用灰色画笔绘制填充这个图像,形成纯色阴影的效果
canvas.drawBitmap(mAlphaBmp,null,new Rect(10,10,width,height),mPaint);
//在上面黑色图像上,绘制原来彩色的图片
canvas.translate(-18,-18);
mPaint.setMaskFilter(null);
canvas.drawBitmap(mBitmap,null, new Rect(10,10,width,height),mPaint);
}
}
效果图如下:
好了,关于文字、图形、图像简单的阴影绘制到此就完结了。欢迎留言评论!谢谢!
以上是关于自定义View之文字图形图片的阴影的主要内容,如果未能解决你的问题,请参考以下文章