Android BitmapShader实现圆角、圆形ImageView
2017-02-19 15:16
453 查看
项目中,有时候我们会有需要圆角,或者是圆形的ImageView,自身的ImageView不带啊,不像Button可以利用shape来简单的实现圆角啊。。啊。。不要急,小司机来和大家一起实现你的需求。
今天小司机就和大家来利用BitmapShader来实现圆角、圆形ImageView,
本篇博客将会继续按照自定义View四大步骤来写,将会直接继承ImageView结合BitmapShader。开始我们的旅途吧。
这里我们只关注BitmapShader,构造方法:
参数1:bitmap
参数2,参数3:TileMode;
TileMode的取值有三种:
CLAMP 拉伸
REPEAT 重复
MIRROR 镜像
如果大家给电脑屏幕设置屏保的时候,如果图片太小,可以选择重复、拉伸、镜像;
重复:就是横向、纵向不断重复这个bitmap
镜像:横向不断翻转重复,纵向不断翻转重复;
拉伸:这个和电脑屏保的模式应该有些不同,这个拉伸的是图片最后的那一个像素;
横向的最后一个横行像素,不断的重复,纵项的那一列像素,不断的重复;
现在大概明白了,BitmapShader通过设置给mPaint,然后用这个mPaint绘图时,就会根据你设置的TileMode,对绘制区域进行着色。
这里需要注意一点:就是BitmapShader是从你的画布的左上角开始绘制的,不在view的右下角绘制个正方形,它不会在你正方形的左上角开始。
首先对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的代码:
最后在onDraw里面调用initBitmapShader(),然后进行绘制。
代码如下:
存储当前的type以及mBorderRadius
此自定义的ImageView,提供了两个方法,用于动态修改圆角大小和type
存在bug和不足之处,欢迎指出,多谢。
Github地址:https://github.com/Shanlovana/RCImageView
今天小司机就和大家来利用BitmapShader来实现圆角、圆形ImageView,
本篇博客将会继续按照自定义View四大步骤来写,将会直接继承ImageView结合BitmapShader。开始我们的旅途吧。
浅显的的了解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实战
1、自定义属性
values/attr.xml<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="RoundImageView"> <attr name="borderRadius" format="reference|dimension"/> <attr name="type"> <enum name="circle" value="0"/> <enum name="round" value="1"/> </attr> </declare-styleable> </resources>
2、代码中获取自定义属性
基本都加了注释;然后在构造方法中获取了我们的自定义属性,以及部分变量的初始化。public class RoundImageView extends ImageView { private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888; private static final int COLORDRAWABLE_DIMENSION = 2; private int type; //picture type public static final int TYPE_CIRCLE = 0; public static final int TYPE_ROUND = 1; private static final int BODER_RADIUS_DEFAULT = 10; //default rectange border radius private int mBorderRadius; private Paint mBitmapPaint; private int mRadius; //circle radius private Matrix mMatrix; //matrix for scale private BitmapShader mBitmapShader; //bitmapshader private int mWidth; //views width private RectF mRoundRect; public RoundImageView(Context context) { this(context,null); } public RoundImageView(Context context, AttributeSet attrs) { this(context, attrs,0); } public RoundImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.RoundImageView); mBorderRadius = array.getDimensionPixelSize( R.styleable.RoundImageView_borderRadius, (int) TypedValue .applyDimension(TypedValue.COMPLEX_UNIT_DIP, BODER_RADIUS_DEFAULT, getResources() .getDisplayMetrics()));// default is 10 type = array.getInt(R.styleable.RoundImageView_type, TYPE_CIRCLE);// circle default array.recycle(); // init paint and matrix mMatrix = new Matrix(); mBitmapPaint = new Paint(); mBitmapPaint.setAntiAlias(true); }
3、重写onMeasure
代码如下:@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); /** * if the shape is circle ,choose the min in width and height */ if (type == TYPE_CIRCLE) { mWidth = Math.min(getMeasuredWidth(), getMeasuredHeight()); mRadius = mWidth / 2; setMeasuredDimension(mWidth, mWidth); } }
4、初始化BitmapShader
代码如下:// init bitmapshader private void initBitmapShader() { Drawable drawable = getDrawable(); if (drawable == null) { return; } Bitmap bitmap = bitmapToDrawable(drawable); if (bitmap == null) { invalidate(); return; } // use bitmap Aas a shader, is drawn in the specified area mBitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); float scale = 1.0f; if (type == TYPE_CIRCLE) { // get the min of bitmap width or height int bSize = Math.min(bitmap.getWidth(), bitmap.getHeight()); scale = mWidth * 1.0f / bSize; } else if (type == TYPE_ROUND) { if (!(bitmap.getWidth() == getWidth() && bitmap.getHeight() == getHeight())) { /*If the width of the picture or the width of the view does not match the width of the need to calculate the need to scale the scale; zoom picture after the width and height, must be greater than the width of our view; so we take a large value*/ scale = Math.max(getWidth() * 1.0f / bitmap.getWidth(), getHeight() * 1.0f / bitmap.getHeight()); } } // shader transformation matrix, we mainly used here to zoom in or out mMatrix.setScale(scale, scale); // set the transformation matrix mBitmapShader.setLocalMatrix(mMatrix); // set shader mBitmapPaint.setShader(mBitmapShader); }
首先对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的代码:
/** * @param drawable used for convert Bitmap to Drawable */ private Bitmap bitmapToDrawable(Drawable drawable) { if (drawable == null) { return null; } if (drawable instanceof BitmapDrawable) { return ((BitmapDrawable) drawable).getBitmap(); } try { Bitmap bitmap; if (drawable instanceof ColorDrawable) { bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG); } else { bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG); } Canvas canvas = new Canvas(bitmap); drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); drawable.draw(canvas); return bitmap; } catch (Exception e) { e.printStackTrace(); return null; } }
最后在onDraw里面调用initBitmapShader(),然后进行绘制。
5、重写onDraw()
最后一步绘制了,范围,缩放都完成了,只剩下绘制了。代码如下:
@Override protected void onDraw(Canvas canvas) { if (getDrawable() == null) { return; } initBitmapShader(); if (type == TYPE_ROUND) { canvas.drawRoundRect(mRoundRect, mBorderRadius, mBorderRadius, mBitmapPaint); } else // circle { canvas.drawCircle(mRadius, mRadius, mRadius, mBitmapPaint); } }
6、状态的存储与恢复
防止突发事件,用来存储他的状态存储当前的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); } }
此自定义的ImageView,提供了两个方法,用于动态修改圆角大小和type
/*Provide the usual setting method*/ 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()); }
7、使用方法
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:custom="http://schemas.android.com/apk/res-auto" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.shanlovana.rcimageview.MainActivity"> <com.shanlovana.rcimageview.views.RoundImageView android:id="@+id/roundone" android:layout_width="100dp" android:layout_height="100dp" android:src="@drawable/damimi" custom:type="circle"/> <com.shanlovana.rcimageview.views.RoundImageView android:id="@+id/roundtwo" android:layout_marginTop="20dp" android:layout_below="@+id/roundone" android:layout_width="100dp" android:layout_height="100dp" android:src="@drawable/pangdi" custom:type="round"/> <com.shanlovana.rcimageview.views.RoundImageView android:id="@+id/roundthree" android:layout_marginTop="20dp" android:layout_below="@+id/roundtwo" android:layout_width="100dp" android:layout_height="100dp" custom:borderRadius="20dp" android:src="@drawable/luozhengying" custom:type="round"/> <ImageView android:id="@+id/roundfour" android:layout_marginTop="20dp" android:layout_below="@+id/roundthree" android:layout_width="100dp" android:layout_height="100dp" android:scaleType="fitXY" android:src="@drawable/pangdi" /> </RelativeLayout>
存在bug和不足之处,欢迎指出,多谢。
Github地址:https://github.com/Shanlovana/RCImageView
相关文章推荐
- Android BitmapShader 实战 实现圆形、圆角图片(重写ImageView)
- Android使用BitmapShader图形渲染实现圆形、圆角和椭圆自定义图片View
- Android自定义View【实战教程】4⃣️----BitmapShader详解及圆形、圆角、多边形实现
- Android 自定义View修炼-Android实现圆形、圆角和椭圆自定义图片View(使用BitmapShader图形渲染方法)
- Android自定义view实现圆形、圆角和椭圆图片(BitmapShader图形渲染)
- Android BitmapShader 实战 实现圆形、圆角图片
- Android BitmapShader 实战 实现圆形、圆角图片
- Android BitmapShader 实战 实现圆形、圆角图片
- Android BitmapShader实现圆形和圆角图片
- Android圆形图片不求人,自定义View实现(BitmapShader使用)
- Android BitmapShader 实现圆形、圆角图片
- Android BitmapShader 实战 实现圆形、圆角图片
- Android BitmapShader 实战 实现圆形、圆角图片
- Android BitmapShader 实战 实现圆形、圆角图片
- Android BitmapShader实现圆形和圆角图片
- Android BitmapShader 实战 实现圆形、圆角图片
- Android BitmapShader 实战 实现圆形、圆角图片
- Android BitmapShader实现圆形和圆角图片
- Android BitmapShader 实战 实现圆形、圆角图片
- Android BitmapShader 实战 实现圆形、圆角图片