Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 關於android開發 >> Android中自定義視圖View之---進階篇(Canvas的使用)

Android中自定義視圖View之---進階篇(Canvas的使用)

編輯:關於android開發

Android中自定義視圖View之---進階篇(Canvas的使用)


一、前言

那麼今天,我們繼續來看一篇關於Android中的UI篇,如何自定義視圖View的進階篇,關於前奏篇之前已經寫過了,在這篇文章中我主要介紹了自定義View的一些基礎知識,講解了Paint,Canvas,Path,漸變色等技術。那麼隔了半年的時間,我們今天再次看一下如何在上一個層次去使用Canvas自定義更多的我們想要的特效呢?說道這裡,我還想多講兩句,就是Android中UI知識點還是很多很高深的,如果你對UI技術了解的非常透徹,那麼待遇也是很不錯的,現在可以看到一些公司在找Android高級UI工程師,就是叫你去做一些酷炫的UI,同時對性能方面也是要了解很多的,因為我們知道酷炫的UI動畫什麼的,其實如果處理不好會給整個程序造成很大的性能問題。所以本人對Android中的UI和逆向領域非常感興趣的。

二、知識點&實現的效果

下面我們就來看看,如何使用Canvas做一些我們經常要用到的效果:

1、修改圖片的透明度

2、圖層的疊加效果(遮罩層)

3、畫布的保存和恢復操作

4、使用矩陣實現平移,旋轉,縮放動畫

5、通過旋轉,平移,縮放畫布來實現動畫

6、裁剪畫布

總共六個知識點,講完這些知識點,現在市面上的View大部分都可以知道原理,以及自己可以動手去繪制了,後面會再寫一篇關於自定義ViewGroup系列(LinearLayout,RelativeLayout等)的相關文章。

三、具體實現

我們下面就用例子一一介紹這上面的六個功能點,首先我們先回顧一下,如何自定義一個View,其實很簡單,我們只需要集成View類,然後在onDraw方法中,得到一個畫布Canvas對象,然後就可以繪制各種我們想要的效果了。

public void onDraw(Canvas canvas)

下面我們先來看看第一個知識點:

1、修改圖片的透明度

/**
 * 修改圖片的argb值
 * @param sourceImg
 * @param number
 * @return
 */
public static Bitmap getTransparentBitmap(Bitmap sourceImg, int number){
	int[] argb = new int[sourceImg.getWidth() * sourceImg.getHeight()];
	sourceImg.getPixels(argb, 0, sourceImg.getWidth(), 0, 0, sourceImg
			.getWidth(), sourceImg.getHeight());// 獲得圖片的ARGB值
	number = number * 255 / 100;
	for (int i = 0; i < argb.length; i++) {
		argb[i] = (number << 24) | (argb[i] & 0x00FFFFFF);
	}
	sourceImg = Bitmap.createBitmap(argb, sourceImg.getWidth(), sourceImg
			.getHeight(), Bitmap.Config.ARGB_8888);
	return sourceImg;
}

我們在onDraw方法繪制出修改之後的圖片:

BitmapDrawable drawable = (BitmapDrawable)ctx.getResources().getDrawable(R.drawable.cm_whatsapp_ico_audio);
Bitmap bitmap = drawable.getBitmap();

//修改圖片的argb值的使用
bitmap = getTransparentBitmap(bitmap, 10);
canvas.drawBitmap(bitmap, 0, 0, paint);
代碼其實很簡單,就是拿到原圖Bitmap,然後在從新構造一張圖片,只是這時候我們會看到這裡的技術點:

首先我們可以獲取到一張圖片的ARGB數組值,數組大小就是圖片的像素值,那麼每個像素都是一個int類型值:

A代表透明度:8位

RGB代表三基色的顏色值:各8位

那麼加起來是32位,正好一個int類型長度。

這個也是在構造圖片的時候我們可以看到Bitmap.Config.ARGB_8888的含義,當然這個Config還有其他值,比如:

Bitmap.Config.ALPHA_8:只有透明度的值,8位,那麼我們可以使用byte類型就可以了。這張圖我們可以想象沒有顏色的圖片。

Bitmap.Config.ARGB_4444:和ARGB.8888類似,就是占用的位數不一樣,這裡是4*4=16位,用short類型即可,由此可見ARGB.8888雖然能夠顯示更高清的圖片,但是占用內存會高點。

Bitmap.Config.RGB_565:只有顏色值,沒有透明度,占用位數:5+6+5=16位,用short類型即可。

就是在構造一張Bitmap的時候,可以傳遞新的argb值了,那麼意味著我們可以隨意的改變圖片的透明度,顏色值啥的,我們看一下效果:

\
下面我們在隨便改一下他的顏色值:

for (int i = 0; i < argb.length; i++) {
	argb[i] = (number << 24) | (argb[i] & 0x00FFFFFF);
	argb[i] = 400 + (argb[i] & 0xFF00FFFF);
}

看一下效果:

\

知識點:

1>、我們可以使用Bitmap的getPixel方法獲取圖片的像素值

2>、了解到了圖片像素值的表示含義和各個配置之間的差異

3>、修改圖片的透明度和顏色值

2、圖層的疊加效果(遮罩層)

這個知識點我們可能在前面已經介紹過了,比如如何制作圓角圖片,就是用遮罩層來做的,其實遮罩層說白了,就是兩張圖片之間如何進行疊加,類似於ppt中我們制作圖片的時候,可以選擇至於上層,至於底層等效果。但是這裡的遮罩層的效果會更多,下面來看一下代碼實現:

//獲得圓角圖片的方法
/**
 *   android.graphics.PorterDuff.Mode.SRC:只繪制源圖像
         android.graphics.PorterDuff.Mode.DST:只繪制目標圖像
         android.graphics.PorterDuff.Mode.DST_OVER:在源圖像的頂部繪制目標圖像
         android.graphics.PorterDuff.Mode.DST_IN:只在源圖像和目標圖像相交的地方繪制目標圖像
         android.graphics.PorterDuff.Mode.DST_OUT:只在源圖像和目標圖像不相交的地方繪制目標圖像
         android.graphics.PorterDuff.Mode.DST_ATOP:在源圖像和目標圖像相交的地方繪制目標圖像,在不相交的地方繪制源圖像
         android.graphics.PorterDuff.Mode.SRC_OVER:在目標圖像的頂部繪制源圖像
         android.graphics.PorterDuff.Mode.SRC_IN:只在源圖像和目標圖像相交的地方繪制源圖像
         android.graphics.PorterDuff.Mode.SRC_OUT:只在源圖像和目標圖像不相交的地方繪制源圖像
         android.graphics.PorterDuff.Mode.SRC_ATOP:在源圖像和目標圖像相交的地方繪制源圖像,在不相交的地方繪制目標圖像
         android.graphics.PorterDuff.Mode.XOR:在源圖像和目標圖像重疊之外的任何地方繪制他們,而在不重疊的地方不繪制任何內容
         android.graphics.PorterDuff.Mode.LIGHTEN:獲得每個位置上兩幅圖像中最亮的像素並顯示
         android.graphics.PorterDuff.Mode.DARKEN:獲得每個位置上兩幅圖像中最暗的像素並顯示
         android.graphics.PorterDuff.Mode.MULTIPLY:將每個位置的兩個像素相乘,除以255,然後使用該值創建一個新的像素進行顯示。結果顏色=頂部顏色*底部顏色/255
         android.graphics.PorterDuff.Mode.SCREEN:反轉每個顏色,執行相同的操作(將他們相乘並除以255),然後再次反轉。結果顏色=255-(((255-頂部顏色)*(255-底部顏色))/255)
 * @param bitmap
 * @param roundPx
 * @return
 */
public static Bitmap getRoundedCornerBitmap(Bitmap bitmap,float roundPx){
	Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap
			.getHeight(), Bitmap.Config.ARGB_8888);
	Canvas canvas = new Canvas(output);

	final int color = 0xff424242;
	final Paint paint = new Paint();
	final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
	final RectF rectF = new RectF(rect);
	paint.setAntiAlias(true);
	canvas.drawARGB(0, 0, 0, 0);
	paint.setColor(color);
	canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
	paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));
	canvas.drawBitmap(bitmap, rect, rect, paint);
	return output;
}
這裡我們看到,我們首先可以創造一個畫布,但是畫布不能為空,需要一個打底圖,我們直接使用配置Bitmap.Config.ARGB_8888配置創造一個和原圖一樣大小的白色圖片,當然我們通過上面說的方法,可以創造一個有透明度和顏色值的圖片,然後貼在畫布上。這裡我們看到也可以直接使用畫布來設置ARGB的值,這裡全是0,這時候我們可以作畫了,我們繪制一個圓角矩形,繪制完成之後,我們需要設置畫筆的遮罩層效果,這裡是關鍵點,關於遮罩層有很多種選項:
android.graphics.PorterDuff.Mode.SRC:只繪制源圖像
android.graphics.PorterDuff.Mode.DST:只繪制目標圖像
android.graphics.PorterDuff.Mode.DST_OVER:在源圖像的頂部繪制目標圖像
android.graphics.PorterDuff.Mode.DST_IN:只在源圖像和目標圖像相交的地方繪制目標圖像
android.graphics.PorterDuff.Mode.DST_OUT:只在源圖像和目標圖像不相交的地方繪制目標圖像
android.graphics.PorterDuff.Mode.DST_ATOP:在源圖像和目標圖像相交的地方繪制目標圖像,在不相交的地方繪制源圖像
android.graphics.PorterDuff.Mode.SRC_OVER:在目標圖像的頂部繪制源圖像
android.graphics.PorterDuff.Mode.SRC_IN:只在源圖像和目標圖像相交的地方繪制源圖像
android.graphics.PorterDuff.Mode.SRC_OUT:只在源圖像和目標圖像不相交的地方繪制源圖像
android.graphics.PorterDuff.Mode.SRC_ATOP:在源圖像和目標圖像相交的地方繪制源圖像,在不相交的地方繪制目標圖像
android.graphics.PorterDuff.Mode.XOR:在源圖像和目標圖像重疊之外的任何地方繪制他們,而在不重疊的地方不繪制任何內容
android.graphics.PorterDuff.Mode.LIGHTEN:獲得每個位置上兩幅圖像中最亮的像素並顯示
android.graphics.PorterDuff.Mode.DARKEN:獲得每個位置上兩幅圖像中最暗的像素並顯示
android.graphics.PorterDuff.Mode.MULTIPLY:將每個位置的兩個像素相乘,除以255,然後使用該值創建一個新的像素進行顯示。結果顏色=頂部顏色*底部顏色/255
android.graphics.PorterDuff.Mode.SCREEN:反轉每個顏色,執行相同的操作(將他們相乘並除以255),然後再次反轉。結果顏色=255-(((255-頂部顏色)*(255-底部顏色))/255)

 

這些選項很全的,可以實現兩張圖片之間疊加的任何效果了。設置完打底圖片的畫筆遮罩層之後,那麼就來繪制第二張圖片,也就是原始圖片,繪制完成之後,直接返回打底圖片即可。

看一下效果:

\
 

看到了,這裡我們選擇的遮罩層選項是:Mode.XOR。就是智慧值他們兩交集以外的地方,挺有意思的吧,下面我們在用Mode.SCR_IN來看看,和Mode.XOR相反,只繪制兩張圖片相交的地方:

\

好吧,這就實現了圖片的圓角效果了。哈哈哈,當然還有其他遮罩的效果,大家都可以去嘗試一下,也很好玩的。

知識點:

1>、手動創建一個Bitmap和Canvas

2>、了解遮罩層技術改變兩張圖片的疊加效果

3、畫布的保存和恢復操作

這個技術,我們在自定義View的時候,用到的最多的地方,也是非常重要的一個點,他說白了,就是先在畫布上繪制一個圖形,然後保存一下,在繪制一個圖形,然後在恢復一下,這樣就可以在一個畫布上繪制出不同的樣式了,下面我們來看一下代碼:

/**
 * 保留圖層技術
 * @param canvas
 */
public static void saveCanvas(Canvas canvas){

	//首先繪制一個線條
	Paint paint = new Paint();
	paint.setColor(Color.BLUE);
	paint.setStrokeWidth(10);
	canvas.drawLine(0,0, 100,100,paint);

	//保存畫布,同時設置區域l,t,r,b
	canvas.saveLayerAlpha(10, 10, 300, 300, 0x88, Canvas.ALL_SAVE_FLAG);

	//繪制圓圈
	Paint paint1 = new Paint();
	paint1.setColor(Color.RED);
	paint1.setStrokeWidth(10);
	paint1.setStyle(Paint.Style.FILL);
	canvas.drawCircle(140, 140, 100, paint1);
	canvas.restore();
}
我們看到首先,我們繪制出一個藍色的線條,然後我們保存畫布狀態,我們在保存的時候,同時設置一下畫布的區域和透明度,然後在繪制一個紅色的圓圈,最後恢復畫布,我們看一下效果:

\
 

看到了,首先紅色的圓圈和藍色的線條重疊的地方,紅色圖片在上面,其次是我們看到紅色的圓圈有透明度的,不是全紅的。這樣我們就學會了保存畫布和恢復畫布狀態操作,但是在實際的過程中我們不可能這麼簡單的,我們改一下代碼,讓畫布平移一下:

\

這裡我們使用矩陣來操作一下畫布平移,然後我們看一下效果:

\

好了,看到了嗎?圓圈移動了,其實實際上不是圓圈移動了,是畫布移動了,所以要理解好畫布的保存和恢復的真正含義:

每個畫板Canvas創建的時候都有一個默認的圖層,我們在使用save方法的時候,會在創建一個圖層,然後繪制,在調用restore方法的時候,相當於兩個圖層的疊加操作,所以看上去是畫布移動了,其實不是的,是新建了一個圖層,畫布不可能移動的。在說的通俗點就是:畫布相當於畫板,圖層相當於畫紙。

當然這裡我們還看到了圓圈沒顯示全,這個就是我們上面在保存畫布的時候,設置了區域,其實這個區域和透明度就是新建圖層的大小和透明度,這個圓圈是繪制在新建的圖層上的所以會被裁剪了。

還有一個地方需要注意的是:

我們每次如果要操作畫布的時候,一定要記住一個准則:

先操作畫布,在繪制圖形

如果順序反了,那麼操作是沒有效果的,比如上面的平移效果如果放到繪制圓圈的後面,那麼就沒有任何效果的。

知識點:

1>、使用畫布保存技術來繪制想要的特殊效果

2>、理解了畫布的平移,縮放等效果不是真正意義上的操作畫布,而是新建一個圖層,然後進行圖層的疊加操作。

3>、使用矩陣來操作畫布

4、使用矩陣實現平移,旋轉,縮放效果

我們在實現View的簡單的平移,旋轉,縮放等功能的時候,Android中提供了很多種選擇,可以使用傳統動畫,屬性動畫等。但是這裡我們今天來看一下,在繪制圖片的時候我們使用矩陣來實現這些效果,說道矩陣,在學習線性數學的時候,我們知道兩個矩陣相乘能夠實現各種變化效果,同時A*B=C,A矩陣我們理解為變化前的矩陣,B矩陣理解為變化矩陣,C矩陣理解為變化後的矩陣。當然Android中操作舉證的Api不需要我們去手動的計算,而是幫我們已經封裝了平移,旋轉,縮放這三種功能的效果矩陣,同樣我們也可以實現矩陣的手動計算,但是這裡就不介紹了,因為矩陣操作還是很復雜的,感興趣的同學可以去網上搜一下。下面我們來看一下代碼:

/**
 * 使用Matrix來實現旋轉圖片
 * @param bitmap
 * @return
 */
public static Bitmap operateBitmap(Bitmap bitmap){
	Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap
			.getHeight(), Bitmap.Config.RGB_565);
	Canvas canvas = new Canvas(output);
	Matrix matrix = new Matrix();
	matrix.postRotate(300);
	matrix.postScale(0.4f, 0.5f);
	matrix.postTranslate(100, 100);
	canvas.drawBitmap(bitmap, matrix, new Paint());
	return output;
}
這裡我們依然是,先創建一個畫布和打底圖,然後就用矩陣來操作圖片了,這裡先旋轉,縮放,平移,然後我們看一下效果:

 

\
看到了,圖片變成這個樣子了,但是這裡我們看到怎麼有一塊是黑色的,原因是我們在創建打底圖的時候設置的圖片配置是:

Bitmap.Config.RGB_565

這個樣式是沒有透明度的,默認是黑色的。

關於Matrix矩陣操作有一個前乘和後乘的區別,因為矩陣的乘法是不支持交換律的,post開頭的方法都是後乘,就是我們正常的操作效果,其中pre開頭的方法是前乘,改一下代碼:

\

看看效果:

\

我擦,不知道變成什麼鬼了,我這裡為什麼要提一下前乘和後乘的區別呢?其實想說明一個問題就是:

一定不要把前乘和後乘理解為是相反的操作

如果我們想操作相反的動作,只需要把數值寫成負數值即可。

知識點:

1>、學習到了矩陣操作

2>、理解了矩陣的前乘和後乘的區別

5、通過旋轉,平移,縮放畫布來實現動畫

關於這個知識點,我們在前面自定義視圖View的基礎篇說到了。但是這裡我們來使用選擇畫布的方式來實現動畫效果,下面來看一下代碼:

 

/**
 * 通過旋轉畫布來實現圖片的旋轉
 * @param canvas
 * @param bitmap
 * @param argee
 */
public void recyleRotateBitmap(Canvas canvas, Bitmap bitmap, int argee){
	//這裡需要注意的是一定是先操作完畫布,才能繪制,不然沒效果的
	canvas.rotate(argee);
	Rect rect1 = new Rect(0,0,bitmap.getWidth(),bitmap.getWidth());
	Rect rect2 = new Rect(0,0,bitmap.getWidth(),bitmap.getHeight());
	canvas.drawBitmap(bitmap, rect1, rect2, new Paint());
	postInvalidateDelayed(200);
}
這裡我們可以看到,Canvas本身也有平移,旋轉,縮放的方法,可以不需要矩陣就可以實現,這裡我們實現的原理很簡單:就是在onDraw方法中來改變旋轉角度,然後從新繪制圖片,然後調用postInvalidateDelayed方法來實現畫布的內容更新操作,在onDraw方法中我們的代碼:

\
我們看一下效果:

\

就是圖片沿著左上角的點開始旋轉。

知識點:

1>、使用Canvas本身的api來操作畫布

6、裁剪畫布

有時候我們可能需要裁剪畫布,就是把畫布上已經畫好的圖形,我們只想要其中的一部分,那麼這時候我們可以使用裁剪技術來實現,比如我們現在想把圖片裁剪成不規則形狀,我們看一下代碼:

 

/**
 * 裁剪畫布
 * @param canvas
 * @param bitmap
 */
public static void clipCanvas(Canvas canvas, Bitmap bitmap){

	canvas.drawColor(Color.GRAY);

	//先裁去完畫布,在繪制內容

	//canvas.clipRect(0, 0, 200, 200);

	Path path = new Path();
	path.lineTo(0, 0);
	path.lineTo(400, 0);
	path.lineTo(100, 300);
	path.lineTo(0, 0);
	canvas.clipPath(path);

	Rect rect1 = new Rect(0,0,bitmap.getWidth(),bitmap.getWidth());
	Rect rect2 = new Rect(0,0,bitmap.getWidth(),bitmap.getHeight());
	canvas.drawBitmap(bitmap, rect1, rect2, new Paint());
}
這裡我們繪制不規則形狀使用Path來實現的,這裡有一點需要注意的是,裁剪畫布也算是操作畫布了,所以還是要遵從一個准則:

 

先操作畫布,在繪制圖形

我們可以看一下效果:

\
擦,被裁成這個吊樣了,我們這裡還看到了,可以使用path來實現繪制不規則圖形的,而且裁剪的api中也支持規則的圖形裁剪,比如是:圓形,矩形等裁剪形狀。

知識點:

1>、使用Path來繪制不規則圖形

2>、隨意裁剪圖片的形狀

四、知識點總結

到這裡我們就介紹完了關於畫布Canvas的一些操作,這些操作可以滿足我們在日常中想要實現的效果了,下面我們就再來整理我們所學習到的知識點:

1> 我們可以使用Bitmap的getPixel方法獲取圖片的像素值
2> 了解到了圖片像素值的表示含義和各個配置之間的差異
3> 修改圖片的透明度和顏色值
4> 手動創建一個Bitmap和Canvas
5> 了解遮罩層技術改變兩張圖片的疊加效果
6> 使用畫布保存技術來繪制想要的特殊效果
7> 理解了畫布的平移,縮放等效果不是真正意義上的操作畫布,而是新建一個圖層,然後進行圖層的疊加操作。
8> 使用矩陣來操作畫布
9> 學習到了矩陣操作
10> 理解了矩陣的前乘和後乘的區別
11> 使用Canvas本身的api來操作畫布
12> 使用Path來繪制不規則圖形
13> 隨意裁剪圖片的形狀

五、總結

我們這篇文章主要介紹了如何操作畫布來實現我們在自定義視圖View的時候想要的效果,這些api我們可以不用記住,但是這些知識點可以記住,因為我們在看到一種UI效果的時候我們可以想到有這些功能就可以了,沒事多看看一些UI上的效果和特效,多想想可以怎麼去實現它,這樣對於我們繪制UI很有幫助,沒事的時候腦補一下還是很有用途的。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved