具体自定义控件

Android实现圆形头像效果

2020-03-16  本文已影响0人  秀叶寒冬

效果截图


image.png

实现原理

对于圆形头像的实现,其实就是对方形图像做某些处理,以达到圆形头像的效果。一般我们会通过Canvas和Paint结合来实现这种效果。

自定义View来实现

因为圆形头像是视觉方面的需求,一般我们会考虑能否从自定义View的角度来解决问题。自定义的核心有两点:视觉和交互。视觉由onMeasureonLayoutonDraw这三个方法来完成,而交互则是由dispatchTouchEventonInterceptTouchEventonTouchEvent等这几个方法来控制,只要处理好这几个方法,就能实现形态各异的自定义View了。

方式一 BitmapShader

package com.yds.myapplication;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;

import androidx.appcompat.widget.AppCompatImageView;

/**
 * Created by yds
 * on 2020/3/10.
 */
public class CircleImageView extends AppCompatImageView {

    private int mSize;
    private Paint mPaint;

    public CircleImageView(Context context) {
        this(context,null);
    }

    public CircleImageView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public CircleImageView(Context context,AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init(){
        mPaint = new Paint();
        mPaint.setDither(true);
        mPaint.setAntiAlias(true);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = getMeasuredWidth();
        int height = getMeasuredHeight();
        mSize = Math.min(width,height);  //取宽高的最小值
        setMeasuredDimension(mSize,mSize);    //设置CircleImageView为等宽高
    }

    @Override
    protected void onDraw(Canvas canvas) {
        //获取sourceBitmap,即通过xml或者java设置进来的图片
        Drawable drawable = getDrawable();
        if (drawable == null) return;

        Bitmap sourceBitmap = ((BitmapDrawable)getDrawable()).getBitmap();
        if (sourceBitmap != null){
            //对图片进行缩放,以适应控件的大小
            Bitmap bitmap = resizeBitmap(sourceBitmap,getWidth(),getHeight());
            drawCircleBitmapByShader(canvas,bitmap);    //利用BitmapShader实现
        }
    }

    private Bitmap resizeBitmap(Bitmap sourceBitmap,int dstWidth,int dstHeight){
        int width = sourceBitmap.getWidth();
        int height = sourceBitmap.getHeight();

        float widthScale = ((float)dstWidth) / width;
        float heightScale = ((float)dstHeight) / height;

        //取最大缩放比
        float scale = Math.max(widthScale,heightScale);
        Matrix matrix = new Matrix();
        matrix.postScale(scale,scale);

        return Bitmap.createBitmap(sourceBitmap,0,0,width,height,matrix,true);
    }

    private void drawCircleBitmapByShader(Canvas canvas,Bitmap bitmap){
        BitmapShader shader = new BitmapShader(bitmap,BitmapShader.TileMode.CLAMP,BitmapShader.TileMode.CLAMP);
        mPaint.setShader(shader);
        canvas.drawCircle(mSize / 2,mSize /2 ,mSize / 2,mPaint);
    }
}

方式二 PorterDuffXfermode

package com.yds.myapplication;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.util.AttributeSet;

import androidx.appcompat.widget.AppCompatImageView;

/**
 * Created by yds
 * on 2020/3/10.
 */
public class CircleImageViewPD extends AppCompatImageView {

    private int mWidth;
    private int mHeight;
    private Paint mPaint;
    private Bitmap CircleBitmap;

    public CircleImageViewPD(Context context) {
        super(context);
    }

    public CircleImageViewPD(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CircleImageViewPD(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    private void ImgCircle(){
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setAntiAlias(true);
        paint.setFilterBitmap(true);
        paint.setColor(Color.GRAY);
        paint.setStrokeWidth(5);
        paint.setStyle(Paint.Style.FILL);
        CircleBitmap = Bitmap.createBitmap(mWidth,mHeight, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(CircleBitmap);
        canvas.drawCircle(mWidth/2,mHeight/2,mWidth/2,paint);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWidth = getMeasuredWidth();
        mHeight = getMeasuredHeight();
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setFilterBitmap(true);
        ImgCircle();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        int count = canvas.saveLayerAlpha(0, 0, mWidth, mHeight, 250, Canvas.ALL_SAVE_FLAG);
        super.onDraw(canvas);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
        canvas.drawBitmap(CircleBitmap,0,0, mPaint);
        canvas.restoreToCount(count);
    }
}

方式三 继承自Drawable

package com.yds.myapplication;

import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;

/**
 * Created by yds
 * on 2020/3/10.
 */
public class CircleImageViewDrawable extends Drawable {
    private Paint mPaint;
    private BitmapShader mBitmapShader;
    private int mSize;
    private int mRadius;
    public CircleImageViewDrawable(Bitmap bitmap){
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mBitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP,Shader.TileMode.CLAMP);
        mPaint.setShader(mBitmapShader);
        mSize = Math.min(bitmap.getWidth(),bitmap.getHeight());
        mRadius = mSize/2;
    }
    @Override
    public void draw(Canvas canvas) {
        canvas.drawCircle(mRadius,mRadius,mRadius,mPaint);
    }

    @Override
    public void setAlpha(int alpha) {
        mPaint.setAlpha(alpha);
    }

    @Override
    public void setColorFilter(ColorFilter colorFilter) {
        mPaint.setColorFilter(colorFilter);
    }

    @Override
    public int getOpacity() {
        return PixelFormat.TRANSLUCENT;
    }

    @Override
    public int getIntrinsicHeight() {
        return mSize;
    }

    @Override
    public int getIntrinsicWidth() {
        return mSize;
    }
}

使用

public class MainActivity extends AppCompatActivity {
    private ImageView mImageView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mImageView = findViewById(R.id.img);

        Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.img);
        Drawable drawable = new CircleImageViewDrawable(bitmap);
        mImageView.setImageDrawable(drawable);
    }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">
    <ImageView
        android:id="@+id/img"
        android:layout_width="120dp"
        android:layout_height="120dp" />

    <com.yds.myapplication.CircleImageView
        android:layout_width="120dp"
        android:layout_height="120dp"
        android:src="@drawable/img"/>
    <com.yds.myapplication.CircleImageViewPD
        android:layout_width="120dp"
        android:layout_height="120dp"
        android:src="@drawable/img"/>
</LinearLayout>

地址:https://github.com/ydslib/CircleImageView.git

上一篇下一篇

猜你喜欢

热点阅读