本文主要對(duì)Android開發(fā)中圖形圖像部分的相關(guān)內(nèi)容作個(gè)簡(jiǎn)單的學(xué)習(xí)總結(jié)。
一薛闪、概述
二、ImageView詳解
三俺陋、Bitmap詳解
四豁延、Canvas詳解
五昙篙、SurfaceView簡(jiǎn)介
六、總結(jié)
一诱咏、概述
在我們的應(yīng)用開發(fā)中苔可,圖形圖像處理往往是不可避免要接觸的內(nèi)容。一個(gè)完整的App袋狞,文字焚辅、圖像、動(dòng)畫等都是用戶交互的重要組成元素苟鸯。本文主要從ImageView開始同蜻,對(duì)Android開發(fā)中與圖形圖像處理相關(guān)的內(nèi)容作個(gè)簡(jiǎn)單的總結(jié)。
二早处、ImageView用法與解析
1湾蔓、ImageView簡(jiǎn)介
ImageView的用途相信大家都已經(jīng)十分的熟悉,應(yīng)用中圖標(biāo)或者圖片的展示一般都需要用到這個(gè)控件砌梆。SDK文檔中對(duì)它的介紹默责,大致翻譯是ImageView,圖像視圖咸包,是用來(lái)展示一個(gè)任意的圖像的控件桃序,例如圖標(biāo)。它可以從不同的來(lái)源載入圖像诉儒,控制圖像的大小葡缰,并提供多種展示選項(xiàng)亏掀,比如縮放比例或者著色器等忱反。
2、ImageView用法
首先我們看下ImageView一個(gè)最簡(jiǎn)單的使用滤愕。
<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代碼動(dòng)態(tài)控制间影,那么src屬性可以用方法setImageResource來(lái)完成注竿。
當(dāng)然,我們也可以配置很多其它屬性魂贬。常用的有scaleType巩割,了解它之前我們來(lái)看兩張對(duì)比圖。
可以看到付燥,scaleType的不同設(shè)置可以控制圖像在ImageView中的顯示樣式宣谈。
scaleType的取值共有8種,分別是:
- MATRIX键科,意思是“矩陣”闻丑,即用矩陣來(lái)繪制漩怎,不縮放
- FIT_XY,不按比例縮放圖片嗦嗡,把圖片塞滿整個(gè)ImageView勋锤。
- FIT_START,置頂侥祭,圖片顯示在ImageView的左上角start的位置
- FIT_CENTER叁执,居中
- FIT_END,置底矮冬,圖片顯示在ImageView的右下角end的位置
- CENTER徒恋,按圖片的原來(lái)尺寸居中顯示,當(dāng)圖片長(zhǎng)/寬超過ImageView的長(zhǎng)/寬欢伏,則截取圖片的居中部分顯示
- CENTER_CROP入挣,按比例擴(kuò)大圖片的size居中顯示,使得圖片長(zhǎng)(寬)等于或大于ImageView的長(zhǎng)(寬)
- CENTER_INSIDE硝拧,圖片完整居中顯示径筏,比例縮小或原來(lái)的size使得圖片長(zhǎng)/寬等于或小于ImageView的長(zhǎng)/寬
一圖勝千言,請(qǐng)看:
在未設(shè)置scaleType時(shí)障陶,系統(tǒng)默認(rèn)保證圖片完整滋恬、居中顯示,并等比縮放抱究,其效果跟android:scaleType=”fitCenter”的效果一致恢氯。
在代碼里控制圖片的縮放樣式是用方法setScaleType來(lái)設(shè)置的」乃拢可以看到勋拟,用好這個(gè)屬性,可以明顯地改善圖片在界面上的顯示效果妈候。
在ImageView的使用過程中敢靡,還有個(gè)tint屬性可以設(shè)置。tint翻譯是著色苦银,它可以用來(lái)改變ImageView中內(nèi)容的顏色啸胧,在代碼中對(duì)應(yīng)的設(shè)置方法是imageview.setColorFilter(Color),我們可以利用這個(gè)屬性來(lái)修改一些圖標(biāo)的顯示顏色幔虏。
三纺念、Bitmap詳解
Bitmap,翻譯為位圖想括,它是一個(gè)final類陷谱,Android系統(tǒng)圖像處理中最重要的類之一。Bitmap可以獲取圖像文件信息主胧,對(duì)圖像進(jìn)行剪切叭首、旋轉(zhuǎn)习勤、縮放,壓縮等操作焙格,并可以以指定格式保存圖像文件图毕。在實(shí)際使用過程中,因?yàn)锽itmap很占內(nèi)存眷唉,所以需要注意進(jìn)行壓縮予颤,高效加載避免OOM。
Bitmap類比較特別冬阳,我們不能通過它的構(gòu)造方法來(lái)實(shí)例化蛤虐,只能通過Bitmap的靜態(tài)方法或者借助BitmapFactory的靜態(tài)方法來(lái)實(shí)例化。類BitmapFactory肝陪,它可以讓我們從不同的來(lái)源驳庭,比如文件,流氯窍,字節(jié)數(shù)組等來(lái)源中創(chuàng)建Bitmap對(duì)象饲常。下面是BitmapFactory的類結(jié)構(gòu)±翘郑可以看到贝淤,它提供了一系列從不同來(lái)源實(shí)例化Bitmap的方法。
下面看個(gè)簡(jiǎn)單的例子政供。
上圖中播聪,已經(jīng)用OkHttp網(wǎng)絡(luò)請(qǐng)求獲取到了字節(jié)數(shù)組,在Handler中處理消息時(shí)布隔,先獲取到字節(jié)數(shù)組离陶,然后利用BitmapFactory的decodeByteArray方法,實(shí)例化Bitmap执泰,最后通過ImageView的setImageBitmap方法將Bitmap設(shè)置為ImageView的內(nèi)容枕磁。
Bitmap特別耗內(nèi)存渡蜻,所以我們?cè)谑褂弥幸肿⒁鈨?yōu)化术吝。常見的措施有,及時(shí)調(diào)用recycle()進(jìn)行內(nèi)存回收茸苇,實(shí)例化Bitmap時(shí)進(jìn)行圖片壓縮排苍,以及采取圖片緩存等。
下面是在使用BitmapFactory的靜態(tài)方法實(shí)例化Bitmap時(shí)進(jìn)行壓縮学密。
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 4;
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
可以看到上面的代碼實(shí)例化參數(shù)Options時(shí)設(shè)置inSampleSize為4淘衙,這樣實(shí)例化的Bitmap占用的內(nèi)存只為原來(lái)的1/16。
1.Bitmap的內(nèi)存占用
上面我們提到腻暮,在使用類BitmapFactory加載Bitmap時(shí)指定Options可以有效的優(yōu)化Bitmap的內(nèi)存占用彤守,那么Bitmap的內(nèi)存占用具體怎么計(jì)算的呢毯侦?這里有個(gè)公式:
占用內(nèi)存 = 圖片寬 * 圖片高 * 單位像素占用的內(nèi)存
這里簡(jiǎn)單說(shuō)明一下,圖片的寬和高比較容易理解具垫,單位像素的占用內(nèi)存一般是由圖片加載時(shí)的色彩模式?jīng)Q定的侈离,android默認(rèn)的色彩模式是ARGB_8888,一個(gè)像素占用的內(nèi)存是4個(gè)字節(jié)筝蚕。
這里還需要注意的是卦碾,圖片的來(lái)源也會(huì)最終影響占用內(nèi)存。如果我們直接從網(wǎng)絡(luò)上獲取圖片起宽,那么圖片的占用內(nèi)存就是上面的公式洲胖。但是如果是從drawable目錄下加載圖片,那么占用內(nèi)存會(huì)因?yàn)椴煌膁rawable目錄而不同坯沪,因?yàn)锳ndroid系統(tǒng)在加載不同drawable目錄下的圖片到不用分辨率的機(jī)型上時(shí)會(huì)進(jìn)行一定的縮放绿映。
2.Bitmap的高效加載
BitmapFactory加載Bitmap時(shí)指定Options是一個(gè)比較好的習(xí)慣,那么Options的inSampleSize指定多少比較合適呢腐晾?其實(shí)最理想的情況是我們根據(jù)需要顯示的ImageView的大小計(jì)算出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的緩存
項(xiàng)目中如果需要加載圖片,那么圖片緩存的設(shè)計(jì)非常重要赴魁。一般來(lái)說(shuō)卸奉,圖片緩存會(huì)涉及到內(nèi)存緩存和磁盤緩存,當(dāng)需要加載圖片時(shí)颖御,先看內(nèi)存中有沒有榄棵,沒有的話再看磁盤緩存中有沒有,最后再嘗試網(wǎng)絡(luò)請(qǐng)求獲取圖片潘拱。
內(nèi)存緩存和磁盤緩存目前比較常用的算法是LRU(Least Recently Used)疹鳄,即近期最少使用算法,內(nèi)存緩存我們可以借助LruCache這個(gè)類來(lái)完成芦岂,磁盤緩存可以用DiskLruCache瘪弓,它們共同的原理是指定一個(gè)緩存的總大小,當(dāng)緩存滿時(shí)禽最,優(yōu)先淘汰那些近期最少使用的緩存的對(duì)象腺怯。
四、Canvas解析
說(shuō)到圖形圖像川无,那么肯定離不開要談Canvas這個(gè)類呛占。當(dāng)我們想要繪制自己的圖形時(shí),或者在自定義View中懦趋,一般都會(huì)用到它晾虑。Canvas,直譯為畫布,SDK文檔對(duì)它的介紹如下:
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個(gè)基本元素:一是Bitmap,用來(lái)包含像素笙隙。二是Canvas继找,包含畫的方法。三是畫的參數(shù)逃沿,比如寬高等屬性婴渡。四是Paint,畫筆凯亮,用來(lái)描述繪畫的顏色和樣式边臼。
同樣的,我們來(lái)看個(gè)簡(jiǎn)單的例子假消。
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)建了一個(gè)繼承自View的子類,構(gòu)造函數(shù)里我們初始化了畫筆Paint富拗,設(shè)置顏色為紅色臼予。在onDraw()方法里,我們使用Canvas內(nèi)部的幾個(gè)draw方法繪制不同的圖形啃沪,drawText是繪制文本粘拾,drawCircle是繪制圓...
最后效果如下:
除了繪制各種基本的圖形,我們也可以通過drawBitmap來(lái)繪制圖片创千。實(shí)際開發(fā)中缰雇,利用Canvas、Paint等類追驴,我們可以創(chuàng)造出很出酷炫的自定義圖形械哟。
五、SurfaceView簡(jiǎn)介
自己之前對(duì)SurfaceView這個(gè)類不是很熟悉殿雪,這次特地查閱了官方文檔結(jié)合相關(guān)資料暇咆,這里簡(jiǎn)單地作個(gè)介紹。
View在大部分情況下可以滿足我們的繪圖需求丙曙,但是當(dāng)需要頻繁刷新爸业,或者刷新時(shí)數(shù)據(jù)量比較大時(shí),容易造成畫面卡頓河泳,SurfaceView是用來(lái)在上述兩種情況下替代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提供了一個(gè)專門用于繪圖的surface,它嵌入在View的內(nèi)部拆挥。我們可以控制這個(gè)surface的格式和大小,SurfaceView主要負(fù)責(zé)把surface放置在屏幕上的正確位置。
SurfaceView可以直接從內(nèi)存或者DMA等硬件接口取得圖像數(shù)據(jù)纸兔,因此是個(gè)非常重要的繪圖容器惰瓜。
六、總結(jié)
在實(shí)際開發(fā)中汉矿,我們可以為顯示的圖形圖像增加很多的特效崎坊,這部分內(nèi)容這里就不詳細(xì)介紹了≈弈矗總的來(lái)說(shuō)奈揍,我們可以利用ColorMatrix顏色矩陣類來(lái)處理圖像的色彩效果,Android系統(tǒng)利用矩陣來(lái)進(jìn)行圖像的圖形變換赋续,當(dāng)然男翰,我們還可以充分地利用Android中的動(dòng)畫來(lái)完成圖形圖像的動(dòng)態(tài)效果展示。
關(guān)于Android中的圖形圖像部分的學(xué)習(xí)總結(jié)就是這些纽乱,當(dāng)然實(shí)踐出真知蛾绎,只有在實(shí)際開發(fā)中不斷摸索不斷嘗試,才能更好地掌握Android中的這些圖形圖像處理鸦列,創(chuàng)造出更多更酷炫的自定義效果租冠。