自定义View实战六:实现圆形、圆角图片

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);

mMatrix = new Matrix();

mBitmapPaint = new Paint();

mBitmapPaint.setAntiAlias(true);

mRoundRect = new RectF();

TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RoundImageView);

mBorderRadius = typedArray.getDimensionPixelSize(R.styleable.RoundImageView_borderRadius,

(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_BORDER_RADIUS,

getResources().getDisplayMetrics()));//默认为10dp

mType = typedArray.getInt(R.styleable.RoundImageView_type, TYPE_CIRCLE);//默认为circle

typedArray.recycle();

}

/**

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

*/

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

// LogUtils.e(“onMeasure”);

//如果类型是圆形,则强制改变view的宽高度一致,以最小值为准

if (mType == TYPE_CIRCLE) {

mWidth = Math.min(getMeasuredWidth(), getMeasuredHeight());

mRadius = mWidth / 2;

setMeasuredDimension(mWidth, mWidth);

}

}

@Override

protected 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);

}

}

@Override

protected 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());

}

}

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”;

/**

  • 状态存储

  • @return

*/

@Nullable

@Override

protected 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;

}

/**

  • 状态恢复

  • @param state

*/

@Override

protected void onRestoreInstanceState(Parcelable state) {

if (state instanceof Bundle) {

Bundle bundle = (Bundle) state;

super.onRestoreInstanceState(bundle.getParcelable(STATE_INSTANCE));

this.mType = 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();

}

}

/**

  • 对外公开的方法:动态修改type

*/

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();

}

}

/**

  • 对外公开的方法:改变type

*/

public void changeType() {

if (this.mType == TYPE_ROUND) {

this.mType = TYPE_CIRCLE;

} else if (this.mType == TYPE_CIRCLE) {

this.mType = TYPE_ROUND;

}

requestLayout();

}

public int dp2px(int dpVal) {

return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal,

getResources().getDisplayMetrics());

}

/**

  • 初始化BitmapShader

*/

private void setUpShader() {

Drawable drawable = getDrawable();

if (drawable == null) return;

Bitmap bitmap = drawableToBitmap(drawable);

//将bitmap作为着色器,就是在指定区域绘制bitmap

//Shader在三维软件中我们称之为着色器,其作用嘛就像它的名字一样是来给图像着色的或者更通俗的说法是上色

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

float scale = 1.0f;

if (mType == TYPE_CIRCLE) {

//拿到bitmap宽或高的最小值

int bitmapSize = Math.min(bitmap.getWidth(), bitmap.getHeight());

scale = mWidth * 1.0f / bitmapSize;

} else if (mType == TYPE_ROUND) {

//如果图片的宽或高与view的宽高不匹配,计算出需要缩放的比例;缩放后的图片的宽高,一定要大于我们

// view的宽高,所以我们这里取大值

scale = Math.max(getWidth() * 1.0f / bitmap.getWidth(), getHeight() * 1.0f / bitmap.getHeight());

}

//shader的变换矩阵,我们这里主要用于放大或缩小

mMatrix.setScale(scale, scale);

//设置变换矩阵

mBitmapShader.setLocalMatrix(mMatrix);

//设置shader

mBitmapPaint.setShader(mBitmapShader);

}

/**

  • drawable转bitmap

*/

private Bitmap drawableToBitmap(Drawable drawable) {

if (drawable instanceof BitmapDrawable) {

BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;

return bitmapDrawable.getBitmap();

}

int width = drawable.getIntrinsicWidth();

int height = drawable.getIntrinsicHeight();

Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);

Canvas canvas = new Canvas(bitmap);

drawable.setBounds(0, 0, width, height);

drawable.draw(canvas);

return bitmap;

}

}

1.2、RoundImageViewActivity.java


public class RoundImageViewActivity extends AppCompatActivity {

@BindView(R.id.round_image_view01)

RoundImageView roundImageView01;

@BindView(R.id.round_image_view02)

RoundImageView roundImageView02;

@BindView(R.id.round_image_view03)

RoundImageView roundImageView03;

@BindView(R.id.round_image_view04)

RoundImageView roundImageView04;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_round_image_view);

ButterKnife.bind(this);

}

@OnClick({R.id.round_image_view01, R.id.round_image_view02, R.id.round_image_view03, R.id.round_image_view04})

public void onViewClicked(View view) {

switch (view.getId()) {

case R.id.round_image_view01:

roundImageView01.changeType();

break;

case R.id.round_image_view02:

roundImageView02.setType(RoundImageView.TYPE_CIRCLE);

break;

case R.id.round_image_view03:

break;

case R.id.round_image_view04:

roundImageView04.setBorderRadius(90);

break;

}

}

}

1.3、activity_round_image_view.xml


<ScrollView xmlns:android=“http://schemas.android.com/apk/res/android”

xmlns:app=“http://schemas.android.com/apk/res-auto”

android:layout_width=“match_parent”

android:layout_height=“wrap_content”

android:background=“@color/light_blue_2e”

<LinearLayout

android:layout_width=“match_parent”

android:layout_height=“wrap_content”

android:orientation=“vertical”>

<com.gs.common3.aView.customView.roundIV.RoundImageView

android:id=“@+id/round_image_view01”

android:layout_width=“match_parent”

android:layout_height=“wrap_content”

android:src=“@mipmap/keji01”/>

<com.gs.common3.aView.customView.roundIV.RoundImageView

android:id=“@+id/round_image_view02”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:layout_marginTop=“10dp”

android:src=“@mipmap/keji01”

app:borderRadius=“60dp”

app:type=“round”/>

<com.gs.common3.aView.customView.roundIV.RoundImageView

android:id=“@+id/round_image_view03”

android:layout_width=“200dp”

android:layout_height=“200dp”

android:layout_margin=“10dp”

android:src=“@mipmap/keji02”/>

<com.gs.common3.aView.customView.roundIV.RoundImageView

android:id=“@+id/round_image_view04”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”

android:layout_margin=“10dp”

android:src=“@mipmap/keji02”

app:borderRadius=“20dp”

app:type=“round”/>

2、文章二:

======

按照文章二编码:

效果如下:有问题。

2.1、RoundIVByXfermode.java


public class RoundIVByXfermode extends AppCompatImageView {

private Paint mPaint;

private Xfermode mXfermode;

private Bitmap mMaskBitmap;

private WeakReference mWeakBitmap;

//图片的类型,圆形or圆角

private int type;

public static final int TYPE_CIRCLE = 0;

public static final int TYPE_ROUND = 1;

//圆角大小的默认值

private static final int BORDER_RADIUS_DEFAULT = 10;

//圆角的大小

private int mBorderRadius;

public RoundIVByXfermode(Context context) {

this(context, null);

mPaint = new Paint();

mPaint.setAntiAlias(true);

}

public RoundIVByXfermode(Context context, AttributeSet attrs) {

super(context, attrs);

mPaint = new Paint();

mPaint.setAntiAlias(true);

TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RoundIVByXfermode);

mBorderRadius = (int) typedArray.getDimension(R.styleable.RoundIVByXfermode_borderRadius,

TypedValue.applyDimension(

TypedValue.COMPLEX_UNIT_DIP, BORDER_RADIUS_DEFAULT,

getResources().getDisplayMetrics()));//默认为10dp

type = typedArray.getInt(R.styleable.RoundIVByXfermode_type, TYPE_CIRCLE);//默认为circle

typedArray.recycle();

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

//如果类型是圆形,则强制改变view的宽高一致,以最小值为准

if (type == TYPE_CIRCLE) {

int width = Math.min(getMeasuredWidth(), getMeasuredHeight());

setMeasuredDimension(width, width);

}

}

@SuppressLint(“DrawAllocation”)

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

//在缓存中取出bitmap

Bitmap bitmap = mWeakBitmap == null ? null : mWeakBitmap.get();

if (bitmap == null || bitmap.isRecycled()) {

//拿到drawable

Drawable drawable = getDrawable();

//获取drawable的宽和高

int dWidth = drawable.getIntrinsicWidth();

int dHeight = drawable.getIntrinsicHeight();

if (drawable != null) {

//创建bitmap

bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);

float scale = 1.0f;

//创建画布

Canvas drawCanvas = new Canvas(bitmap);

//按照bitmap的宽高,以及view的宽高,计算缩放比例;因为设置的src宽高比例可能和

// imageview的宽高比例不同,这里我们不希望图片失真;

if (type == TYPE_ROUND) {

// 如果图片的宽或者高与view的宽高不匹配,计算出需要缩放的比例;缩放后的图片的宽高,

// 一定要大于我们view的宽高;所以我们这里取大值;

scale = Math.max(getWidth() * 1.0f / dWidth, getHeight() * 1.0f / dHeight);

} else {

scale = getWidth() * 1.0f / Math.min(dWidth, dHeight);

}

//根据缩放比例,设置bounds,相当于缩放图片了

drawable.setBounds(0, 0, (int) scale * dWidth,

(int) scale * dHeight);

学习宝典

对我们开发者来说,一定要打好基础,随时准备战斗。不论寒冬是否到来,都要把自己的技术做精做深。虽然目前移动端的招聘量确实变少了,但中高端的职位还是很多的,这说明行业只是变得成熟规范起来了。竞争越激烈,产品质量与留存就变得更加重要,我们进入了技术赋能业务的时代。

不论遇到什么困难,都不应该成为我们放弃的理由!

很多人在刚接触这个行业的时候或者是在遇到瓶颈期的时候,总会遇到一些问题,比如学了一段时间感觉没有方向感,不知道该从那里入手去学习,对此我针对Android程序员,我这边给大家整理了一套学习宝典!包括不限于高级UI、性能优化、移动架构师、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!

【Android核心高级技术PDF文档,BAT大厂面试真题解析】

【算法合集】

【延伸Android必备知识点】

【Android部分高级架构视频学习资源】
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
setBounds(0, 0, (int) scale * dWidth,

(int) scale * dHeight);

学习宝典

对我们开发者来说,一定要打好基础,随时准备战斗。不论寒冬是否到来,都要把自己的技术做精做深。虽然目前移动端的招聘量确实变少了,但中高端的职位还是很多的,这说明行业只是变得成熟规范起来了。竞争越激烈,产品质量与留存就变得更加重要,我们进入了技术赋能业务的时代。

不论遇到什么困难,都不应该成为我们放弃的理由!

很多人在刚接触这个行业的时候或者是在遇到瓶颈期的时候,总会遇到一些问题,比如学了一段时间感觉没有方向感,不知道该从那里入手去学习,对此我针对Android程序员,我这边给大家整理了一套学习宝典!包括不限于高级UI、性能优化、移动架构师、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!

【Android核心高级技术PDF文档,BAT大厂面试真题解析】

[外链图片转存中…(img-SmghcllN-1715364052759)]

【算法合集】

[外链图片转存中…(img-3ElKe6Td-1715364052759)]

【延伸Android必备知识点】

[外链图片转存中…(img-L5lql92K-1715364052760)]

【Android部分高级架构视频学习资源】
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值