Personal blog
www.milovetingting.cn
Android custom View- round picture control
preface
In daily development, circular image effects are still very common. This can be done by setting Xfermode for Paint, as briefly noted below.
implementation
The core to achieve the circular effect is PorterDuffXfermode. For PorterDuffXfermode, it is not expanded here and relevant information can be queried.
The core code
// Draw the background
canvas.drawCircle(mSize / 2, mSize / 2, mSize / 2, mPaint);
// Set the mode to: display the intersection of the background layer and the upper layer, and display the upper layer image
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
// Draw the image to display
canvas.drawBitmap(mSrcBitmap, 0.0, mPaint);
/ / reset Xfermode
mPaint.setXfermode(null);
Copy the code
Custom attributes
<?xml version="1.0" encoding="utf-8"? >
<resources>
<declare-styleable name="CircleView">
<! -- Define resources -->
<attr name="src" format="reference" />
<! -- Define type -->
<attr name="type" format="enum">
<! -- the round -- -- >
<enum name="round" value="1" />
<! - rectangular - >
<enum name="rect" value="2" />
</attr>
</declare-styleable>
</resources>
Copy the code
Custom control
public class CircleView extends View {
private static final int DEFAULT_SIZE = 200;
private static final int DEFAULT_RADIUS = 20;
private static final int TYPE_ROUND = 1;
private static final int TYPE_RECT = 2;
private int mSize;
private int mResourceId;
private int mType;
private Paint mPaint;
private Bitmap mSrcBitmap;
public CircleView(Context context) {
this(context, null);
}
public CircleView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CircleView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CircleView);
mResourceId = ta.getResourceId(R.styleable.CircleView_src, R.mipmap.ic_launcher);
mType = ta.getInt(R.styleable.CircleView_type, TYPE_ROUND);
ta.recycle();
init();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = getMeasureSize(widthMeasureSpec);
int height = getMeasureSize(heightMeasureSpec);
mSize = Math.min(width, height);
setMeasuredDimension(mSize, mSize);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Draw the background
if (mSrcBitmap == null) {
mSrcBitmap = getScaleBitmap();
}
if (mType == TYPE_ROUND) {
canvas.drawCircle(mSize / 2, mSize / 2, mSize / 2, mPaint);
} else if (mType == TYPE_RECT) {
canvas.drawRoundRect(0.0, mSize, mSize, DEFAULT_RADIUS, DEFAULT_RADIUS, mPaint);
}
// Set the mode to: display the intersection of the background layer and the upper layer, and display the upper layer image
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
// Draw the image to display
canvas.drawBitmap(mSrcBitmap, 0.0, mPaint);
/ / reset Xfermode
mPaint.setXfermode(null);
}
private void init(a) {
// Disable hardware acceleration, otherwise circles may not be drawn
setLayerType(LAYER_TYPE_HARDWARE, null);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.FILL);
}
private int getMeasureSize(int measureSpec) {
int mode = MeasureSpec.getMode(measureSpec);
int size = MeasureSpec.getSize(measureSpec);
return mode == MeasureSpec.EXACTLY ? size : DEFAULT_SIZE;
}
/** * Get the scaled Bitmap **@return* /
private Bitmap getScaleBitmap(a) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), mResourceId, options);
options.inSampleSize = calcSampleSize(options, mSize, mSize);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(getResources(), mResourceId, options);
}
/** * Computes the scale **@param option
* @param width
* @param height
* @return* /
private int calcSampleSize(BitmapFactory.Options option, int width, int height) {
int originWidth = option.outWidth;
int originHeight = option.outHeight;
int sampleSize = 1;
while ((originWidth = originWidth >> 1) > width && (originHeight = originHeight >> 1) > height) {
sampleSize = sampleSize << 1;
}
returnsampleSize; }}Copy the code
Note: If there is no circular effect, then hardware acceleration may need to be disabled: setLayerType(LAYER_TYPE_HARDWARE, null)
layout
<?xml version="1.0" encoding="utf-8"? >
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
tools:context=".MainActivity">
<com.wangyz.custom.CircleView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
app:src="@drawable/image" />
<com.wangyz.custom.CircleView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="10dp"
app:src="@drawable/image" />
<com.wangyz.custom.CircleView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="10dp"
app:src="@drawable/image"
app:type="rect" />
</LinearLayout>
Copy the code