本文主要對Android開發(fā)中圖形圖像部分的相關(guān)內(nèi)容作個簡單的學(xué)習(xí)總結(jié)。
一、概述
二、ImageView詳解
三、Bitmap詳解
四、Canvas詳解
五、SurfaceView簡介
六、總結(jié)
一、概述
在我們的應(yīng)用開發(fā)中,圖形圖像處理往往是不可避免要接觸的內(nèi)容。一個完整的App,文字、圖像、動畫等都是用戶交互的重要組成元素。本文主要從ImageView開始,對Android開發(fā)中與圖形圖像處理相關(guān)的內(nèi)容作個簡單的總結(jié)。
二、ImageView用法與解析
1、ImageView簡介
ImageView的用途相信大家都已經(jīng)十分的熟悉,應(yīng)用中圖標(biāo)或者圖片的展示一般都需要用到這個控件。SDK文檔中對它的介紹,大致翻譯是ImageView,圖像視圖,是用來展示一個任意的圖像的控件,例如圖標(biāo)。它可以從不同的來源載入圖像,控制圖像的大小,并提供多種展示選項,比如縮放比例或者著色器等。
2、ImageView用法
首先我們看下ImageView一個最簡單的使用。
<ImageView
android:id="@+id/iv_scaletype_test"
android:layout_width="200dp"
android:layout_height="200dp"
android:src="@drawable/img_test" />
當(dāng)在布局文件里指定ImageView的寬高以及src屬性后,一張圖片就可以展示在ImageView上了。如果是用Java代碼動態(tài)控制,那么src屬性可以用方法setImageResource來完成。
當(dāng)然,我們也可以配置很多其它屬性。常用的有scaleType,了解它之前我們來看兩張對比圖。
可以看到,scaleType的不同設(shè)置可以控制圖像在ImageView中的顯示樣式。
scaleType的取值共有8種,分別是:
- MATRIX,意思是“矩陣”,即用矩陣來繪制,不縮放
- FIT_XY,不按比例縮放圖片,把圖片塞滿整個ImageView。
- FIT_START,置頂,圖片顯示在ImageView的左上角start的位置
- FIT_CENTER,居中
- FIT_END,置底,圖片顯示在ImageView的右下角end的位置
- CENTER,按圖片的原來尺寸居中顯示,當(dāng)圖片長/寬超過ImageView的長/寬,則截取圖片的居中部分顯示
- CENTER_CROP,按比例擴大圖片的size居中顯示,使得圖片長(寬)等于或大于ImageView的長(寬)
- CENTER_INSIDE,圖片完整居中顯示,比例縮小或原來的size使得圖片長/寬等于或小于ImageView的長/寬
一圖勝千言,請看:
在未設(shè)置scaleType時,系統(tǒng)默認保證圖片完整、居中顯示,并等比縮放,其效果跟android:scaleType=”fitCenter”的效果一致。
在代碼里控制圖片的縮放樣式是用方法setScaleType來設(shè)置的。可以看到,用好這個屬性,可以明顯地改善圖片在界面上的顯示效果。
在ImageView的使用過程中,還有個tint屬性可以設(shè)置。tint翻譯是著色,它可以用來改變ImageView中內(nèi)容的顏色,在代碼中對應(yīng)的設(shè)置方法是imageview.setColorFilter(Color),我們可以利用這個屬性來修改一些圖標(biāo)的顯示顏色。
三、Bitmap詳解
Bitmap,翻譯為位圖,它是一個final類,Android系統(tǒng)圖像處理中最重要的類之一。Bitmap可以獲取圖像文件信息,對圖像進行剪切、旋轉(zhuǎn)、縮放,壓縮等操作,并可以以指定格式保存圖像文件。在實際使用過程中,因為Bitmap很占內(nèi)存,所以需要注意進行壓縮,高效加載避免OOM。
Bitmap類比較特別,我們不能通過它的構(gòu)造方法來實例化,只能通過Bitmap的靜態(tài)方法或者借助BitmapFactory的靜態(tài)方法來實例化。類BitmapFactory,它可以讓我們從不同的來源,比如文件,流,字節(jié)數(shù)組等來源中創(chuàng)建Bitmap對象。下面是BitmapFactory的類結(jié)構(gòu)。可以看到,它提供了一系列從不同來源實例化Bitmap的方法。
下面看個簡單的例子。
上圖中,已經(jīng)用OkHttp網(wǎng)絡(luò)請求獲取到了字節(jié)數(shù)組,在Handler中處理消息時,先獲取到字節(jié)數(shù)組,然后利用BitmapFactory的decodeByteArray方法,實例化Bitmap,最后通過ImageView的setImageBitmap方法將Bitmap設(shè)置為ImageView的內(nèi)容。
Bitmap特別耗內(nèi)存,所以我們在使用中要十分注意優(yōu)化。常見的措施有,及時調(diào)用recycle()進行內(nèi)存回收,實例化Bitmap時進行圖片壓縮,以及采取圖片緩存等。
下面是在使用BitmapFactory的靜態(tài)方法實例化Bitmap時進行壓縮。
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 4;
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
可以看到上面的代碼實例化參數(shù)Options時設(shè)置inSampleSize為4,這樣實例化的Bitmap占用的內(nèi)存只為原來的1/16。
1.Bitmap的內(nèi)存占用
上面我們提到,在使用類BitmapFactory加載Bitmap時指定Options可以有效的優(yōu)化Bitmap的內(nèi)存占用,那么Bitmap的內(nèi)存占用具體怎么計算的呢?這里有個公式:
占用內(nèi)存 = 圖片寬 * 圖片高 * 單位像素占用的內(nèi)存
這里簡單說明一下,圖片的寬和高比較容易理解,單位像素的占用內(nèi)存一般是由圖片加載時的色彩模式?jīng)Q定的,android默認的色彩模式是ARGB_8888,一個像素占用的內(nèi)存是4個字節(jié)。
這里還需要注意的是,圖片的來源也會最終影響占用內(nèi)存。如果我們直接從網(wǎng)絡(luò)上獲取圖片,那么圖片的占用內(nèi)存就是上面的公式。但是如果是從drawable目錄下加載圖片,那么占用內(nèi)存會因為不同的drawable目錄而不同,因為Android系統(tǒng)在加載不同drawable目錄下的圖片到不用分辨率的機型上時會進行一定的縮放。
2.Bitmap的高效加載
BitmapFactory加載Bitmap時指定Options是一個比較好的習(xí)慣,那么Options的inSampleSize指定多少比較合適呢?其實最理想的情況是我們根據(jù)需要顯示的ImageView的大小計算出Options的inSampleSize。
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
3.Bitmap的緩存
項目中如果需要加載圖片,那么圖片緩存的設(shè)計非常重要。一般來說,圖片緩存會涉及到內(nèi)存緩存和磁盤緩存,當(dāng)需要加載圖片時,先看內(nèi)存中有沒有,沒有的話再看磁盤緩存中有沒有,最后再嘗試網(wǎng)絡(luò)請求獲取圖片。
內(nèi)存緩存和磁盤緩存目前比較常用的算法是LRU(Least Recently Used),即近期最少使用算法,內(nèi)存緩存我們可以借助LruCache這個類來完成,磁盤緩存可以用DiskLruCache,它們共同的原理是指定一個緩存的總大小,當(dāng)緩存滿時,優(yōu)先淘汰那些近期最少使用的緩存的對象。
四、Canvas解析
說到圖形圖像,那么肯定離不開要談Canvas這個類。當(dāng)我們想要繪制自己的圖形時,或者在自定義View中,一般都會用到它。Canvas,直譯為畫布,SDK文檔對它的介紹如下:
The Canvas class holds the "draw" calls. To draw something, you need 4 basic components: A Bitmap to hold the pixels, a Canvas to host the draw calls (writing into the bitmap), a drawing primitive (e.g. Rect, Path, text, Bitmap), and a paint (to describe the colors and styles for the drawing).
Canvas類包含很多draw的方法,要畫某樣?xùn)|西,一般需要4個基本元素:一是Bitmap,用來包含像素。二是Canvas,包含畫的方法。三是畫的參數(shù),比如寬高等屬性。四是Paint,畫筆,用來描述繪畫的顏色和樣式。
同樣的,我們來看個簡單的例子。
public class MyDrawView extends View {
private Paint mPaint;
public MyDrawView(Context context) {
super(context);
mPaint = new Paint();
mPaint.setColor(Color.RED);// 設(shè)置畫筆顏色為紅色
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setTextSize(50f);
canvas.drawText("畫圓:", 100, 200, mPaint);// 畫文本
canvas.drawCircle(300, 200, 80, mPaint);// 小圓
mPaint.setAntiAlias(true);// 設(shè)置畫筆的鋸齒效果
canvas.drawCircle(600, 200, 150, mPaint);// 大圓
canvas.drawText("畫線:", 100, 600, mPaint);
canvas.drawLine(600, 400, 1000, 400, mPaint);// 畫線
canvas.drawLine(600, 400, 1000, 800, mPaint);// 斜線
}
}
在上面的代碼中,我們創(chuàng)建了一個繼承自View的子類,構(gòu)造函數(shù)里我們初始化了畫筆Paint,設(shè)置顏色為紅色。在onDraw()方法里,我們使用Canvas內(nèi)部的幾個draw方法繪制不同的圖形,drawText是繪制文本,drawCircle是繪制圓...
最后效果如下:
除了繪制各種基本的圖形,我們也可以通過drawBitmap來繪制圖片。實際開發(fā)中,利用Canvas、Paint等類,我們可以創(chuàng)造出很出酷炫的自定義圖形。
五、SurfaceView簡介
自己之前對SurfaceView這個類不是很熟悉,這次特地查閱了官方文檔結(jié)合相關(guān)資料,這里簡單地作個介紹。
View在大部分情況下可以滿足我們的繪圖需求,但是當(dāng)需要頻繁刷新,或者刷新時數(shù)據(jù)量比較大時,容易造成畫面卡頓,SurfaceView是用來在上述兩種情況下替代View的。
下面是官方文檔的介紹
Provides a dedicated drawing surface embedded inside of a view hierarchy. You can control the format of this surface and, if you like, its size; the SurfaceView takes care of placing the surface at the correct location on the screen
SurfaceView提供了一個專門用于繪圖的surface,它嵌入在View的內(nèi)部。我們可以控制這個surface的格式和大小,SurfaceView主要負責(zé)把surface放置在屏幕上的正確位置。
SurfaceView可以直接從內(nèi)存或者DMA等硬件接口取得圖像數(shù)據(jù),因此是個非常重要的繪圖容器。
六、總結(jié)
在實際開發(fā)中,我們可以為顯示的圖形圖像增加很多的特效,這部分內(nèi)容這里就不詳細介紹了。總的來說,我們可以利用ColorMatrix顏色矩陣類來處理圖像的色彩效果,Android系統(tǒng)利用矩陣來進行圖像的圖形變換,當(dāng)然,我們還可以充分地利用Android中的動畫來完成圖形圖像的動態(tài)效果展示。
關(guān)于Android中的圖形圖像部分的學(xué)習(xí)總結(jié)就是這些,當(dāng)然實踐出真知,只有在實際開發(fā)中不斷摸索不斷嘗試,才能更好地掌握Android中的這些圖形圖像處理,創(chuàng)造出更多更酷炫的自定義效果。