转载请标明出处: http://blog.csdn.net/lmj623565791/article/details/41967509,本文出自: 【张鸿洋的博客】

1、概述

记得初学那会写过一篇博客Android 完美实现图片圆角和圆形(对实现进行分析),主要是个自定View加上使用Xfermode实现的。其实实现圆角图片的方法应该很多,常见的就是利用Xfermode,Shader。本篇博客会直接继承直接继承ImageView,使用BitmapShader实现圆角的绘制,大家如果耐着性子看完,我估计什么形状都能绘制出来。

2、效果图

这是圆角的一个演示图~~这个没什么说的,直接设置的圆角的大小就行;



这是圆形的显示图,这里需要注意下,因为设置的图片可能是长方形,例如上图:有两个长方形,一个宽比较大,一个高比较大;

那么我们希望显示成圆形,我们可能就要对其进行放大或者缩小(因为图片的宽可能不满足设置的边长,而高超出,此时我们就需要放大其宽度)。



这个一张图,中间是正常尺寸;上下分别为特大特小,主要可以当尺寸大于或者小于设置尺寸,我们需要对其放大或者缩小;

圆角时如果图片与view的宽高不一致,也需要进行放大缩小,这里就不截图了,代码里面看吧。

3、浅谈BitmapShader

BitmapShader是Shader的子类,可以通过Paint.setShader(Shader shader)进行设置、

这里我们只关注BitmapShader,构造方法:

mBitmapShader = new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP);

参数1:bitmap

参数2,参数3:TileMode;

TileMode的取值有三种:

CLAMP 拉伸

REPEAT 重复

MIRROR 镜像

如果大家给电脑屏幕设置屏保的时候,如果图片太小,可以选择重复、拉伸、镜像;

重复:就是横向、纵向不断重复这个bitmap

镜像:横向不断翻转重复,纵向不断翻转重复;

拉伸:这个和电脑屏保的模式应该有些不同,这个拉伸的是图片最后的那一个像素;横向的最后一个横行像素,不断的重复,纵项的那一列像素,不断的重复;

现在大概明白了,BitmapShader通过设置给mPaint,然后用这个mPaint绘图时,就会根据你设置的TileMode,对绘制区域进行着色。

这里需要注意一点:就是BitmapShader是从你的画布的左上角开始绘制的,不在view的右下角绘制个正方形,它不会在你正方形的左上角开始。

好了,到此,我相信大家对BitmapShader有了一定的了解了;当然了,如果你希望对Shader充分的了解,请参考爱歌的神作:自定义控件其实很简单1/3

对于我们的圆角,以及圆形,我们设置的模式都是CLAMP ,但是你会不会会有一个疑问:

view的宽或者高大于我们的bitmap宽或者高岂不是会拉伸?

嗯,我们会为BitmapShader设置一个matrix,去适当的放大或者缩小图片,不会让“view的宽或者高大于我们的bitmap宽或者高 ”此条件成立的。

到此我们的原理基本介绍完毕了,拿到drawable转化为bitmap,然后直接初始化BitmapShader,画笔设置Shader,最后在onDraw里面进行画圆就行了。

4、BitmapShader实战

首先就来看看利用BitmapShader实现的圆形或者圆角。

我们这里直接继承ImageView,这样大家设置图片的代码会比较熟悉;但是我们需要支持两种模式,那么就需要自定义属性了:

1、自定义属性

values/attr.xml

<?xml version="1.0" encoding="utf-8"?><resources>

<attr name="borderRadius" format="dimension" />
<attr name="type">
<enum name="circle" value="0" />
<enum name="round" value="1" />
</attr>


<declare-styleable name="RoundImageView">
<attr name="borderRadius" />
<attr name="type" />
</declare-styleable>

</resources>

我们定义了一个枚举和一个圆角的大小borderRadius。


2、获取自定义属性

public class RoundImageView extends ImageView{	/**	 * 图片的类型,圆形or圆角	 */	private int type;	private static final int TYPE_CIRCLE = 0;	private static final int TYPE_ROUND = 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 RoundImageView(Context context, AttributeSet attrs)	{		super(context, attrs);		mMatrix = new Matrix();		mBitmapPaint = new Paint();		mBitmapPaint.setAntiAlias(true);		TypedArray a = context.obtainStyledAttributes(attrs,				R.styleable.RoundImageView);		mBorderRadius = a.getDimensionPixelSize(				R.styleable.RoundImageView_borderRadius, (int) TypedValue						.applyDimension(TypedValue.COMPLEX_UNIT_DIP,								BODER_RADIUS_DEFAULT, getResources()										.getDisplayMetrics()));// 默认为10dp		type = a.getInt(R.styleable.RoundImageView_type, TYPE_CIRCLE);// 默认为Circle		a.recycle();	}

可以看到我们的一些成员变量,基本都加了注释;然后在构造方法中获取了我们的自定义属性,以及部分变量的初始化。


3、onMeasure

@Override	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)	{		Log.e("TAG", "onMeasure");		super.onMeasure(widthMeasureSpec, heightMeasureSpec);		/**		 * 如果类型是圆形,则强制改变view的宽高一致,以小值为准		 */		if (type == TYPE_CIRCLE)		{			mWidth = Math.min(getMeasuredWidth(), getMeasuredHeight());			mRadius = mWidth / 2;			setMeasuredDimension(mWidth, mWidth);		}	}

我们复写了onMeasure方法,主要用于当设置类型为圆形时,我们强制让view的宽和高一致。

接下来只剩下设置BitmapShader和绘制了


4、设置BitmapShader

/**	 * 初始化BitmapShader	 */	private void setUpShader()	{		Drawable drawable = getDrawable();		if (drawable == null)		{			return;		}		Bitmap bmp = drawableToBitamp(drawable);		// 将bmp作为着色器,就是在指定区域内绘制bmp		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_ROUND)		{			// 如果图片的宽或者高与view的宽高不匹配,计算出需要缩放的比例;缩放后的图片的宽高,一定要大于我们view的宽高;所以我们这里取大值;			scale = Math.max(getWidth() * 1.0f / bmp.getWidth(), getHeight()					* 1.0f / bmp.getHeight());		}		// shader的变换矩阵,我们这里主要用于放大或者缩小		mMatrix.setScale(scale, scale);		// 设置变换矩阵		mBitmapShader.setLocalMatrix(mMatrix);		// 设置shader		mBitmapPaint.setShader(mBitmapShader);	}

在setUpShader中,首先对drawable转化为我们的bitmap;

然后初始化mBitmapShader = new BitmapShader(bmp, TileMode.CLAMP, TileMode.CLAMP);

接下来,根据类型以及bitmap和view的宽高,计算scale;

关于scale的计算:

圆形时:取bitmap的宽或者高的小值作为基准,如果采用大值,缩放后肯定不能填满我们的圆形区域。然后,view的mWidth/bSize ; 得到的就是scale。

圆角时:因为设计到宽/高比例,我们分别getWidth() * 1.0f / bmp.getWidth() 和getHeight()* 1.0f / bmp.getHeight();最终取大值,因为我们要让最终缩放完成的图片一定要大于我们的view的区域,有点类似centerCrop;

比如:view的宽高为10*20;图片的宽高为5*100 ; 最终我们应该按照宽的比例放大,而不是按照高的比例缩小;因为我们需要让缩放后的图片,自定大于我们的view宽高,并保证原图比例。

有了scale,就可以设置给我们的matrix;

然后使用mBitmapShader.setLocalMatrix(mMatrix);

最后将bitmapShader设置给paint。

关于drawable转bitmap的代码:

/**	 * drawable转bitmap	 * 	 * @param drawable	 * @return	 */	private Bitmap drawableToBitamp(Drawable 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;	}

最后我们会在onDraw里面调用setUpShader(),然后进行绘制。

5、绘制

到此,就剩下最后一步绘制了,因为我们的范围,以及缩放都完成了,所以真的只剩下绘制了。

@Override	protected void onDraw(Canvas canvas)	{		if (getDrawable() == null)		{			return;		}		setUpShader();		if (type == TYPE_ROUND)		{			canvas.drawRoundRect(mRoundRect, mBorderRadius, mBorderRadius,					mBitmapPaint);		} else		{			canvas.drawCircle(mRadius, mRadius, mRadius, mBitmapPaint);			// drawSomeThing(canvas);		}	}		@Override	protected void onSizeChanged(int w, int h, int oldw, int oldh)	{		super.onSizeChanged(w, h, oldw, oldh);		// 圆角图片的范围		if (type == TYPE_ROUND)			mRoundRect = new RectF(0, 0, getWidth(), getHeight());	}

绘制就很简单了,画个圆,圆角矩形什么的。圆角矩形的限定范围mRoundRect在onSizeChanged里面进行了初始化。

5、状态的存储与恢复

当然了,如果内存不足,而恰好我们的Activity置于后台,不幸被重启,或者用户旋转屏幕造成Activity重启,我们的View应该也能尽可能的去保存自己的属性。

更多相关文章

  1. Android多线程下载远程图片【转】
  2. Android界面的.9.png图片显示出错,怎么回事啊?
  3. 关于Android4.0之上的ListView显示从网络上获取图片和文字
  4. Android 图片添加水印图片或者文字
  5. 自定义Button形状(圆形、椭圆)
  6. 拍照后获取不了图片!
  7. Android——播放器和图片轮播
  8. Android实现非本地图片的点击效果
  9. Android自定义Toast带图片的

随机推荐

  1. 7个Android应用程序源代码
  2. android图片转换代码
  3. Android监听屏幕锁屏
  4. Android 中使用MediaRecorder进行录像详
  5. 一个android通信录的源代码
  6. Android在内存中读取数据
  7. Android: change app names
  8. Android7.0中文文档(API)-- AutoCompleteTe
  9. android 进程通信
  10. Android右箭头的显示文字的View