寫在前面的幾句話
<p>
在實際的開發(fā)中绕辖,我們經(jīng)常會遇到需要圓角ImageView的情況擂红,但是這種ImageView官方是沒有提供的篮条,所以需要我們?nèi)プ约褐貙慖mageView來達到圓角的效果,但是實現(xiàn)這種圓角效果其實有幾種不同的實現(xiàn)方式赴恨,所以這一篇就對不同的實現(xiàn)方式進行講解伦连,并簡單分析。
一.BitmapShader方式
<p>
首先簡單了解下BitmapShader惑淳,BitmapShader是Shader的子類,Shader在三維軟件中我們稱之為著色器移斩,所以通俗的理解向瓷,Shader的作用是給圖像著色或者上色舰涌,BitmapShader允許我們載入一張圖片來給圖像著色,具體不做過多的解釋,結尾貼出關于Shader的具體使用的文章
所以其實根據(jù)上面對于BitmapShader的描述朱躺,其實就可以對圓角ImageView有一定的思路了吧长搀,畫一個圓角矩形落追,然后把本來畫上去的圖像著色到圓角矩形上轿钠,這樣就實現(xiàn)了圓角的ImageView
思路說了就把代碼直接貼上來了病苗,代碼注釋也很清楚的硫朦,不做過多的解釋了
public class RoundImageView extends ImageView{
//圓角大小,默認為10
private int mBorderRadius = 10;
private Paint mPaint;
// 3x3 矩陣泽裳,主要用于縮小放大
private Matrix mMatrix;
//渲染圖像涮总,使用圖像為繪制圖形著色
private BitmapShader mBitmapShader;
public RoundImageView(Context context) {
this(context, null);
}
public RoundImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RoundImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mMatrix = new Matrix();
mPaint = new Paint();
mPaint.setAntiAlias(true);
}
@Override
protected void onDraw(Canvas canvas) {
if (getDrawable() == null){
return;
}
Bitmap bitmap = drawableToBitamp(getDrawable());
mBitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
float scale = 1.0f;
if (!(bitmap.getWidth() == getWidth() && bitmap.getHeight() == getHeight()))
{
// 如果圖片的寬或者高與view的寬高不匹配瀑梗,計算出需要縮放的比例;縮放后的圖片的寬高抛丽,一定要大于我們view的寬高亿鲜;所以我們這里取大值;
scale = Math.max(getWidth() * 1.0f / bitmap.getWidth(),
getHeight() * 1.0f / bitmap.getHeight());
}
// shader的變換矩陣陷寝,我們這里主要用于放大或者縮小
mMatrix.setScale(scale, scale);
// 設置變換矩陣
mBitmapShader.setLocalMatrix(mMatrix);
// 設置shader
mPaint.setShader(mBitmapShader);
canvas.drawRoundRect(new RectF(0,0,getWidth(),getHeight()), mBorderRadius, mBorderRadius,
mPaint);
}
private Bitmap drawableToBitamp(Drawable drawable)
{
if (drawable instanceof BitmapDrawable)
{
BitmapDrawable bd = (BitmapDrawable) drawable;
return bd.getBitmap();
}
// 當設置不為圖片凤跑,為顏色時叛复,獲取的drawable寬高會有問題,所有當為顏色時候獲取控件的寬高
int w = drawable.getIntrinsicWidth() <= 0 ? getWidth() : drawable.getIntrinsicWidth();
int h = drawable.getIntrinsicHeight() <= 0 ? getHeight() : 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;
}
}
只是基本功能的實現(xiàn)咖耘,并沒有進行更多的擴展儿倒,所以如果需要夫否,自給自足即可
運行截圖如下
二. PorterDuffXfermode方式
<p>
首先得了解下什么是PorterDuffXfermode凰慈,PorterDuffXfermode是Xfermode的子類驼鹅,Xfermode又稱為圖像混合模式,除了PorterDuffXfermode之外還有其他幾個子類分別為AvoidXfermode豺型,PixelXorXfermode买乃,
不做詳細的介紹,會在文章后面貼上詳細學習的文章哼绑,我們要知道的是我們需要用到的,我們通過設置PorterDuffXfermode屬性PorterDuff.Mode.SRC_IN來實現(xiàn)需要的效果蛀恩,那么這個PorterDuff.Mode.SRC_IN是什么意思呢茂浮?他的意思是源圖像與目標圖像相交地方繪制源圖像席揽,所以只需要我們把源圖像設置為圓角矩形,目標圖像繪制源圖像寸谜,那么經(jīng)過PorterDuffXfermode的效果就可以達到圓角矩形的效果
這個API因為不支持硬件加速在API 16已經(jīng)過時了属桦,如果想在高于API 16的機子上測試這玩意,必須現(xiàn)在應用或手機設置中關閉硬件加速
接下來貼上實現(xiàn)的源碼果善,不做解釋了巾陕,注釋寫的很清晰了
public class RoundImageView extends ImageView{
private Paint mPaint;
private Xfermode mXfermode;
private int mBorderRadius = 10;
public RoundImageView(Context context) {
this(context, null);
}
public RoundImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RoundImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
}
@Override
protected void onDraw(Canvas canvas) {
if (getDrawable() == null){
return;
}
int sc = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
//畫源圖像鄙煤,為一個圓角矩形
canvas.drawRoundRect(new RectF(0, 0, getWidth(), getHeight()), mBorderRadius, mBorderRadius,
mPaint);
//設置混合模式
mPaint.setXfermode(mXfermode);
//畫目標圖像
canvas.drawBitmap(drawableToBitamp(exChangeSize(getDrawable())), 0, 0, mPaint);
// 還原混合模式
mPaint.setXfermode(null);
canvas.restoreToCount(sc);
}
/**
* 圖片拉升
*
* @param drawable
* @return
*/
private Drawable exChangeSize(Drawable drawable){
float scale = 1.0f;
scale = Math.max(getWidth() * 1.0f / drawable.getIntrinsicWidth(), getHeight()
* 1.0f / drawable.getIntrinsicHeight());
drawable.setBounds(0, 0, (int) (scale * drawable.getIntrinsicWidth()),
(int) (scale * drawable.getIntrinsicHeight()));
return drawable;
}
private Bitmap drawableToBitamp(Drawable drawable)
{
if (drawable instanceof BitmapDrawable)
{
BitmapDrawable bd = (BitmapDrawable) drawable;
return bd.getBitmap();
}
// 當設置不為圖片馆类,為顏色時弹谁,獲取的drawable寬高會有問題句喜,所有當為顏色時候獲取控件的寬高
int w = drawable.getIntrinsicWidth() <= 0 ? getWidth() : drawable.getIntrinsicWidth();
int h = drawable.getIntrinsicHeight() <= 0 ? getHeight() : 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;
}
}
三.ClipPath方式
<p>
ClipPath是Canvas提供對畫布裁剪的方法之一植康,除了ClipPath還有clipRect方法展懈,畫布裁剪后后面的Canvas操作供璧,都會在對裁剪后的畫布進行操作
所以呢睡毒,只要繪出一個圓角矩形的路徑冗栗,然后用ClipPath裁剪,那么得到的畫布就是圓角矩形的钠至,那么后面的繪制自然也就是圓角矩形的了棉钧,解釋很清楚了,直接上代碼了掰盘,
public class RoundImageView extends ImageView{
float width,height;
public RoundImageView(Context context) {
this(context, null);
}
public RoundImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RoundImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
if (Build.VERSION.SDK_INT < 18) {
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
width = getWidth();
height = getHeight();
}
@Override
protected void onDraw(Canvas canvas) {
if (width > 12 && height > 12) {
Path path = new Path();
path.moveTo(12, 0);
path.lineTo(width - 12, 0);
path.quadTo(width, 0, width, 12);
path.lineTo(width, height - 12);
path.quadTo(width, height, width - 12, height);
path.lineTo(12, height);
path.quadTo(0, height, 0, height - 12);
path.lineTo(0, 12);
path.quadTo(0, 0, 12, 0);
canvas.clipPath(path);
}
super.onDraw(canvas);
}
}
四.選擇哪一種愧捕?
<p>
實現(xiàn)有三種次绘,那選擇哪一種最時候呢撒遣?less is more,我們當然選擇代碼量最少的咯,畢竟程序員禾进,偷懶最重要廉涕,哈哈,其實沒有啦狐蜕,主要是第二和第一種方式或多或少的都處理了Bitmap和Drawable,自然沒有第三種來的安全婆瓜。
寫在后面的幾句話
<p>
其實實現(xiàn)都不難,主要也是了解下一些繪圖用到的類和方法个初,掌握了各種繪圖的類和使用猴蹂,自定義View什么的都不是問題來著的,我的愿望是世界和平8不瘛F笆 !
Refrence
1.PorterDuffXfermode相關From AigeStudio
3.ClipPath相關From AigeStudio