Bitmap擷取中間正方形並取出圓形圖片

soft_po發表於2016-10-29

自定義View繼承ImageView

/**
 * Created by softpo on 2016/10/29.
 */

public class CircleImageView extends ImageView {

    private Bitmap mBitmap;
    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);
        //得到影像
        mBitmap = ((BitmapDrawable) getDrawable()).getBitmap();
    }

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

    @Override
    protected void onDraw(Canvas canvas) {
        Bitmap bitmap = centerSquareScaleBitmap(mBitmap,100);
        canvas.drawBitmap(bitmap,100,100,null);
    }
    /**
     * 將給定圖片維持寬高比縮放後,擷取正中間的正方形部分。
     *
     * @param bitmap     原圖
     * @param edgeLength 希望得到的正方形部分的邊長
     * @return 縮放擷取正中部分後的點陣圖。
     */
    public static Bitmap centerSquareScaleBitmap(Bitmap bitmap, int edgeLength) {
        if (null == bitmap || edgeLength <= 0) {
            return null;
        }
        Bitmap result = bitmap;
        int widthOrg = bitmap.getWidth();
        int heightOrg = bitmap.getHeight();
        if (widthOrg >= edgeLength && heightOrg >= edgeLength) {
            //壓縮到一個最小長度是edgeLength的bitmap
            int longerEdge = (int) (edgeLength * Math.max(widthOrg, heightOrg) / Math.min(widthOrg, heightOrg));
            int scaledWidth = widthOrg > heightOrg ? longerEdge : edgeLength;
            int scaledHeight = widthOrg > heightOrg ? edgeLength : longerEdge;
            Bitmap scaledBitmap;
            try {
                scaledBitmap = Bitmap.createScaledBitmap(bitmap, scaledWidth, scaledHeight, true);
            } catch (Exception e) {
                return null;
            }
            //從圖中擷取正中間的正方形部分。
            int xTopLeft = (scaledWidth - edgeLength) / 2;
            int yTopLeft = (scaledHeight - edgeLength) / 2;
            try {
                result = Bitmap.createBitmap(scaledBitmap, xTopLeft, yTopLeft, edgeLength, edgeLength);
                scaledBitmap.recycle();
            } catch (Exception e) {
                return null;
            }
        }
        return result;
    }
}

佈局中使用

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    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"
    tools:context="com.softpo.customviewdemo.MainActivity">

    <ImageView
        android:id="@+id/rawImg"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/p5"
        />

    <com.softpo.customviewdemo.widget.CircleImageView
        android:layout_below="@id/rawImg"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:src="@mipmap/p5"
        />
</RelativeLayout>

效果圖:
這裡寫圖片描述

從中取出圓形圖片
修改程式碼:

/**
 * Created by softpo on 2016/10/29.
 */

public class CircleImageView extends ImageView {

    private Bitmap mBitmap;
    private int mWidth,mHeight;
    private BitmapShader mBitmapShader;
    private ShapeDrawable mShapeDrawable;
    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);
        //得到影像
        mBitmap = ((BitmapDrawable) getDrawable()).getBitmap();
        mWidth = mBitmap.getWidth();
        mHeight = mBitmap.getHeight();
        //構造渲染器BitmapShader
        /*
         CLAMP  :如果渲染器超出原始邊界範圍,會複製範圍內邊緣染色。
         REPEAT :橫向和縱向的重複渲染器圖片,平鋪。
         MIRROR :橫向和縱向的重複渲染器圖片,這個和REPEAT 重複方式不一樣,它是以映象方式平鋪。
         */
        Bitmap squareScaleBitmap = centerSquareScaleBitmap(mBitmap, 100);

        mBitmapShader = new BitmapShader(squareScaleBitmap, Shader.TileMode.MIRROR,Shader.TileMode.MIRROR);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
        int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
        //精確賦值
        /**
         * EXACTLY:當我們設定width或height為match_parent或者固定值時,容器在佈局時呼叫子 view的measure方法傳入的模式是EXACTLY,
         * 因為子view會佔據剩餘容器的空間,所以它大小是確定的;
         *
         * AT_MOST:而當設定為 wrap_content時,容器傳進去的是AT_MOST,
         * 表示子view的大小最多是多少,這樣子view會根據這個上限來設定自己的尺寸;
         *
         * UNSPECIFIED:是未指定尺寸,這種情況不多,一般都是父控制元件是AdapterView,通過measure方法傳入的模式
         */
        if(modeWidth==MeasureSpec.EXACTLY&&modeHeight==MeasureSpec.EXACTLY){
            mWidth = MeasureSpec.getSize(widthMeasureSpec);
            mHeight = MeasureSpec.getSize(heightMeasureSpec);
        }

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onDraw(Canvas canvas) {
//        super.onDraw(canvas);
        //將圖片裁剪為橢圓形
        //構建ShapeDrawable物件並定義形狀為橢圓
        mShapeDrawable = new ShapeDrawable(new OvalShape());
        //得到畫筆並設定渲染器
        mShapeDrawable.getPaint().setShader(mBitmapShader);
        //設定顯示區域
        mShapeDrawable.setBounds(20, 20,mWidth,mHeight);
        //繪製shapeDrawable
        mShapeDrawable.draw(canvas);
    }
    /**
     * 將給定圖片維持寬高比縮放後,擷取正中間的正方形部分。
     *
     * @param bitmap     原圖
     * @param edgeLength 希望得到的正方形部分的邊長
     * @return 縮放擷取正中部分後的點陣圖。
     */
    public static Bitmap centerSquareScaleBitmap(Bitmap bitmap, int edgeLength) {
        if (null == bitmap || edgeLength <= 0) {
            return null;
        }
        Bitmap result = bitmap;
        int widthOrg = bitmap.getWidth();
        int heightOrg = bitmap.getHeight();
        if (widthOrg >= edgeLength && heightOrg >= edgeLength) {
            //壓縮到一個最小長度是edgeLength的bitmap
            int longerEdge = (int) (edgeLength * Math.max(widthOrg, heightOrg) / Math.min(widthOrg, heightOrg));
            int scaledWidth = widthOrg > heightOrg ? longerEdge : edgeLength;
            int scaledHeight = widthOrg > heightOrg ? edgeLength : longerEdge;
            Bitmap scaledBitmap;
            try {
                scaledBitmap = Bitmap.createScaledBitmap(bitmap, scaledWidth, scaledHeight, true);
            } catch (Exception e) {
                return null;
            }
            //從圖中擷取正中間的正方形部分。
            int xTopLeft = (scaledWidth - edgeLength) / 2;
            int yTopLeft = (scaledHeight - edgeLength) / 2;
            try {
                result = Bitmap.createBitmap(scaledBitmap, xTopLeft, yTopLeft, edgeLength, edgeLength);
                scaledBitmap.recycle();
            } catch (Exception e) {
                return null;
            }
        }
        return result;
    }
}

佈局中使用:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    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"
    tools:context="com.softpo.customviewdemo.MainActivity">

    <ImageView
        android:id="@+id/rawImg"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/p5"
        />

    <com.softpo.customviewdemo.widget.CircleImageView
        android:layout_below="@id/rawImg"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:src="@mipmap/p5"
        />
</RelativeLayout>

這裡寫圖片描述

相關文章