目前為止,已經(jīng)掌握了兩種制作圓形頭像的方法,一種是利用 PorterDuffXfermode
听诸,一種是 Shader
工具。至于選擇哪一種方法因人而異蚕泽,但幸運的是這兩種方法的使用都并不難晌梨。好了,現(xiàn)在介紹一下如何利用 BitmapShader
制作圓形頭像须妻,為了演示仔蝌,我做了一個 Demo ,可以自定義要顯示的圖片荒吏,邊框顏色敛惊,邊框?qū)挾龋Ч缦聢D所示绰更。
BitmapShader 的簡單介紹
關(guān)于 Shader
是什么豆混,Shader
的種類有哪幾種以及如何使用不屬于本文范疇,對這方面不是很了解的同學动知,建議先去學習一下 Shader
的基本使用。
BitmapShader
主要的作用就是 通過 Paint
對象员辩,對 畫布進行指定的 Bitmap 填充
盒粮,實現(xiàn)一系列效果,可以有以下三種模式進行選擇
CLAMP - 拉伸奠滑,這里拉伸的是圖片的最后一個元素丹皱,不斷地重復(fù),這個效果宋税,在圖片比較小摊崭,而所要畫的面積比較大的時候會比較明顯。
REPEAT - 重復(fù)杰赛,橫向縱向不斷地重復(fù)呢簸,不同于上一模式,這種模式在圖片比較小不能滿足要求是乏屯,會在橫向縱向不斷重復(fù)繪制圖形根时。
MIRROR - 翻轉(zhuǎn),這種模式和 REPEAT 是類似的辰晕,只不過這里的重復(fù)是翻轉(zhuǎn)著重復(fù)蛤迎,和折紙的效果差不多。
而我們將要使用的是 CLAMP 模式含友,因為只要我們對圖形的大小進行控制替裆,就可以避免圖像進行拉伸校辩。
具體實現(xiàn)
為了自定義 圖像,邊框?qū)挾群皖伾就覀兪紫仍?res/values 目錄下宜咒,新建一個 attrs.xml文件,里面要書寫的內(nèi)容如下所示
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyCustomView">
<attr name="mborder_color" format="color"></attr>
<attr name="mborder_width" format="dimension"></attr>
<attr name="msrc" format="reference"></attr>
</declare-styleable>
</resources>
當然胸遇,在這里還可以添加一些其他的特性荧呐。既然定義了我們想要使用的特性,那么我們就要在自定義 View
里面 解析這些屬性并且加以利用纸镊,解析過程如下所示
TypedArray type = context.obtainStyledAttributes(attrs, R.styleable.MyCustomView);
mBorderColor = type.getColor(R.styleable.MyCustomView_mborder_color,0);
mDrawable = type.getDrawable(R.styleable.MyCustomView_msrc);
//將獲得的 Drawable 轉(zhuǎn)換成 Bitmap
BitmapDrawable bitmapDrawable = (BitmapDrawable) mDrawable;
mBitmap = bitmapDrawable.getBitmap();
mBorderWidth = type.getDimensionPixelSize(R.styleable.MyCustomView_mborder_width, 2);
值得注意的是 mSrc 屬性的解析倍阐,由于獲得是 Drawable
對象,所以我們需要將其轉(zhuǎn)換為 Bitmap
對象峰搪。
下面就利用我們獲得的 Bitmap
對象進行圓形頭像的繪制凯旭,對 BitmapShader
和 Paint
的初始化如下所示
mSrcBitmap = Bitmap.createScaledBitmap(mBitmap, mWidth, mHeight, false);
mShader = new BitmapShader(mSrcBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mPaint = new Paint();
mPaint.setShader(mShader);
mRadius = (mWidth - mBorderWidth * 2 - 4) / 2;
mCircleX = (mWidth) / 2;
mCircleY = (mHeight) / 2;
mSrcBitmap 是對獲得的圖像進行適當?shù)目s小或者放大,以適應(yīng)我們對圖形的要求罐呼,而這里的 mWidth 和 mHeight 又是什么呢鞠柄?實際上就是我們在 定義視圖在 layout_width
和 layout_height
中傳遞進來的值,不過在這里我對他們進行了處理嫉柴,也就是選取最小值操作,這樣的話就可以避免由于寬大于高或者高大于寬而造成圖像填充不滿指定區(qū)域的現(xiàn)象夯尽。值得注意的是,自定義視圖登馒,需要對 wrap_content
進行特殊處理匙握,否則系統(tǒng)對該屬性的視圖不予以顯示。至于如何進行處理陈轿,可以看看本例的源碼,很簡單济欢,相信很多人一看就知道或者說早就了然于胸。
還有一點需要強調(diào)的是這里的 mRadius 茫叭,也就是將要繪制的圓的半徑半等,為什么要減去邊框的寬度 乘 2 呢呐萨? 要知道,我們的圓是根據(jù) 視圖指定的寬度或者高度來畫的谬擦,如果我們所畫 的圓恰好是指定視圖的內(nèi)切圓朽缎,那么邊框放在哪里呢?它肯定要被畫在視圖的外面北秽,那樣我們就看不到完整的邊框了最筒。所以床蜘,這么減去的意義在于為邊框騰出空間。
經(jīng)過以上操作扬蕊,我們就已經(jīng)將圓形頭像繪制出來了丹擎,下面來繪制邊框,其實非常簡單,我只不過是又定義了一個 Paint
對象庶骄,并且利用它畫了一個圓而已,畫筆的初始化操作如下所示
mBorderPaint = new Paint();
mBorderPaint.setStyle(Paint.Style.STROKE);
mBorderPaint.setStrokeWidth(mBorderWidth);
mBorderPaint.setColor(mBorderColor);
mBorderPaint.setStrokeCap(Paint.Cap.ROUND);
好了灸异,下面就可以在 onDraw()
函數(shù)中羔飞,進行繪制了肺樟,如下所示
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(mCircleX, mCircleY, mRadius, mPaint);
canvas.drawCircle(mCircleX, mCircleY, mRadius, mBorderPaint);
}
這樣,整個效果就算實現(xiàn)完畢了么伯。下面來看看如何使用
<com.example.hwaphon.patheffecttest.MyView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginRight="8dp"
app:mborder_color="@android:color/holo_green_light"
app:mborder_width="4dp"
app:msrc="@drawable/myview_test"/>
注意卡儒,一定要在容器中加上這么一句
xmlns:app="http://schemas.android.com/apk/res-auto"
具體實現(xiàn)的核心代碼
package com.example.hwaphon.patheffecttest;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by Hwaphon on 2016/5/12.
*/
public class MyView extends View {
private Bitmap mBitmap;
private Drawable mDrawable;
private Bitmap mSrcBitmap;
private BitmapShader mShader;
private Paint mPaint;
private int mWidth, mHeight;
private int mRadius;
private int mCircleX, mCircleY;
private int mBorderColor;
private Paint mBorderPaint;
private int mBorderWidth;
public MyView(Context context) {
this(context, null);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray type = context.obtainStyledAttributes(attrs, R.styleable.MyCustomView);
mBorderColor = type.getColor(R.styleable.MyCustomView_mborder_color,0);
mDrawable = type.getDrawable(R.styleable.MyCustomView_msrc);
//將獲得的 Drawable 轉(zhuǎn)換成 Bitmap
BitmapDrawable bitmapDrawable = (BitmapDrawable) mDrawable;
mBitmap = bitmapDrawable.getBitmap();
mBorderWidth = type.getDimensionPixelSize(R.styleable.MyCustomView_mborder_width, 2);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = measureWidth(widthMeasureSpec);
mHeight = measureHeight(heightMeasureSpec);
int temp = mWidth > mHeight ? mHeight : mWidth;
mWidth = mHeight = temp;
initView();
setMeasuredDimension(mWidth, mHeight);
}
private int measureHeight(int heightMeasureSpec) {
int size = MeasureSpec.getSize(heightMeasureSpec);
int sizeMode = MeasureSpec.getMode(heightMeasureSpec);
int result = 0;
if (sizeMode == MeasureSpec.EXACTLY) {
result = size;
} else {
result = 200;
if (sizeMode == MeasureSpec.AT_MOST) {
result = Math.min(result, size);
}
}
return result;
}
private int measureWidth(int widthMeasureSpec) {
int size = MeasureSpec.getSize(widthMeasureSpec);
int sizeMode = MeasureSpec.getMode(widthMeasureSpec);
int result = 0;
if (sizeMode == MeasureSpec.EXACTLY) {
result = size;
} else {
result = 200;
if (sizeMode == MeasureSpec.AT_MOST) {
result = Math.min(result, size);
}
}
return result;
}
private void initView() {
mSrcBitmap = Bitmap.createScaledBitmap(mBitmap, mWidth, mHeight, false);
mShader = new BitmapShader(mSrcBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mPaint = new Paint();
mPaint.setShader(mShader);
mRadius = (mWidth - mBorderWidth * 2) / 2;
mCircleX = (mWidth) / 2;
mCircleY = (mHeight) / 2;
mBorderPaint = new Paint();
mBorderPaint.setStyle(Paint.Style.STROKE);
mBorderPaint.setStrokeWidth(mBorderWidth);
mBorderPaint.setColor(mBorderColor);
mBorderPaint.setStrokeJoin(Paint.Join.ROUND);
mBorderPaint.setStrokeCap(Paint.Cap.ROUND);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(mCircleX, mCircleY, mRadius, mPaint);
canvas.drawCircle(mCircleX, mCircleY, mRadius, mBorderPaint);
}
}