自定义控件---BitmapShader/Matrix仿CircleImageView
来源:互联网 发布:美团大数据 在哪看 编辑:程序博客网 时间:2024/05/17 09:13
前言
开发中头像的使用,我们会选择CircleImageView。实现圆角图片的方法应该很多,常见的就是利用Xfermode,Shader。下面我们就按照CircleImageView的实现方式,直接继承直接继承ImageView,使用BitmapShader实现圆角的绘制,进行简单的自定义控件
开发准备
BitmapShader
BitmapShader是Shader的子类,可以以下作用:
重复:就是横向、纵向不断重复这个bitmap镜像:横向不断翻转重复,纵向不断翻转重复;拉伸:这个和电脑屏保的模式应该有些不同,这个拉伸的是图片最后的那一个像素;横向的最后一个横行像素,不断的重复,纵项的那一列像素,不断的重复;
实现的方式,是通过构造传入参数确定的
mBitmapShader = new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP);
TileMode的三种参数:
CLAMP 拉伸REPEAT 重复MIRROR 镜像
BitmapShader通过设置给mPaint,然后用这个mPaint绘图时,就会根据你设置的TileMode,对绘制区域进行着色。本例中设置为CLAMP:拉伸。同时为BitmapShader设置一个matrix,去适当的放大或者缩小图片。
开发之路
首先,我们选择继承ImageView进行自定义控件,紧接着我们设计一些自定义属性
<declare-styleable name="RoundImageView"> <attr name="borderRadius" format="dimension"/> <attr name="type" > <!-- 可以设置模式:圆形:圆角矩形 --!> <enum name="circle" value="0"/> <enum name="round" value="1"/> </attr></declare-styleable>
代码获取自定义属性,并初始化画笔和图形处理的Matrix:
public class RoundImageView extends ImageView{ public static final int TYPE_CIRCLE = 0; public static final int TYPE_ROUND = 1; public static final String STATE_INSTANCE = "state_instance"; public static final String STATE_TYPE = "state_type"; public static final String STATE_BORDER_RADIUS = "state_border_radius"; /** * 圆角大小的默认值 */ public static final int BODER_RADIUS_DEFAULT = 10; private int mBorderRadius; private int mType; private int mWidth; /** * 圆角的半径 */ private int mRadius; /** * 渲染图像,使用图像为绘制图形着色 */ private BitmapShader mBitmapShader; /** * 3x3 矩阵,主要用于缩小放大 */ private Matrix mMatrix; /** * 绘图的Paint */ private Paint mBitmapPaint; 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 defStyle) { super(context, attrs, defStyle); TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.RoundImageView,defStyle,0); mBorderRadius = a.getDimensionPixelSize(R.styleable.RoundImageView_borderRadius, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, BODER_RADIUS_DEFAULT, getResources().getDisplayMetrics())); mType = a.getInt(R.styleable.RoundImageView_type, TYPE_CIRCLE); a.recycle(); mMatrix = new Matrix(); mBitmapPaint = new Paint(); }}
重写onMeasure方法,当类型为圆形时,让view的宽和高一致
@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //如果类型是圆形,则强制改变view的宽高一致,以小值为准 if (mType == TYPE_CIRCLE){ mWidth = Math.min(getMeasuredHeight(),getMeasuredWidth()); mRadius = mWidth / 2; setMeasuredDimension(mWidth,mWidth); }}
接下来就是设置BitmapShader
private void setUpShader(){ Drawable drawable = getDrawable(); if (drawable == null){ return; } Bitmap bmp = drawableToBitmap(drawable); // 将bmp作为着色器,就是在指定区域内绘制bmp mBitmapShader = new BitmapShader(bmp, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); float scale = 1.0f; if (mType == TYPE_CIRCLE){ // 拿到bitmap宽或高的小值 int bSize = Math.min(bmp.getWidth(),bmp.getHeight()); scale = mWidth * 1.0f / bSize; }else if (mType == 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);}private Bitmap drawableToBitmap(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(),进行绘制。
@Overrideprotected void onDraw(Canvas canvas) { if (getDrawable() == null){ return; } setUpShader(); if (mType == TYPE_ROUND){ canvas.drawRoundRect(mRoundRect,mBorderRadius,mBorderRadius,mBitmapPaint); }else{ canvas.drawCircle(mRadius,mRadius,mRadius,mBitmapPaint); }}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); if (mType == TYPE_ROUND){ mRoundRect = new RectF(0,0,getWidth(),getHeight()); }}
最后,优化一下:遇到Activity重启,View应该也能尽可能的去保存自己的属性
@Overrideprotected Parcelable onSaveInstanceState() { Bundle bundle = new Bundle(); bundle.putParcelable(STATE_INSTANCE,super.onSaveInstanceState()); bundle.putInt(STATE_TYPE,mType); bundle.putInt(STATE_BORDER_RADIUS,mBorderRadius); return bundle;}@Overrideprotected void onRestoreInstanceState(Parcelable state{ if (state instanceof Bundle){ Bundle bundle = (Bundle) state; super.onRestoreInstanceState((Bundle) ((Bundle) state).getParcelable(STATE_INSTANCE)); this.mType = bundle.getInt(STATE_TYPE); this.mBorderRadius = bundle.getInt(STATE_BORDER_RADIUS); }else{ super.onRestoreInstanceState(state); }}
暴露动态修改圆角大小和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.mType != type){ this.mType = type; if (this.mType != TYPE_ROUND && this.mType != TYPE_CIRCLE){ this.mType = TYPE_CIRCLE; } requestLayout(); }}private int dp2px(int dpVal) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dpVal,getResources().getDisplayMetrics());}
自定义控件已经写完,下面就是如何使用:
xml布局:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:orientation="vertical" android:layout_height="match_parent" > <ScrollView android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:orientation="vertical" android:layout_height="match_parent"> <com.zhang.customview.widget.RoundImageView android:id="@+id/riv_first" android:layout_margin="10dp" android:src="@mipmap/lake" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <com.zhang.customview.widget.RoundImageView android:id="@+id/riv_second" android:layout_margin="10dp" android:src="@mipmap/lake" android:layout_width="wrap_content" app:borderRadius="20dp" app:type="round" android:layout_height="wrap_content" /> <com.zhang.customview.widget.RoundImageView android:layout_margin="10dp" android:src="@mipmap/lake" android:layout_width="200dp" android:layout_height="200dp" /> </LinearLayout> </ScrollView></LinearLayout>
Activity代码:
public class MainActivity extends AppCompatActivity { @BindView(R.id.riv_first) RoundImageView mRivFirst; @BindView(R.id.riv_second) RoundImageView mRivSecond; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); } @OnClick({R.id.riv_first, R.id.riv_second}) public void onClick(View view) { switch (view.getId()) { case R.id.riv_first: mRivFirst.setType(RoundImageView.TYPE_ROUND); break; case R.id.riv_second: mRivSecond.setBorderRadius(90); break; } }}
效果图如下
如还有问题,请查阅:
阅读全文
0 0
- 自定义控件---BitmapShader/Matrix仿CircleImageView
- 自定义圆形圆形控件CircleImageview
- CircleImageView自定义圆形头像控件
- Android自定义控件之CircleImageView
- CircleImageView自定义圆形控件的使用
- git-osc自定义控件之:CircleImageView
- CircleImageView自定义圆形控件的使用
- CircleImageView自定义圆形控件的使
- 自定义圆形图片显示控件CircleImageView
- CircleImageView自定义圆形控件的使用
- Android自定义圆形图片控件CircleImageView
- binbinyang-----CircleImageView自定义圆形控件的使用
- 自定义控件之圆形头像CircleImageView
- CircleImageView自定义圆形控件的使用
- 安卓自定义控件(二)BitmapShader、ShapeDrawable、Shape
- 自定义控件---Shader,Matrix
- android开源系列:CircleImageView自定义圆形控件的使用
- android开源系列:CircleImageView自定义圆形控件的使用
- 嵌入式实训大纲 --上海怡胜信息科技有限公司
- Android Matrix详解
- mysql事务学习
- 关于实现c#多继承的总结
- bitmap用途
- 自定义控件---BitmapShader/Matrix仿CircleImageView
- 使用AndFix实现热修复
- android 分享到YouTube失败 403
- 设计模式之观察者模式
- 嵌入式系统架构
- poj 3904 Sky Code(容斥原理)
- sqlserver存储过程中动态添加链接服务器示例(存储过程操作其他数据库服务器)
- @responseBody注解的使用
- 自定义控件---仿PhotoView实现图片查看