本文主要包括以下内容:
使用Xfermode设置圆角图片 使用BitmapShader设置圆角图片 滑动旋转缩放的bimp图片 图片颜色处理(滑动) 图片 + 文字
其中1,2是两种不同方式处理图片圆角的情况。3,是通过Matrix进行图片缩放,旋转等。4,是通过Matrix操作图片的处理,包括去饱和,四角黑影,中心突出等。5,图片加文字组合显示。
如果暂时感觉这些看不懂:
先看看这两篇:
Android自定义控件深入学习 Android生成随机验证码
详解Android自定义控件属性TypedArray以及attrs
1、使用Xfermode设置圆角图片
主要代码
package com.example.customimage.view; import com.example.customimage.R; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.RectF; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; import android.view.View; /** * 自定义View,实现圆角,圆形等效果 */ public class XfermodeImageView extends View { //类型 private int type; private static final int TYPE_CIRCLE = 0; //圆形 private static final int TYPE_RECT = 1; //矩形圆角 //原始图片 private Bitmap mSrc; //矩形圆角的幅度 private int mRadius; //控件的宽度 private int mWidth; // 控件的高度 private int mHeight; public XfermodeImageView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public XfermodeImageView(Context context) { this(context, null); } /** * 初始化一些自定义的参数 * * @param context * @param attrs * @param defStyle */ public XfermodeImageView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.XfermodeImageView, defStyle, 0); int n = a.getIndexCount(); for (int i = 0; i < n; i++) { int attr = a.getIndex(i); switch (attr) { //原始图片,在布局里面获取 case R.styleable.XfermodeImageView_src: mSrc = BitmapFactory.decodeResource(getResources(), a.getResourceId(attr, 0)); break; //类型属性选择 case R.styleable.XfermodeImageView_type: //自定义类型属性,0是圆形,1是矩形圆角 type = a.getInt(attr, 0);// 默认为Circle break; //矩形圆角幅度的获取,默认是10dp case R.styleable.XfermodeImageView_borderRadius: mRadius = a.getDimensionPixelSize(attr, (int) TypedValue .applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10f, getResources().getDisplayMetrics())); Log.i("Show", String.valueOf(mRadius)); break; } } a.recycle(); } /** * 计算控件的高度和宽度 */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // 设置宽度 int specMode = MeasureSpec.getMode(widthMeasureSpec); int specSize = MeasureSpec.getSize(widthMeasureSpec); //match_parent或者设置的精确值获取 //MeasureSpec.EXACTLY if (specMode == MeasureSpec.EXACTLY) { mWidth = specSize; } else { // 由图片决定的宽 //getPaddingLeft(),getPaddingRight()这两个值是控件属性的向内偏移的距离值,所以的一起计算 //区别于layout_marginLeft,两个控件的左间距值设置 int desireByImg = getPaddingLeft() + getPaddingRight() + mSrc.getWidth(); // wrap_content if (specMode == MeasureSpec.AT_MOST) { //所以最小的值,宽度的话是左右内偏移距离之和 mWidth = Math.min(desireByImg, specSize); } else mWidth = desireByImg; } // 设置高度,部分解释同上 specMode = MeasureSpec.getMode(heightMeasureSpec); specSize = MeasureSpec.getSize(heightMeasureSpec); //match_parent或者设置的精确值获取 //MeasureSpec.EXACTLY if (specMode == MeasureSpec.EXACTLY) { mHeight = specSize; } else { int desire = getPaddingTop() + getPaddingBottom() + mSrc.getHeight(); // wrap_content if (specMode == MeasureSpec.AT_MOST) { mHeight = Math.min(desire, specSize); } else mHeight = desire; } //计算好的宽度以及高度是值,设置进去 setMeasuredDimension(mWidth, mHeight); } /** * 绘制image控件 */ @Override protected void onDraw(Canvas canvas) { switch (type) { // 如果是TYPE_CIRCLE绘制圆形 case TYPE_CIRCLE: //圆形宽度和高度应该一致的,所以也要比较一下大小,取小的值 int min = Math.min(mWidth, mHeight); // 圆形宽度和高度如果不一致,按小的值进行压缩 mSrc = Bitmap.createScaledBitmap(mSrc, min, min, false); //绘制圆形 canvas.drawBitmap(createCircleImage(mSrc, min), 0, 0, null); break; case TYPE_RECT: canvas.drawBitmap(createRoundConerImage(mSrc), 0, 0, null); break; } } /** * 根据原图和变长绘制圆形图片 * * @param source * @param min * @return */ private Bitmap createCircleImage(Bitmap source, int min) { final Paint paint = new Paint(); //防止边缘的抗锯齿 paint.setAntiAlias(true); Bitmap target = Bitmap.createBitmap(min, min, Config.ARGB_8888); // 产生一个同样大小的画布 Canvas canvas = new Canvas(target); // 首先绘制圆形,除以2就是半径了 //最主要设置第三个参数为min/2,圆角幅度那么大就是圆形了 canvas.drawCircle(min / 2, min / 2, min/2, paint); // 使用SRC_IN,参考上面的说明---上下层都显示。下层居上显示 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); // 绘制图片 canvas.drawBitmap(source, 0, 0, paint); return target; } /** * 根据原图添加圆角 * * @param source * @return */ private Bitmap createRoundConerImage(Bitmap source) { final Paint paint = new Paint(); paint.setAntiAlias(true); Bitmap target = Bitmap.createBitmap(mWidth, mHeight, Config.ARGB_8888); Canvas canvas = new Canvas(target); //绘制矩形 RectF rect = new RectF(0, 0, source.getWidth(), source.getHeight()); //设置圆角幅度 canvas.drawRoundRect(rect, mRadius, mRadius, paint); // 使用SRC_IN,参考上面的说明---上下层都显示。下层居上显示 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); canvas.drawBitmap(source, 0, 0, paint); return target; } }
2、使用BitmapShader设置圆角图片
package com.example.customimage.view; import com.example.customimage.R; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.BitmapShader; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.RectF; import android.graphics.Shader.TileMode; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Parcelable; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; import android.widget.ImageView; /** * */ public class BitmapShaderImageView extends ImageView { // 图片的类型,圆形or圆角 private int type; public static final int TYPE_CIRCLE = 0; public static final int TYPE_RECT = 1; //圆角大小的默认值 private static final int BODER_RADIUS_DEFAULT = 10; //圆角的大小 private int mBorderRadius; // 绘图的Paint private Paint mBitmapPaint; //圆角的半径 private int mRadius; // 3x3 矩阵,主要用于缩小放大 private Matrix mMatrix; // 渲染图像,使用图像为绘制图形着色 private BitmapShader mBitmapShader; // view的宽度 private int mWidth; //矩形 private RectF mRoundRect; public BitmapShaderImageView(Context context) { this(context, null); } public BitmapShaderImageView(Context context, AttributeSet attrs) { super(context, attrs); mMatrix = new Matrix(); mBitmapPaint = new Paint(); mBitmapPaint.setAntiAlias(true); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.BitmapShaderImageView); //矩形圆角幅度的获取,默认是10dp mBorderRadius = a.getDimensionPixelSize( R.styleable.BitmapShaderImageView_borderRadius, (int) TypedValue .applyDimension(TypedValue.COMPLEX_UNIT_DIP, BODER_RADIUS_DEFAULT, getResources() .getDisplayMetrics())); //自定义类型属性,0是圆形,1是矩形圆角 type = a.getInt(R.styleable.BitmapShaderImageView_type, TYPE_CIRCLE); a.recycle(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); // 如果类型是圆形,则强制改变view的宽高一致,以小值为准 if (type == TYPE_CIRCLE) { mWidth = Math.min(getMeasuredWidth(), getMeasuredHeight()); //圆形的半径 mRadius = mWidth / 2; setMeasuredDimension(mWidth, mWidth); } } // 初始化BitmapShader,获取到图片资源 // 等待画布的准备好,然后在画布上加上Paint就是了 //就是说图片的载体是Paint private void setUpShader() { Drawable drawable = getDrawable(); if (drawable == null) { return; } Bitmap bmp = drawableToBitamp(drawable); // 将bmp作为着色器,就是在指定区域内绘制bmp //TileMode.CLAMP 拉伸 mBitmapShader = new BitmapShader(bmp, TileMode.CLAMP, TileMode.CLAMP); float scale = 1.0f; if (type == TYPE_CIRCLE) { // 拿到bitmap宽或高的小值 int bSize = Math.min(bmp.getWidth(), bmp.getHeight()); scale = mWidth * 1.0f / bSize; } else if (type == TYPE_RECT) { if (!(bmp.getWidth() == getWidth() && bmp.getHeight() == getHeight())) { // 如果图片的宽或者高与view的宽高不匹配,计算出需要缩放的比例;缩放后的图片的宽高,一定要大于我们view的宽高;所以我们这里取大值; scale = Math.max(getWidth() * 1.0f / bmp.getWidth(), getHeight() * 1.0f / bmp.getHeight()); } } // shader的变换矩阵,我们这里主要用于放大或者缩小 // scale * scale 的矩阵 mMatrix.setScale(scale, scale); // 设置变换矩阵 mBitmapShader.setLocalMatrix(mMatrix); // 设置shader mBitmapPaint.setShader(mBitmapShader); } @Override protected void onDraw(Canvas canvas) { if (getDrawable() == null) { return; } setUpShader(); if (type == TYPE_RECT) { //绘制矩形 canvas.drawRoundRect(mRoundRect, mBorderRadius, mBorderRadius, mBitmapPaint); } else { //绘制圆形 canvas.drawCircle(mRadius, mRadius, mRadius, mBitmapPaint); } } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); // 圆角图片的范围 if (type == TYPE_RECT) mRoundRect = new RectF(0, 0, w, h); } //drawable转bitmap private Bitmap drawableToBitamp(Drawable drawable) { //从控件的src获取背景,也是drawable文件获取 if (drawable instanceof BitmapDrawable) { BitmapDrawable bd = (BitmapDrawable) drawable; return bd.getBitmap(); } //如果没有绘图一个,只不过是空白的图片 int w = drawable.getIntrinsicWidth(); int h = drawable.getIntrinsicHeight(); Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); drawable.setBounds(0, 0, w, h); drawable.draw(canvas); return bitmap; } private static final String STATE_INSTANCE = "state_instance"; private static final String STATE_TYPE = "state_type"; private static final String STATE_BORDER_RADIUS = "state_border_radius"; //屏幕旋转后,取出保存的值 @Override protected Parcelable onSaveInstanceState() { Bundle bundle = new Bundle(); bundle.putParcelable(STATE_INSTANCE, super.onSaveInstanceState()); bundle.putInt(STATE_TYPE, type); bundle.putInt(STATE_BORDER_RADIUS, mBorderRadius); return bundle; } //屏幕旋转,变量的保存,因为外面设置值,如果不保存,一旋转就变成个xml里面设置的值 @Override protected void onRestoreInstanceState(Parcelable state) { if (state instanceof Bundle) { Bundle bundle = (Bundle) state; super.onRestoreInstanceState(((Bundle) state) .getParcelable(STATE_INSTANCE)); this.type = bundle.getInt(STATE_TYPE); this.mBorderRadius = bundle.getInt(STATE_BORDER_RADIUS); } else { super.onRestoreInstanceState(state); } } //设置矩形圆角幅度后,重新绘制控件 public void setBorderRadius(int borderRadius) { int pxVal = dp2px(borderRadius); if (this.mBorderRadius != pxVal) { this.mBorderRadius = pxVal; invalidate(); } } //设置是圆形还是矩形圆角 public void setType(int type) { if (this.type != type) { this.type = type; if (this.type != TYPE_RECT && this.type != TYPE_CIRCLE) { this.type = TYPE_CIRCLE; } requestLayout(); } } //dp转px public int dp2px(int dpVal) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, getResources().getDisplayMetrics()); } }
3、滑动旋转缩放的bimp图片
package com.example.customimage.view; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PaintFlagsDrawFilter; import android.graphics.PointF; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.MotionEvent; import android.widget.ImageView; public class MatrixImageView extends ImageView { private static final int MODE_NONE = 0x00123;// 默认的触摸模式 private static final int MODE_DRAG = 0x00321;// 拖拽模式 private static final int MODE_ZOOM = 0x00132;// 缩放or旋转模式 private int mode;// 当前的触摸模式 private float preMove = 1F;// 上一次手指移动的距离 private float saveRotate = 0F;// 保存了的角度值 private float rotate = 0F;// 旋转的角度 private float[] preEventCoor;// 上一次各触摸点的坐标集合 private PointF startPointF, midPointF;// 起点、中点对象 private Matrix currentMatrix, savedMatrix;// 当前和保存了的Matrix对象 //原始图片 private Bitmap mSrc; //控件的宽度 private int mWidth; // 控件的高度 private int mHeight; private PaintFlagsDrawFilter mDrawFilter; public MatrixImageView(Context context, AttributeSet attrs) { super(context, attrs); // 初始化 init(); } /** * 初始化 */ private void init() { // 实例化对象 currentMatrix = new Matrix(); savedMatrix = new Matrix(); startPointF = new PointF(); midPointF = new PointF(); mDrawFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG); // 模式初始化 mode = MODE_NONE; Drawable drawable = getDrawable(); mSrc = drawableToBitamp(drawable); } /** * 计算控件的高度和宽度 */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // 设置宽度 int specMode = MeasureSpec.getMode(widthMeasureSpec); int specSize = MeasureSpec.getSize(widthMeasureSpec); //match_parent或者设置的精确值获取 //MeasureSpec.EXACTLY if (specMode == MeasureSpec.EXACTLY) { mWidth = specSize; } else { // 由图片决定的宽 //getPaddingLeft(),getPaddingRight()这两个值是控件属性的向内偏移的距离值,所以的一起计算 //区别于layout_marginLeft,两个控件的左间距值设置 int desireByImg = getPaddingLeft() + getPaddingRight() + mSrc.getWidth(); // wrap_content if (specMode == MeasureSpec.AT_MOST) { //所以最小的值,宽度的话是左右内偏移距离之和 mWidth = Math.min(desireByImg, specSize); } else mWidth = desireByImg; } // 设置高度,部分解释同上 specMode = MeasureSpec.getMode(heightMeasureSpec); specSize = MeasureSpec.getSize(heightMeasureSpec); //match_parent或者设置的精确值获取 //MeasureSpec.EXACTLY if (specMode == MeasureSpec.EXACTLY) { mHeight = specSize; } else { int desire = getPaddingTop() + getPaddingBottom() + mSrc.getHeight(); // wrap_content if (specMode == MeasureSpec.AT_MOST) { mHeight = Math.min(desire, specSize); } else mHeight = desire; } //计算好的宽度以及高度是值,设置进去 setMeasuredDimension(mWidth, mHeight); } //drawable转bitmap private Bitmap drawableToBitamp(Drawable drawable) { //从控件的src获取背景,也是drawable文件获取 if (drawable instanceof BitmapDrawable) { BitmapDrawable bd = (BitmapDrawable) drawable; return bd.getBitmap(); } //如果没有绘图一个,只不过是空白的图片 int w = drawable.getIntrinsicWidth(); int h = drawable.getIntrinsicHeight(); Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); drawable.setBounds(0, 0, w, h); drawable.draw(canvas); return bitmap; } final Paint paint = new Paint(); @Override protected void onDraw(Canvas canvas) { //消除锯齿, 图片旋转后的锯齿消除不成功,实在不行图片边缘加一些白色像素点 canvas.setDrawFilter(mDrawFilter); //画经过Matrix变化后的图 canvas.drawBitmap(mSrc, currentMatrix, null); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN:// 单点接触屏幕时 savedMatrix.set(currentMatrix); startPointF.set(event.getX(), event.getY()); //单点触摸是移动模式 mode = MODE_DRAG; preEventCoor = null; break; case MotionEvent.ACTION_POINTER_DOWN:// 第二个点接触屏幕时 preMove = calSpacing(event); if (preMove > 10F) { savedMatrix.set(currentMatrix); // 计算两个触摸点的中点坐标 calMidPoint(midPointF, event); //两点是旋转或者缩放模式 mode = MODE_ZOOM; } preEventCoor = new float[4]; preEventCoor[0] = event.getX(0); preEventCoor[1] = event.getX(1); preEventCoor[2] = event.getY(0); preEventCoor[3] = event.getY(1); saveRotate = calRotation(event); break; case MotionEvent.ACTION_UP:// 单点离开屏幕时 case MotionEvent.ACTION_POINTER_UP:// 第二个点离开屏幕时 mode = MODE_NONE; preEventCoor = null; break; case MotionEvent.ACTION_MOVE:// 触摸点移动时 /* * 单点触控拖拽平移 */ if (mode == MODE_DRAG) { currentMatrix.set(savedMatrix); float dx = event.getX() - startPointF.x; float dy = event.getY() - startPointF.y; currentMatrix.postTranslate(dx, dy); } /* * 两点触控拖放旋转 */ else if (mode == MODE_ZOOM && event.getPointerCount() == 2) { float currentMove = calSpacing(event); currentMatrix.set(savedMatrix); /* * 指尖移动距离大于10F缩放 */ if (currentMove > 10F) { float scale = currentMove / preMove; currentMatrix.postScale(scale, scale, midPointF.x, midPointF.y); } /* * 保持两点时旋转 */ if (preEventCoor != null) { rotate = calRotation(event); r = rotate - saveRotate; currentMatrix.postRotate(r, getMeasuredWidth() / 2, getMeasuredHeight() / 2); } } break; } setImageMatrix(currentMatrix); return true; } float r; /** * 计算两个触摸点间的距离 */ private float calSpacing(MotionEvent event) { float x = event.getX(0) - event.getX(1); float y = event.getY(0) - event.getY(1); return (float) Math.sqrt(x * x + y * y); } /** * 计算两个触摸点的中点坐标 */ private void calMidPoint(PointF point, MotionEvent event) { float x = event.getX(0) + event.getX(1); float y = event.getY(0) + event.getY(1); point.set(x / 2, y / 2); } /** * 计算旋转角度 * * @param 事件对象 * @return 角度值 */ private float calRotation(MotionEvent event) { double deltaX = (event.getX(0) - event.getX(1)); double deltaY = (event.getY(0) - event.getY(1)); double radius = Math.atan2(deltaY, deltaX); return (float) Math.toDegrees(radius); } }
4、图片颜色处理(滑动)
package com.example.customimage.view; import com.example.customimage.R; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorMatrixColorFilter; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PorterDuff.Mode; import android.graphics.PorterDuffXfermode; import android.graphics.RadialGradient; import android.graphics.Shader.TileMode; import android.util.AttributeSet; import android.view.View; public class DreamEffectView extends View { private Paint mBitmapPaint, mShaderPaint;// 位图画笔和Shader图形的画笔 private PorterDuffXfermode mXfermode;// 图形混合模式 private int x, y;// 位图起点坐标 //控件的宽度 private int mWidth; // 控件的高度 private int mHeight; //原始图片 private Bitmap mSrc; //生成暗角的图片 private Bitmap darkCornerBitmap; public final static int BITMAP_NULL = 0; //原图 private final static int BITMAP_DEAST = 1; //去饱和 private final static int BITMAP_CENTER = 2; //中心突出 private final static int BITMAP_DARKCORNER = 3; //四角黑暗 private int mnSelectMode = 0; public DreamEffectView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public DreamEffectView(Context context) { this(context, null); } public DreamEffectView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs,defStyle); TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.DreamEffectView, defStyle, 0); int n = a.getIndexCount(); for (int i = 0; i < n; i++) { int attr = a.getIndex(i); switch (attr) { //原始图片,在布局里面获取 case R.styleable.DreamEffectView_src: mSrc = BitmapFactory.decodeResource(getResources(), a.getResourceId(attr, 0)); break; case R.styleable.DreamEffectView_mode: mnSelectMode = a.getInt(attr, 0); break; } } a.recycle(); // 实例化混合模式 mXfermode = new PorterDuffXfermode(Mode.SCREEN); // 初始化画笔 initPaint(); } /** * 计算控件的高度和宽度 */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // 设置宽度 int specMode = MeasureSpec.getMode(widthMeasureSpec); int specSize = MeasureSpec.getSize(widthMeasureSpec); if (specMode == MeasureSpec.EXACTLY) { mWidth = specSize; } else { int desireByImg = getPaddingLeft() + getPaddingRight() + mSrc.getWidth(); // wrap_content if (specMode == MeasureSpec.AT_MOST) { //所以最小的值,宽度的话是左右内偏移距离之和 mWidth = Math.min(desireByImg, specSize); } else mWidth = desireByImg; } // 设置高度,部分解释同上 specMode = MeasureSpec.getMode(heightMeasureSpec); specSize = MeasureSpec.getSize(heightMeasureSpec); if (specMode == MeasureSpec.EXACTLY) { mHeight = specSize; } else { int desire = getPaddingTop() + getPaddingBottom() + mSrc.getHeight(); // wrap_content if (specMode == MeasureSpec.AT_MOST) { mHeight = Math.min(desire, specSize); } else mHeight = desire; } x = mWidth / 2 - mSrc.getWidth() / 2; y = mHeight / 2 - mSrc.getHeight() / 2; //计算好的宽度以及高度是值,设置进去 setMeasuredDimension(mWidth, mHeight); } /** * 初始化画笔 */ private void initPaint() { // 实例化画笔 mBitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG); // 实例化Shader图形的画笔 mShaderPaint = new Paint(); switch (mnSelectMode) { case BITMAP_NULL: break; case BITMAP_DEAST: // 去饱和、提亮、色相矫正 mBitmapPaint.setColorFilter(new ColorMatrixColorFilter( new float[] { 0.8587F, 0.2940F, -0.0927F, 0, 6.79F, 0.0821F, 0.9145F, 0.0634F, 0, 6.79F, 0.2019F, 0.1097F, 0.7483F, 0, 6.79F, 0, 0, 0, 1, 0 })); break; case BITMAP_CENTER: // 去饱和、提亮、色相矫正 mBitmapPaint.setColorFilter(new ColorMatrixColorFilter( new float[] { 0.8587F, 0.2940F, -0.0927F, 0, 6.79F, 0.0821F, 0.9145F, 0.0634F, 0, 6.79F, 0.2019F, 0.1097F, 0.7483F, 0, 6.79F, 0, 0, 0, 1, 0 })); // 设置径向渐变,渐变中心当然是图片的中心也是屏幕中心,渐变半径我们直接拿图片的高度但是要稍微小一点 // 中心颜色为透明而边缘颜色为黑色 mShaderPaint.setShader(new RadialGradient(mWidth / 2, mWidth / 2, mSrc.getHeight() * 7 / 8, Color.TRANSPARENT, Color.BLACK, TileMode.CLAMP)); break; case BITMAP_DARKCORNER: // 去饱和、提亮、色相矫正 mBitmapPaint.setColorFilter(new ColorMatrixColorFilter( new float[] { 0.8587F, 0.2940F, -0.0927F, 0, 6.79F, 0.0821F, 0.9145F, 0.0634F, 0, 6.79F, 0.2019F, 0.1097F, 0.7483F, 0, 6.79F, 0, 0, 0, 1, 0 })); // 根据我们源图的大小生成暗角Bitmap darkCornerBitmap = Bitmap.createBitmap(mSrc.getWidth(), mSrc.getHeight(), Bitmap.Config.ARGB_8888); // 将该暗角Bitmap注入Canvas Canvas canvas = new Canvas(darkCornerBitmap); // 计算径向渐变半径 float radiu = canvas.getHeight() * (2F / 3F); // 实例化径向渐变 RadialGradient radialGradient = new RadialGradient(canvas.getWidth() / 2F, canvas.getHeight() / 2F, radiu, new int[] { 0, 0, 0xAA000000 }, new float[] { 0F, 0.7F, 1.0F }, TileMode.CLAMP); // 实例化一个矩阵 Matrix matrix = new Matrix(); // 设置矩阵的缩放 matrix.setScale(canvas.getWidth() / (radiu * 2F), 1.0F); // 设置矩阵的预平移 matrix.preTranslate(((radiu * 2F) - canvas.getWidth()) / 2F, 0); // 将该矩阵注入径向渐变 radialGradient.setLocalMatrix(matrix); // 设置画笔Shader mShaderPaint.setShader(radialGradient); // 绘制矩形 canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), mShaderPaint); break; default: break; } } @Override protected void onDraw(Canvas canvas) { canvas.drawColor(Color.BLACK); // 新建图层 int sc = canvas.saveLayer(x, y, x + mSrc.getWidth(), y + mSrc.getHeight(), null, Canvas.ALL_SAVE_FLAG); // 绘制混合颜色 canvas.drawColor(0xcc1c093e); // 设置混合模式 mBitmapPaint.setXfermode(mXfermode); // 绘制位图 canvas.drawBitmap(mSrc, x, y, mBitmapPaint); // 还原混合模式 mBitmapPaint.setXfermode(null); // 还原画布 canvas.restoreToCount(sc); switch (mnSelectMode) { case BITMAP_NULL: break; case BITMAP_DEAST: break; case BITMAP_CENTER: // 绘制一个跟图片大小一样的矩形 canvas.drawRect(x, y, x + mSrc.getWidth(), y + mSrc.getHeight(),mShaderPaint); break; case BITMAP_DARKCORNER: // 绘制我们画好的径向渐变图 canvas.drawBitmap(darkCornerBitmap, x, y, null); break; default: break; } } }
1)、实现 去饱和、提亮、色相矫正 效果的部分代码
mBitmapPaint.setColorFilter(new ColorMatrixColorFilter( new float[] { 0.8587F, 0.2940F, -0.0927F, 0, 6.79F, 0.0821F, 0.9145F, 0.0634F, 0, 6.79F, 0.2019F, 0.1097F, 0.7483F, 0, 6.79F, 0, 0, 0, 1, 0 }));
2)、实现中心颜色为透明而边缘颜色为黑色效果的部分代码
mShaderPaint.setShader(new RadialGradient(mWidth / 2, mWidth / 2, mSrc.getHeight() * 7 / 8, Color.TRANSPARENT, Color.BLACK, TileMode.CLAMP));
3)、实现四角黑暗效果的部分代码
// 根据我们源图的大小生成暗角Bitmap darkCornerBitmap = Bitmap.createBitmap(mSrc.getWidth(), mSrc.getHeight(), Bitmap.Config.ARGB_8888); // 将该暗角Bitmap注入Canvas Canvas canvas = new Canvas(darkCornerBitmap); // 计算径向渐变半径 float radiu = canvas.getHeight() * (2F / 3F); // 实例化径向渐变 RadialGradient radialGradient = new RadialGradient(canvas.getWidth() / 2F, canvas.getHeight() / 2F, radiu, new int[] { 0, 0, 0xAA000000 }, new float[] { 0F, 0.7F, 1.0F }, TileMode.CLAMP); // 实例化一个矩阵 Matrix matrix = new Matrix(); // 设置矩阵的缩放 matrix.setScale(canvas.getWidth() / (radiu * 2F), 1.0F); // 设置矩阵的预平移 matrix.preTranslate(((radiu * 2F) - canvas.getWidth()) / 2F, 0); // 将该矩阵注入径向渐变 radialGradient.setLocalMatrix(matrix); // 设置画笔Shader mShaderPaint.setShader(radialGradient); // 绘制矩形 canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), mShaderPaint);
5、图片 + 文字
package com.example.customimage.view; import com.example.customimage.R; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.RectF; import android.graphics.Bitmap.Config; import android.graphics.Paint.Style; import android.graphics.Rect; import android.text.TextPaint; import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; import android.view.View; public class ImageViewText extends View{ //类型 private int type; private static final int TYPE_CIRCLE = 0; //圆形 private static final int TYPE_RECT = 1; //矩形圆角 //原始图片 private Bitmap mImage; //控件的宽度 private int mWidth; // 控件的高度 private int mHeight; //矩形圆角的幅度 private int mRadius; //图片下面的文字 private String mTitle; private int mTextColor; private int mTextSize; private Rect rect; private Paint mPaint; private Rect mTextBound; public ImageViewText(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ImageViewText(Context context) { this(context, null); } /** * 初始化所特有自定义类型 * * @param context * @param attrs * @param defStyle */ public ImageViewText(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ImageViewText, defStyle, 0); int n = a.getIndexCount(); for (int i = 0; i < n; i++) { int attr = a.getIndex(i); switch (attr) { //获取图片 case R.styleable.ImageViewText_image: mImage = BitmapFactory.decodeResource(getResources(), a.getResourceId(attr, 0)); break; //获取圆形还是方形 case R.styleable.ImageViewText_type: type = a.getInt(attr, 0); break; //获取文字 case R.styleable.ImageViewText_titleText: mTitle = a.getString(attr); break; //获取文字的图片 case R.styleable.ImageViewText_titleTextColor: mTextColor = a.getColor(attr, Color.BLACK); break; //获取文字的大小 case R.styleable.ImageViewText_titleTextSize: mTextSize = a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics())); break; //矩形圆角幅度的获取,默认是10dp case R.styleable.ImageViewText_borderRadius: mRadius = a.getDimensionPixelSize(attr, (int) TypedValue .applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10f, getResources().getDisplayMetrics())); break; } } a.recycle(); rect = new Rect(); mPaint = new Paint(); mTextBound = new Rect(); mPaint.setTextSize(mTextSize); // 计算了描绘字体需要的范围 mPaint.getTextBounds(mTitle, 0, mTitle.length(), mTextBound); } //测量控件的大小 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //设置宽度 int specMode = MeasureSpec.getMode(widthMeasureSpec); int specSize = MeasureSpec.getSize(widthMeasureSpec); // match_parent , accurate 大小 if (specMode == MeasureSpec.EXACTLY) { mWidth = specSize; } else { // 由图片决定的宽 int desireByImg = getPaddingLeft() + getPaddingRight() + mImage.getWidth(); // 由字体决定的宽 int desireByTitle = getPaddingLeft() + getPaddingRight() + mTextBound.width(); //wrap_content 大小 if (specMode == MeasureSpec.AT_MOST) { //取小的值 int desire = Math.max(desireByImg, desireByTitle); mWidth = Math.min(desire, specSize); } } //设置高度 specMode = MeasureSpec.getMode(heightMeasureSpec); // match_parent , accurate 大小 if (specMode == MeasureSpec.EXACTLY) { mHeight = specSize; } else { //wrap_content 大小 mHeight = getPaddingTop() + getPaddingBottom() + mImage.getHeight() + mTextBound.height(); } //测量好的大小设置进去 setMeasuredDimension(mWidth, mHeight); } @Override protected void onDraw(Canvas canvas) { // 边框 mPaint.setStrokeWidth(4); mPaint.setStyle(Paint.Style.STROKE); mPaint.setColor(Color.CYAN); canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint); mPaint.setColor(mTextColor); mPaint.setStyle(Style.FILL); //当前设置的宽度小于字体需要的宽度,将字体改为 //绘制文字 if (mTextBound.width() > mWidth) { TextPaint paint = new TextPaint(mPaint); String msg = TextUtils.ellipsize(mTitle, paint, (float) mWidth - getPaddingLeft() - getPaddingRight(), TextUtils.TruncateAt.END).toString(); canvas.drawText(msg, getPaddingLeft(), mHeight - getPaddingBottom(), mPaint); } else { //正常情况,将字体居中 canvas.drawText(mTitle, mWidth / 2 - mTextBound.width() * 1.0f / 2, mHeight - getPaddingBottom(), mPaint); } //计算居中的矩形范围 rect.left = mWidth / 2 - mImage.getWidth() / 2 + getPaddingLeft(); rect.right = mWidth / 2 + mImage.getWidth() / 2 + getPaddingRight(); rect.top = (mHeight - mTextBound.height()) / 2 - mImage.getHeight() / 2 + getPaddingTop(); rect.bottom = (mHeight - mTextBound.height()) / 2 + mImage.getHeight() / 2 + getPaddingBottom(); switch (type) { // 如果是TYPE_CIRCLE绘制圆形 case TYPE_CIRCLE: //圆形宽度和高度应该一致的,所以也要比较一下大小,取小的值 int min = Math.min(mWidth, mHeight); // 圆形宽度和高度如果不一致,按小的值进行压缩 mImage = Bitmap.createScaledBitmap(mImage, min, min, false); //绘制圆形 canvas.drawBitmap(createCircleImage(mImage, min), null, rect, null); break; case TYPE_RECT: canvas.drawBitmap(createRoundConerImage(mImage), null, rect, null); break; } } /** * 根据原图和变长绘制圆形图片 * * @param source * @param min * @return */ private Bitmap createCircleImage(Bitmap source, int min) { final Paint paint = new Paint(); //防止边缘的抗锯齿 paint.setAntiAlias(true); Bitmap target = Bitmap.createBitmap(min, min, Config.ARGB_8888); // 产生一个同样大小的画布 Canvas canvas = new Canvas(target); // 首先绘制圆形,除以2就是半径了 //最主要设置第三个参数为min/2,圆角幅度那么大就是圆形了 canvas.drawCircle(min / 2, min / 2, min/2, paint); // 使用SRC_IN,参考上面的说明---上下层都显示。下层居上显示 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); // 绘制图片 canvas.drawBitmap(source, 0, 0, paint); return target