近期又在啃《Android開發藝術探索》這本書,最近看到了第六章節—Android中的Drawable。我寫博客的風格不喜歡一味的介紹理論知識,更喜歡從實戰的角度去學習,在敲代碼的過程中去補充理論知識,根據實際情況做出分析,最後實現想要的效果。本文就從製作圓形頭像的角度,來學習Android中的Drawable的那些事。
一.準備工作
Drawable有很多種,表示的是一種可以在Canvas上進行繪製的抽象的概念,它的種類有很多種,最常見的顏色和圖片都可以是一個Drawable。它是所有Drawable對象的基類,每個具體的Drawable都是它的子類。比如下文提到的BitmapDrawable。
BitmapDrawable是一種最簡單的Drawable,它表示的就是一張圖片,在實際開發中應用也是最廣泛的,我們可以直接引用原始圖片即可獲取。
回到需求上,我們要製作一個圓形頭像,首先需要一張圖片,最終以圓形的方式展現出來。
這是我在網上找的一張圖片:
製作圓形圖片,最理想的效果是原始圖片能夠是正方形的,這樣圖片裁剪成圓形以後纔會顯得對稱。而提供給我們的原始圖片很有可能不是正方形的,比如上圖,所以我們首先要加工這張圖片,使成爲正方形圖片。
佈局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_third"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="org.tyk.android.artstudy.ThirdActivity">
<ImageView
android:id="@+id/my_circle_img"
android:layout_width="100dp"
android:layout_height="150dp"
android:layout_marginTop="10dp"
android:src="@drawable/k" />
<ImageView
android:id="@+id/second_img"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginTop="10dp" />
<Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="縮放" />
</LinearLayout>
佈局文件中,上面是我們的原始圖片,下面是我們處理以後的圖片,看看點擊事件做了什麼:
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Bitmap bitmap = ((BitmapDrawable) myCircleImageView.getDrawable()).getBitmap();
Matrix matrix = new Matrix();
float size = Math.min(bitmap.getWidth(), bitmap.getHeight());
//x縮放比例
float x = size / bitmap.getWidth();
//y縮放比例
float y = size / bitmap.getHeight();
matrix.setScale(x, y);
Bitmap newBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
img.setImageBitmap(newBitmap);
}
});
首先獲取當前ImageView的Bitmap,關於ImageView轉換爲Bitmap,大家可以參考這篇博客:
然後構造一個矩陣Matrix,計算並設置好縮放比例,然後調用
createBitmap(Bitmap source, int x, int y, int width, int height,
Matrix m, boolean filter)
方法生成新的Bitmap。看看這個方法具體幹了什麼:
這是官方文檔解釋,source代表原始圖片的Bitmap;x,y代表X,Y方向上的起始位置;width,height代表X,Y方向需要處理的寬度與高度;m代表圖片處理的矩陣;,filter參數爲true表示進行濾波處理,有助於改善新圖像質量,flase代表不做過濾處理。
看看處理後的效果:
OK,圖片的前期準備工作已經做好了,現在開始製作圓形頭像了。
二.XferMode方法制作圓形圖片
Xfermode有三個子類 :
AvoidXfermode ,PixelXorXfermode,PorterDuffXfermode,其中前兩個類在API 16被遺棄了 。PorterDuffXfermode類主要用於計算圖形合成時的圖像過渡模式 ,一共有16條規則。然後調用 paint.setXfermode(XferMode)方法設置圖像的過渡模式,這樣就可以完成一些複雜的效果。先看看有哪些模式:
每種模式代表的含義如下:
1.PorterDuff.Mode.CLEAR
所繪製不會提交到畫布上。
2.PorterDuff.Mode.SRC
顯示上層繪製圖片
3.PorterDuff.Mode.DST
顯示下層繪製圖片
4.PorterDuff.Mode.SRC_OVER
正常繪製顯示,上下層繪製疊蓋。
5.PorterDuff.Mode.DST_OVER
上下層都顯示。下層居上顯示。
6.PorterDuff.Mode.SRC_IN
取兩層繪製交集。顯示上層。
7.PorterDuff.Mode.DST_IN
取兩層繪製交集。顯示下層。
8.PorterDuff.Mode.SRC_OUT
取上層繪製非交集部分。
9.PorterDuff.Mode.DST_OUT
取下層繪製非交集部分。
10.PorterDuff.Mode.SRC_ATOP
取下層非交集部分與上層交集部分
11.PorterDuff.Mode.DST_ATOP
取上層非交集部分與下層交集部分
12.PorterDuff.Mode.XOR
異或:去除兩圖層交集部分
13.PorterDuff.Mode.DARKEN
取兩圖層全部區域,交集部分顏色加深
14.PorterDuff.Mode.LIGHTEN
取兩圖層全部,點亮交集部分顏色
15.PorterDuff.Mode.MULTIPLY
取兩圖層交集部分疊加後顏色
16.PorterDuff.Mode.SCREEN
取兩圖層全部區域,交集部分變爲透明色
大家也可以參考這篇文章:
這裏我們結合圓形圖像的例子看看怎麼使用:
//縮放以後的bitmap
Bitmap newBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
int width = newBitmap.getWidth();
int height = newBitmap.getHeight();
//圓形bitmap
Bitmap circleBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(circleBitmap);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.BLACK);
canvas.drawCircle(width / 2, height / 2, width / 2, paint);
PorterDuffXfermode porterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
paint.setXfermode(porterDuffXfermode);
canvas.drawBitmap(newBitmap, 0, 0, paint);
circleImg.setImageBitmap(circleBitmap);
得到縮放以後的Bitmap以後,創建一個Canvas對象,首先繪製了一個黑色實心圓,然後設置Xfermode爲PorterDuff.Mode.SRC_IN,最後繪製縮放以後的Bitmap。根據這種模式的定義:取兩層繪製交集並顯示上層,可以得到我們的圓形頭像。最後實現的效果如下:
三.BitmapShader方法制作圓形圖片
BitmapShader是Shader的子類,可以通過Paint.setShader(Shader shader)進行設置,BitmapShader的構造方法如下:
mBitmapShader = new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP);
參數1:bitmap
參數2,參數3:TileMode;
TileMode的取值有三種:
CLAMP 拉伸 拉伸的是圖片最後的那一個像素;橫向的最後一個橫行像素,不斷的重複,縱項的那一列像素,不斷的重複
REPEAT 重複 就是橫向、縱向不斷重複這個bitmap
MIRROR 鏡像 橫向不斷翻轉重複,縱向不斷翻轉重複
關於BitmapShader可參考:
自定義控件其實很簡單1/3
這裏我們結合圓形圖像的例子看看怎麼使用:
//縮放以後的bitmap
Bitmap newBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
int width = newBitmap.getWidth();
int height = newBitmap.getHeight();
//圓形bitmap
Bitmap circleBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(circleBitmap);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.BLACK);
/**第一種方式**/
// canvas.drawCircle(width / 2, height / 2, width / 2, paint);
// PorterDuffXfermode porterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
// paint.setXfermode(porterDuffXfermode);
// canvas.drawBitmap(newBitmap, 0, 0, paint);
/**第二種方式**/
BitmapShader bitmapShader = new BitmapShader(newBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
paint.setShader(bitmapShader);
canvas.drawCircle(width / 2, height / 2, width / 2, paint);
circleImg.setImageBitmap(circleBitmap);
得到縮放以後的Bitmap以後,創建一個Canvas對象,初始化BitmapShader,畫筆設置Shader,最後在canvas裏面進行畫圓就行了。效果與第一種方式實現的一樣,就不重複貼圖了。
四.ClipPath方法制作圓形圖片
使用clipPath的方法進行切割,來實現圓角圖片,具體可參考:
這裏我們結合圓形圖像的例子看看怎麼使用:
//縮放以後的bitmap
Bitmap newBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
int width = newBitmap.getWidth();
int height = newBitmap.getHeight();
//圓形bitmap
Bitmap circleBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(circleBitmap);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.BLACK);
/**第一種方式**/
// canvas.drawCircle(width / 2, height / 2, width / 2, paint);
// PorterDuffXfermode porterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
// paint.setXfermode(porterDuffXfermode);
// canvas.drawBitmap(newBitmap, 0, 0, paint);
/**第二種方式**/
// BitmapShader bitmapShader = new BitmapShader(newBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
// paint.setShader(bitmapShader);
// canvas.drawCircle(width / 2, height / 2, width / 2, paint);
// circleImg.setImageBitmap(circleBitmap);
/**第三種方式**/
Path path = new Path();
//按照順時針方向添加一個圓
path.addCircle(width / 2, height / 2, width / 2, Path.Direction.CW);
canvas.save();
//設置爲在圓形區域內繪製
canvas.clipPath(path);
canvas.drawBitmap(newBitmap, 0, 0, paint);
canvas.restore();
circleImg.setImageBitmap(circleBitmap);
得到縮放以後的Bitmap以後,創建一個Canvas對象,設置爲在圓形區域內繪製,最後在canvas裏面繪製Bitmap。效果與第一種方式實現的一樣,就不重複貼圖了。
當然,要實現其他形狀圖片,其實很簡單,每種方法稍微改變一下就行,只要先繪製出不同的形狀,原理還是和這個一樣。