編輯:Android編程入門
Android開發過程中,想必都使用過PhotoView來實現圖片展示的功能。在最新版的sdk(android-23)有了一個原生的photoView,並且代碼實現也很簡單,邏輯也很清晰。我們在實際的工作中,遇到的需求可能與這些photoview現有功能有些細微的差別,需要修改,或者重新開發。本文簡單介紹下android-23中photoview涉及到的相關技術,相信讀者看完後會發現,其實很簡單。以下為實現思路和步驟
通過自定義視圖,繼承View,為外界提過public接口方法來設置drawable,或者提供自定義屬性在layout文件中設置drawable。在onDraw方法中,將drawable畫到canvas上。
圖片首次顯示時,一般要居中全部顯示在屏幕內,並且至少x軸或y軸方向占滿屏幕。如何實現?
對於一個drawable,可以獲取其原始的寬、高:
int drawableWidth = mDrawable.getIntrinsicWidth(); int drawableHeight = mDrawable.getIntrinsicHeight();
然後將原始的寬、高,保存到一個RectF對象中:
private RectF mTmpRectSrc = new Rect();
mTmpRectSrc.set(0, 0, drawableWidth, drawableHeight);
自定義的視圖在measure完之後,會有一個寬、高:
int viewWidth = getWidth(); int viewHeight = getHeight();
將view的寬、高也保存到一個RectF對象中:
private RectF mTmpRectDst = new RectF(); mTmpRectDst.set(0, 0, viewWidth, viewHeight);
這時候,可以計算出一個從 mTmpRectSrc 到 mTmpRectDst的變換矩陣:
private Matrix mDrawMatrix = new Matrix(); mDrawMatrix.setRectToRect(mTmpRectSrc, mTmpRectDst, Matrix.ScaleToFit.CENTER);
Matrix.scaleToFit.CENTER參數確保圖片最後可以:居中全部顯示在屏幕內,並且至少x軸或y軸方向占滿屏幕。
最後,在onDraw方法中,結合上面到到的mDrawMatrix,即可將drawable畫到canvas上:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mDrawable != null) {
int saveCount = canvas.getSaveCount();
canvas.save();
canvas.concat(mDrawMatrix);
mDrawable.draw(canvas);
canvas.restoreToCount(saveCount);
}
}
上面講到了兩個RectF區域,和一個matrix。分別是:
mTmpRectSrc: drawable原始矩形區域
ScaleGestureDetector GestureDetector
mTmpRectDst: view顯示矩形區域
mDrawMatrix: 從mTmpRectSrc到mTmpRectDst的變換矩陣
如何的到矩陣變換後,圖片的實際顯示區域?這裡要注意,mTmpRectDst並非圖片的顯示區域,這個矩形區域僅僅是用來限定圖片的顯示區域,而最終的顯示區域並不是他。方法很簡單:
private RectF mTranslateRect = new Rect(); mDrawMatrix.mapRect(mTranslateRect, mTmpRectSrc);
然後通過mTranslateRect,就可以獲取最終顯示圖片矩形區域的letf/top/right/bottom
我們可以自己在onTouchEvent方法中識別出滑動、縮放等手勢。但朋友們是否有感覺,這些判斷邏輯自己實現起來很繁瑣,很難保證邏輯的完美無缺。其實android系統提供了兩個很好用的輔助類來幫我們做這些事情:
GestureDetector ScaleGestureDetector
GestureDetector可以用來識別滑動(scroll),ScaleGestureDetector可以用來識別縮放(scale)
我們來看看如何處理scroll。
首先定一個GestureDetector:
mGestureDetector = new GestureDetector(context, this);
this是指GestureDetector.OnGestureListener的實現類,這裡可以用view自己來實現。實現的onScroll方法如下:
@Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { mDrawMatrix.mapRect(mTranslateRect, mTmpRectSrc); distanceX = -distanceX; distanceY = -distanceY; mDrawMatrix.postTranslate(distanceX, distanceY); invalidate(); return true; }
直接將接口傳給我們的x,y方向偏移量設置到matrix中,然後invalidate,view重新draw出來的drawable就移動了。實際應用中,我們可以先獲取drawable的實際顯示矩形區域,再根據x,y方向偏移量,計算得出最後的偏移量,來限制不可以將圖片劃出view顯示區域。
還差一步,需要在view的onTouchEvent方法中,將MotionEvent事件傳給GestureDetector,這樣就可以用手指來移動圖片了:
@Override public boolean onTouchEvent(MotionEvent event) { mGestureDetector.onTouchEvent(event); return true; }
如果少了這步,GestureDetector根據接觸不到MotionEvent,也就無法幫我們識別相關手勢。
處理縮放幾本一樣,首先定義detector:
private ScaleGestureDetector mScaleGestureDetector = new ScaleGestureDetector(context, this);
this是ScaleGestureDetector.OnScaleGestureListener的實現類,這裡可以用view自己來實現。實現的方法為:
@Override public boolean onScale(ScaleGestureDetector detector) { float factor = detector.getScaleFactor(); mDrawMatrix.mapRect(mTranslateRect, mTmpRectSrc); int width = getWidth(); float centerX; if (factor > 1 && mTranslateRect.right - mTranslateRect.left < width) { centerX = mTranslateRect.left * width / (mTranslateRect.left + width - mTranslateRect.right); } else { centerX = detector.getFocusX(); } int height = getHeight(); float centerY; if (factor > 1 && mTranslateRect.bottom - mTranslateRect.top < height) { centerY = mTranslateRect.top * height / (mTranslateRect.top + height - mTranslateRect.bottom); } else { centerY = detector.getFocusY(); } mDrawMatrix.postScale(factor, factor, centerX, centerY); invalidate(); return true; } @Override public boolean onScaleBegin(ScaleGestureDetector detector) { return true; } @Override public void onScaleEnd(ScaleGestureDetector detector) { }
首先,onScaleBegin方法要返回true,否則不會回調onScale。在onScale中,可以獲取縮放中心點、縮放比例,以及當前圖片的實際顯示區域,根據實際需求,計算出一個最終的縮放比例、中心點。最後將結果postScale到matrix中,invalidate,view重新draw之後,就可看到縮放的效果
最後,需要在view的onTouchEvent方法中,將觸摸時間告知scaleDetector。否則,scaleDetector根本接觸不到相關事件,不會回調相關方法:
@Override public boolean onTouchEvent(MotionEvent event) { mScaleGestureDetector.onTouchEvent(event); mGestureDetector.onTouchEvent(event); return true; }
通過以上講解,可以看出只要我們熟悉了matrix、rect的相關用法,Photoviewer的實現技術其實很簡單。常見的fling、snap動畫效果,photoview中也有實現,讀者感興趣的話,可以查看源碼自行研究。
Camera的架構與Android系統的整體架構保持一致,如下圖所示,本文主要從以下四個方面對其進行說明。 Framework:Camera.java Android
Android其本質就是在標准的Linux系統上增加了Java虛擬機Dalvik,並在Dalvik虛擬機上搭建了一個JAVA的application framework,
上一章講述了Android界面開發中的Widget,Service,BroadcastReceiver基本知識點,本章以一個實際案例-後台音樂播放器解析各個知識點之間的關
一,簡述線程池:線程池是如何工作的:一系列任務出現後,根據自己的線程池安排任務進行。如圖: 線程池的好處:重用線程池中的線程,避免因為線程的創建和銷毀所帶來的性能開銷。能