1. 程式人生 > >Android繪製圓形圖片的方法總結

Android繪製圓形圖片的方法總結

  在需求開發中經常會遇到需要顯示圓形圖片的情況,比如我們經常見到的使用者圓形頭像。為了實現這個效果,我們需要重新定義View,通過重寫onDraw方法來使得我們的View顯示成圓形。要是實現這個功能主要有三種方法。

使用Xfermode 相交模式

  什麼是相交模式?正常的情況下,在已有的影象上繪圖將會在其上面新增一層新的形狀。如果新的Paint是完全不透明的,那麼它將完全遮擋住下面的Paint;如果它是部分透明的,那麼它將會被染上下面的顏色。而setXfermode就可以來解決這個問題,設定兩張圖相交之後的顯示模式。canvas原有的圖片可以理解為背景,就是dst;新畫上去的圖片可以理解為前景,就是src。下面是全部的16種相交模式的效果圖。

這裡寫圖片描述
  由上圖可以看到,如果我們需要畫一個圓形的圖,可以有幾種方法,比如,可以通過先繪製原圖,然後使用DstIn的方式繪製一個圓;或者可以先繪製一個圓,然後通過SrcIn的方式繪製原圖。我們使用第一種方法的程式碼如下:

@Override
protected void onDraw(Canvas canvas) {
    Drawable drawable = getDrawable();
    if (drawable != null) {
        //步驟1:先生成一個bitmap,在bitmap上繪製原圖
        bitmap = Bitmap.createBitmap(getWidth(),getHeight(), Bitmap.Config.ARGB_8888);
        Canvas bitmapCanvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, getWidth(), getHeight());
        drawable.draw(bitmapCanvas);

        //步驟2:生成圓形圖片蒙版
        Bitmap mask = Bitmap.createBitmap(width, height,Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(mask);
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(Color.BLACK);
        canvas.drawOval(new RectF(0.0f, 0.0f, width, height), paint);

        //步驟3:使用DST_IN的方式在原圖的bitmap上繪製蒙版圖
        mPaint.reset();
        mPaint.setFilterBitmap(false);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
        bitmapCanvas.drawBitmap(mask, 0.0f, 0.0f, mPaint);

        //步驟4:將最後生成的這個bitmap繪製到View的canvas上
        if (bitmap != null) {
            mPaint.setXfermode(null);
            canvas.drawBitmap(bitmap, 0.0f, 0.0f, mPaint);
        }
    }
}

通過裁剪畫布區域實現

  Canvas類提供了ClipPath, ClipRect, ClipRegion 等方法來裁剪畫布,通過他們的不同組合,可以得到任意形狀的畫布,然後在這個區域上畫圖,就可以獲得對應形狀的View了。但是,使用裁剪畫布的方式實現圓形頭像會有鋸齒,邊緣不如其他方式平滑。程式碼如下:

​//為了保證繪製出來的View為圓形,如果圖片的長寬不一致,長的部分會被截斷
​@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    int mSize = Math.min(getMeasuredWidth(), getMeasuredHeight());
    mRadius = mSize / 2;
    setMeasuredDimension(mSize, mSize);
}

@Override
protected void onDraw(Canvas canvas) {
    mPath.addCircle(mRadius, mRadius, mRadius, Path.Direction.CW);
    canvas.clipPath(mPath);
    super.onDraw(canvas);
}

使用BitmapShader

  Shader就是畫筆Paint的渲染器,本質上這種方法其實是畫圓,只是渲染時採用了我們設定的圖片。
  BitmapShader是Shader的子類,可以通過Paint.setShader(Shader shader)進行設定,然後用這個Paint繪圖時,就會根據你設定的TileMode,對繪製區域進行著色。BitmapShader的構造方法:
mBitmapShader = new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP);
  引數TileMode的取值有三種:
CLAMP 拉伸:拉伸的是圖片最後的那一個畫素
REPEAT 重複:就是橫向、縱向不斷重複這個bitmap
MIRROR 映象:橫向、縱向不斷翻轉重複
  程式碼示例如下:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    mSize = Math.min(getMeasuredWidth(), getMeasuredHeight());
    mRadius = mSize / 2;
    setMeasuredDimension(mSize, mSize);
}

@Override
protected void onDraw(Canvas canvas) {
    //得到原bitmap
    Bitmap src = ((BitmapDrawable) getDrawable()).getBitmap();
    if (src == null) {
        super.onDraw(canvas);
        return;
    }
    //把bitmap縮小為和View大小一致
    Bitmap newBitmp = Bitmap.createScaledBitmap(src, mSize, mSize, false);
    if (newBitmp == null) {
        return;
    }
    //將縮小後的bitmap設定為畫筆的shader
    mBitmapShader = new BitmapShader(newBitmp, Shader.TileMode.REPEAT,
            Shader.TileMode.REPEAT);
    //生成用來繪圖的bitmap,並在其上用畫筆繪圖
    Bitmap dest = Bitmap.createBitmap(mSize, mSize, Bitmap.Config.ARGB_8888);
    if (dest == null) {
        return;
    }
    Canvas c = new Canvas(dest);
    Paint paint = new Paint();
    paint.setAntiAlias(true);
    paint.setShader(mBitmapShader);
    c.drawCircle(mRadius, mRadius, mRadius, paint);
    //將最後生成的bitmap繪製到View的canvas上
    canvas.drawBitmap(dest, 0, 0, paint);
}

  上面這三種方法都可以實現圓形頭像,也可以實現其他形狀,通常第一種實現方式使用的比較多。