Android中的很多自带控件都有类似软阴影的效果,比如说Button,使用BlurMaskFilter就可以得到类似的效果
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#FFFFFF" android:orientation="vertical" > <com.aigestudio.customviewdemo.views.MaskFilterView android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
package com.aigestudio.customviewdemo.utils; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.Color; import android.util.DisplayMetrics; public final class MeasureUtil { public static int[] getScreenSize(Activity activity) { DisplayMetrics metrics = new DisplayMetrics(); activity.getWindowManager().getDefaultDisplay().getMetrics(metrics); return new int[] { metrics.widthPixels, metrics.heightPixels }; } }
package com.aigestudio.customviewdemo.activities; import android.app.Activity; import android.os.Bundle; import com.aigestudio.customviewdemo.R; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } }
package com.aigestudio.customviewdemo.views; import android.app.Activity; import android.content.Context; import android.graphics.BlurMaskFilter; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.util.AttributeSet; import android.view.View; import com.aigestudio.customviewdemo.utils.MeasureUtil; public class MaskFilterView extends View { private static final int RECT_SIZE = 800; private Paint mPaint;// 画笔 private Context mContext;// 上下文环境引用 private int left, top, right, bottom;// public MaskFilterView(Context context) { this(context, null); } public MaskFilterView(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; // 初始化画笔 initPaint(); // 初始化资源 initRes(context); /** * 如果可以只针对某个View关闭硬件加速那岂不是很好么?当然,Android也给我们提供了这样的功能,我们可以在View中通过 */ setLayerType(LAYER_TYPE_SOFTWARE, null); } /** * 初始化画笔 */ private void initPaint() { // 实例化画笔 mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG); mPaint.setStyle(Paint.Style.FILL); mPaint.setColor(0xFF603811); /** * radius很容易理解,值越大我们的阴影越扩散 * SOLID,其效果就是在图像的Alpha边界外产生一层与Paint颜色一致的阴影效果而不影响图像本身 * NORMAL会将整个图像模糊掉 * OUTER会在Alpha边界外产生一层阴影且会将原本的图像变透明 * INNER则会在图像内部产生模糊 */ mPaint.setMaskFilter(new BlurMaskFilter(20, BlurMaskFilter.Blur.SOLID)); } /** * 初始化资源 */ private void initRes(Context context) { /* * 计算位图绘制时左上角的坐标使其位于屏幕中心 */ left = MeasureUtil.getScreenSize((Activity) mContext)[0] / 2 - RECT_SIZE / 2; top = MeasureUtil.getScreenSize((Activity) mContext)[1] / 2 - RECT_SIZE / 2; right = MeasureUtil.getScreenSize((Activity) mContext)[0] / 2 + RECT_SIZE / 2; bottom = MeasureUtil.getScreenSize((Activity) mContext)[1] / 2 + RECT_SIZE / 2; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawColor(Color.GRAY); // 画一个矩形 //(float left, float top, float right, float bottom, Paint paint) canvas.drawRect(left, top, right, bottom, mPaint); } }
或者下面的方法也可以实现
package com.aigestudio.customviewdemo.views; import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Style; import android.util.AttributeSet; import android.view.View; import com.aigestudio.customviewdemo.utils.MeasureUtil; @SuppressLint("NewApi") public class ShadowView extends View { private static final int RECT_SIZE = 800;// 方形大小 private Paint mPaint;// 画笔 private int left, top, right, bottom;// 绘制时坐标 public ShadowView(Context context, AttributeSet attrs) { super(context, attrs); // setShadowLayer不支持HW setLayerType(LAYER_TYPE_SOFTWARE, null); // 初始化画笔 initPaint(); // 初始化资源 initRes(context); } /** * 初始化画笔 */ private void initPaint() { // 实例化画笔 mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setColor(Color.RED); mPaint.setStyle(Style.FILL); //radius表示阴影的扩散半径,而dx和dy表示阴影平面上的偏移值,shadowColor就不说了阴影颜色 mPaint.setShadowLayer(60, 3, 3, Color.DKGRAY); } /** * 初始化资源 */ private void initRes(Context context) { /* * 计算位图绘制时左上角的坐标使其位于屏幕中心 */ left = MeasureUtil.getScreenSize((Activity) context)[0] / 2 - RECT_SIZE / 2; top = MeasureUtil.getScreenSize((Activity) context)[1] / 2 - RECT_SIZE / 2; right = MeasureUtil.getScreenSize((Activity) context)[0] / 2 + RECT_SIZE / 2; bottom = MeasureUtil.getScreenSize((Activity) context)[1] / 2 + RECT_SIZE / 2; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 先绘制位图 canvas.drawRect(left, top, right, bottom, mPaint); } }
如上所说BlurMaskFilter是根据Alpha通道的边界来计算模糊的,如果是一张图片(注:上面我们说过Android会把拷贝到资源目录的图片转为RGB565,具体原因具体分析我会单独开一篇帖子说,这里就先假设所有提及的图片格式为RGB565)你会发现没有任何效果,那么假使我们需要给图片加一个类似阴影的效果该如何做呢?其实很简单,我们可以尝试从Bitmap中获取其Alpha通道,并在绘制Bitmap前先以该Alpha通道绘制一个模糊效果不就行了?
如下面代码所示我们通过Bitmap的extractAlpha()方法从原图中分离出一个Alpha通道位图并在计算模糊滤镜的时候使用该位图生成模糊效果:
BlurMaskFilterView
package com.aigestudio.customviewdemo.views; import android.app.Activity; 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.util.AttributeSet; import android.view.View; import com.aigestudio.customviewdemo.R; import com.aigestudio.customviewdemo.utils.MeasureUtil; public class BlurMaskFilterView extends View { private Paint shadowPaint;// 画笔 private Context mContext;// 上下文环境引用 private Bitmap srcBitmap, shadowBitmap;// 位图和阴影位图 private int x, y;// 位图绘制时左上角的起点坐标 public BlurMaskFilterView(Context context) { this(context, null); } public BlurMaskFilterView(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; // 记得设置模式为SOFTWARE setLayerType(LAYER_TYPE_SOFTWARE, null); // 初始化画笔 initPaint(); // 初始化资源 initRes(context); } /** * 初始化画笔 */ private void initPaint() { // 实例化画笔 shadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG); shadowPaint.setColor(Color.DKGRAY); //outer solid normal 都可以 但是inner不行 shadowPaint.setMaskFilter(new BlurMaskFilter(10, BlurMaskFilter.Blur.NORMAL)); } /** * 初始化资源 */ private void initRes(Context context) { // 获取位图 srcBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.a); // 获取位图的Alpha通道图 shadowBitmap = srcBitmap.extractAlpha(); /* * 计算位图绘制时左上角的坐标使其位于屏幕中心 */ x = MeasureUtil.getScreenSize((Activity) mContext)[0] / 2 - srcBitmap.getWidth() / 2; y = MeasureUtil.getScreenSize((Activity) mContext)[1] / 2 - srcBitmap.getHeight() / 2; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 先绘制阴影 canvas.drawBitmap(shadowBitmap, x, y, shadowPaint); // 再绘制位图 canvas.drawBitmap(srcBitmap, x, y, null); } }