ImageView 绘制圆角效果_BitmapShader

前言

之前我们已经提到过,我们可以使用两种方式来实现圆角图片的效果。一种是使用Xfermode,另一种是BitmapShader来实现。下面我将介绍BitmapShader用法。

使用BitmapShader的方式实现

1.自定义属性在attrs.xml文件中

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <attr name="borderRadius" format="dimension" />

    <attr name="type">
        <enum name="circle" value="0"/>
        <enum name="round" value="1"/>
    </attr>
    <declare-styleable name="RoundImageView">
        <attr name="borderRadius"/>
        <attr name="type"/>
    </declare-styleable>

</resources>

2.自定义ImageView

public class RoundImageView extends ImageView {

    private int type;
    private static final int TYPE_CIRCLE = 0;
    private static final int TYPE_ROUND = 1;

    //圆形半径大小
    private int mRadius;

    //圆角矩形半径大小
    private int mBorderRadius;
    //圆角默认值
    private static final int BODER_RADIUS_DEFAULT = 10;

    private Paint mPaint;

    //矩阵用来缩放
    private Matrix mMatrix;

    private BitmapShader mBitmapShader;

    private RectF mRectF;

    public RoundImageView(Context context, AttributeSet attrs) {
        super(context, attrs);

        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mMatrix = new Matrix();

        //获取自定义的属性
        TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.RoundImageView);
        int defvalue = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,BODER_RADIUS_DEFAULT,getResources().getDisplayMetrics());
        mBorderRadius = a.getDimensionPixelSize(R.styleable.RoundImageView_borderRadius,defvalue);
        type = a.getInt(R.styleable.RoundImageView_type,TYPE_CIRCLE);
        a.recycle();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        //为圆形图片,所以应该让宽高保持一致,获得半径大小
        if (type == TYPE_CIRCLE){
            int size = Math.min(getMeasuredWidth(), getMeasuredHeight());
            mRadius = size / 2;
            setMeasuredDimension(size, size);
        }

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mRectF = new RectF(0,0,getWidth(),getHeight());
    }

    @Override
    protected void onDraw(Canvas canvas) {

        if (getDrawable() == null){
            return;
        }
        setUpShader();
        if (type == TYPE_ROUND){
            canvas.drawRoundRect(mRectF,mBorderRadius,mBorderRadius,mPaint);
        }else {
            canvas.drawCircle(mRadius,mRadius,mRadius,mPaint);
        }

    }

    private void setUpShader() {
        Drawable drawable = getDrawable();
        if (drawable == null){
            return;
        }
        Bitmap bitmap = drawableToBitmap(drawable);

        //初始化BitmapShader,传入bitmap对象,在指定区域内绘制bitmap
        mBitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

        //计算缩放比例
        float scale = 1.0f;
        if (type == TYPE_CIRCLE){
            //根据最小宽高,设置比例
            int size = Math.min(bitmap.getWidth(),bitmap.getHeight());
            scale = mRadius * 2.0f/ size;
        }else if (type == TYPE_ROUND){
            // 如果图片的宽或者高与view的宽高不匹配,计算出需要缩放的比例;
            // 缩放后的图片的宽高,一定要大于我们view的宽高;所以我们这里取大值
            scale = Math.max(getMeasuredWidth() * 1.0f / bitmap.getWidth(), getMeasuredHeight() * 1.0f / bitmap.getHeight());
        }
        //shader的变换矩阵,用于放大或者缩小
        mMatrix.setScale(scale,scale);
        mBitmapShader.setLocalMatrix(mMatrix);

        mPaint.setShader(mBitmapShader);
    }

    /**
     * 将drawable 转化为 Bitmap
     * @param drawable
     * @return
     */
    private Bitmap drawableToBitmap(Drawable drawable) {
        if (drawable instanceof BitmapDrawable){
            BitmapDrawable bitmapDrawable = (BitmapDrawable)drawable;
            return bitmapDrawable.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;
    }

}

3.布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:roundview="http://schemas.android.com/apk/res-auto"
              xmlns:tools="http://schemas.android.com/tools"
              android:id="@+id/activity_main"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical"
              android:padding="10dp">


    <mo.yumf.com.myviews.RoundImageView
        android:id="@+id/Roundview"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_marginTop="20dp"
        roundview:borderRadius="5dp"
        roundview:type="round"/>

    <mo.yumf.com.myviews.RoundImageView
        android:id="@+id/Circleview"
        android:layout_width="200dp"
        android:layout_height="100dp"
        roundview:type="circle"
        android:layout_marginTop="20dp"/>

</LinearLayout>

关于BitmapShader类
BitmapShader是Shader的子类,可以通过Paint.setShader(Shader shader)来设置。构造方法:
BitmapShader mBitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

TileMode的取值有三种:
CLAMP :拉伸,这个拉伸的是图片最后的那一个像素;横向的最后一个横行像素,不断的重复,纵项的那一列像素,不断的重复;
REPEAT: 重复,就是横向、纵向不断重复这个bitmap;
MIRROR :镜像,横向不断翻转重复,纵向不断翻转重复;

BitmapShader通过设置给mPaint,然后用这个mPaint绘图时,就会根据你设置的TileMode,对绘制区域进行着色。
这里需要注意一点:就是BitmapShader是从你的画布的左上角开始绘制的,不在view的右下角绘制个正方形,它不会在你正方形的左上角开始。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章