public BitmapShader(Bitmap bitmap,Shader.TileMode tileX,Shader.TileMode tileY)
调用这个类来产生一个画有一个位图的渲染器(Shader)。
bitmap:在渲染器内使用的位图
(1)tileX:The tiling mode for x to draw the bitmap in. 在位图上X方向花砖模式
(2)tileY:The tiling mode for y to draw the bitmap in. 在位图上Y方向花砖模式
TileMode:(一共有三种)
(1)CLAMP:如果渲染器超出原始边界范围,会复制范围内边缘染色。
(2)REPEAT:横向和纵向的重复渲染器图片,平铺。
(3)MIRROR:横向和纵向的重复渲染器图片,这个和REPEAT 重复方式不一样,他是以镜像方式平铺。
还是不太明白?那看一下效果图吧!
对于我们的圆角,以及圆形,我们设置的模式都是CLAMP ,但是你会不会会有一个疑问:
vIEw的宽或者高大于我们的bitmap宽或者高岂不是会拉伸?
嗯,我们会为BitmapShader设置一个matrix,去适当的放大或者缩小图片,不会让“ vIEw的宽或者高大于我们的bitmap宽或者高 ”此条件成立的。
到此我们的原理基本介绍完毕了,拿到drawable转化为bitmap,然后直接初始化BitmapShader,画笔设置Shader,最后在onDraw里面进行画圆就行了。
基本用法及实例
首先就来看看利用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);
接下来,根据类型以及bitmap和vIEw的宽高,计算scale;
关于scale的计算:
(1)圆形时:取bitmap的宽或者高的小值作为基准,如果采用大值,缩放后肯定不能填满我们的圆形区域。然后,vIEw的mWIDth/bSize ; 得到的就是scale。
(2)圆角时:因为设计到宽/高比例,我们分别getWIDth() * 1.0f / bmp.getWIDth() 和 getHeight() * 1.0f / bmp.getHeight() ;最终取大值,因为我们要让最终缩放完成的图片一定要大于我们的vIEw的区域,有点类似centerCrop;
(3)比如: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,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,mBitmapPaint); } else { canvas.drawCircle(mRadius,mRadius,mBitmapPaint); // drawSomeThing(canvas); } } @OverrIDe protected voID onSizeChanged(int w,int h,int olDW,int oldh) { super.onSizeChanged(w,olDW,oldh); // 圆角图片的范围 if (type == TYPE_ROUND) mRoundRect = new RectF(0,getWIDth(),getHeight()); }
绘制就很简单了,画个圆,圆角矩形什么的。圆角矩形的限定范围mRoundRect在onSizeChanged里面进行了初始化。
6、状态的存储与恢复
当然了,如果内存不足,而恰好我们的Activity置于后台,不幸被重启,或者用户旋转屏幕造成Activity重启,我们的VIEw应该也能尽可能的去保存自己的属性。
状态保存什么用处呢?比如,现在一个的圆角大小是10dp,用户点击后变成50dp;当用户旋转以后,或者长时间置于后台以后,返回我们的Activity应该还是50dp;
我们简单的存储一下,当前的type以及mborderRadius
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; } @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); } }
代码比较简单。我们文章中的demo中,第一个,第四个是可以点击的,点击后会发生变化,你可以点击后,然后旋转屏幕进行测试。
同时我们也对外公布了两个方法,用于动态修改圆角大小和type
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_ROUND && this.type != TYPE_CIRCLE) { this.type = TYPE_CIRCLE; } requestLayout(); } } public int dp2px(int dpVal) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dpVal,getResources().getdisplayMetrics()); }
最后贴一下我们的布局文件和MainActivity。
6、调用
布局文件:
<ScrollVIEw xmlns:androID="http://schemas.androID.com/apk/res/androID" xmlns:tools="http://schemas.androID.com/tools" xmlns:zhy="http://schemas.androID.com/apk/res/com.zhy.varIoUsshapeimagevIEw" androID:layout_wIDth="match_parent" androID:layout_height="wrap_content" > <linearLayout androID:layout_wIDth="match_parent" androID:layout_height="match_parent" androID:orIEntation="vertical" > <com.zhy.vIEw.RoundImageVIEw androID:ID="@+ID/ID_qiqiu" androID:layout_wIDth="wrap_content" androID:layout_height="wrap_content" androID:layout_margin="10dp" androID:src="@drawable/qiqiu" > </com.zhy.vIEw.RoundImageVIEw> <com.zhy.vIEw.RoundImageVIEw androID:layout_wIDth="200dp" androID:layout_height="200dp" androID:layout_margin="10dp" androID:src="@drawable/aa" > </com.zhy.vIEw.RoundImageVIEw> <com.zhy.vIEw.RoundImageVIEw androID:layout_wIDth="wrap_content" androID:layout_height="wrap_content" androID:layout_margin="10dp" androID:src="@drawable/icon" > </com.zhy.vIEw.RoundImageVIEw> <com.zhy.vIEw.RoundImageVIEw androID:ID="@+ID/ID_meinv" androID:layout_wIDth="wrap_content" androID:layout_height="wrap_content" androID:layout_margin="10dp" androID:src="@drawable/aa" zhy:borderRadius="20dp" zhy:type="round" > </com.zhy.vIEw.RoundImageVIEw> <com.zhy.vIEw.RoundImageVIEw androID:layout_wIDth="wrap_content" androID:layout_height="wrap_content" androID:layout_margin="10dp" androID:src="@drawable/icon" zhy:borderRadius="40dp" zhy:type="round" > </com.zhy.vIEw.RoundImageVIEw> <com.zhy.vIEw.RoundImageVIEw androID:layout_wIDth="wrap_content" androID:layout_height="wrap_content" androID:layout_margin="10dp" androID:src="@drawable/qiqiu" zhy:borderRadius="60dp" zhy:type="round" > </com.zhy.vIEw.RoundImageVIEw> </linearLayout> </ScrollVIEw>
没撒,ScrollVIEw里面一个线性布局,里面一堆RoundImageVIEw。
MainActivity
package com.zhy.varIoUsshapeimagevIEw; import androID.app.Activity; import androID.os.Bundle; import androID.vIEw.VIEw; import androID.vIEw.VIEw.OnClickListener; import com.zhy.vIEw.RoundImageVIEw; public class MainActivity extends Activity { private RoundImageVIEw mQiQiu; private RoundImageVIEw mMeiNv ; @OverrIDe protected voID onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentVIEw(R.layout.activity_main); mQiQiu = (RoundImageVIEw) findVIEwByID(R.ID.ID_qiqiu); mMeiNv = (RoundImageVIEw) findVIEwByID(R.ID.ID_meinv); mQiQiu.setonClickListener(new OnClickListener() { @OverrIDe public voID onClick(VIEw v) { mQiQiu.setType(RoundImageVIEw.TYPE_ROUND); } }); mMeiNv.setonClickListener(new OnClickListener() { @OverrIDe public voID onClick(VIEw v) { mMeiNv.setborderRadius(90); } }); } }
最后的效果图:
以上是内存溢出为你收集整理的Android中使用BitmapShader类来制作各种图片的圆角全部内容,希望文章能够帮你解决Android中使用BitmapShader类来制作各种图片的圆角所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)