標籤:super 問題 螢幕 int and 清理 ble XML 視窗
最近做項目遇到一個問題,需要在螢幕上即時的顯示手的座標,這樣話就涉及到一個即時畫圖的問題了。對於即時更新UI這個問題,懂點android的都知道,android的UI更新都需要在主線程中更新,但是如果將一個即時繪圖的操作放在主線程,必定會出現阻塞主線程的問題,即便是不阻塞主線程,也會降低程式啟動並執行速度。因此,我就想到android的一個控制項,surfaceView。這個控制項在官網上是這樣描述的:
SurfaceView是視圖(View)的繼承類,這個視圖裡內嵌了一個專門用於繪製的Surface。你可以控制這個Surface的格式和尺寸。Surfaceview控制這個Surface的繪製位置。
surface是縱深排序(Z-ordered)的,這表明它總在自己所在視窗的後面。surfaceview提供了一個可見地區,只有在這個可見地區內 的surface部分內容才可見,可見地區外的部分不可見。surface的排版顯示受到視圖層級關係的影響,它的兄弟視圖結點會在頂端顯示。這意味者 surface的內容會被它的兄弟視圖遮擋,這一特性可以用來放置遮蓋物(overlays)(例如,文本和按鈕等控制項)。注意,如果surface上面 有透明控制項,那麼它的每次變化都會引起架構重新計算它和頂層控制項的透明效果,這會影響效能。
你可以通過SurfaceHolder介面訪問這個surface,getHolder()方法可以得到這個介面。
surfaceview變得可見時,surface被建立;surfaceview隱藏前,surface被銷毀。這樣能節省資源。如果你要查看 surface被建立和銷毀的時機,可以重載surfaceCreated(SurfaceHolder)和 surfaceDestroyed(SurfaceHolder)。
surfaceview的核心在於提供了兩個線程:UI線程和渲染線程。這裡應注意:
1> 所有SurfaceView和SurfaceHolder.Callback的方法都應該在UI線程裡調用,一般來說就是應用程式主線程。渲染線程所要訪問的各種變數應該作同步處理。
2> 由於surface可能被銷毀,它只在SurfaceHolder.Callback.surfaceCreated()和 SurfaceHolder.Callback.surfaceDestroyed()之間有效,所以要確保渲染線程訪問的是合法有效surface。
通過這段話,我們可以知道,surfaceView提供了UI線程。可以自己更新UI,因此,這樣我們在surfaceView中進行即時的繪畫,然後通過更改其中的繪畫的資料,既可以實現我們想要的即時的更新UI的這個問題了,並且消耗較小的資源。那麼接下來我們來具體談下實現的問題。
那麼如何使用surfaceView實現我們的需求呢?首先我們需要自訂一個控制項,並且繼承surfaceView這個類。並且要在其中實現SurfaceHolder.Callback的介面。之所以實現介面,是因為我們要確保我們所做的操作需要在surfaceView建立後才能開始。然後我們來具體看下這個函數需要實現的三個函數。
new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder surfaceHolder) { //建立時需要執行的操作 } @Override public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) { //surfaceView大小改變時需要執行的操作 } @Override public void surfaceDestroyed(SurfaceHolder surfaceHolder) { //銷毀時進行的操作。 }}
然後我們如果要繪製內容,並且即時的更新,需要在surfaceCreated中開一個線程,然後即時的進行繪製。這樣即完成我們所需要的需求了。但是在繪製的時候需要有一些注意的地方。這個在接下來呈現出代碼後我們來具體分析和具體的看,接下來是自訂view的具體的代碼。我們來具體的看下。
public class MyView extends SurfaceView { private final String TAG = "acmTest"; private SurfaceHolder mSurfaceHolder; private Canvas mCanvas;//畫布 public boolean isRunning;//用來標識是否開啟繪畫的線程。 Context context; int positionX;//所繪製圖形的x座標 int positionY;//所繪製圖形的y座標 public MyView(Context context) { super(context); init(); } public MyView(Context context, AttributeSet attributeSet) { super(context,attributeSet); this.context = context; init(); } //初始化surfaceView,在這個函數中,實現surfaceHolder.Callback public void init() { setBackgroundColor(Color.parseColor("#6A14b7f5"));//設定surfaceView的背景顏色 setZOrderOnTop(true);//使surfaceview放到最頂層 getHolder().setFormat(PixelFormat.TRANSLUCENT);//使視窗支援透明度 mSurfaceHolder = getHolder(); mSurfaceHolder.addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder surfaceHolder) { //啟動開始繪畫的線程,這個線程中的函數不需要再在主線程中執行,可以直接執行並更新UI。 new Thread(new Runnable() { @Override public void run() { while (isRunning) { drawPoint(); } } }).start(); } @Override public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) { } @Override public void surfaceDestroyed(SurfaceHolder surfaceHolder) { } }); } //具體繪畫的操作 public void drawPoint() { //需要注意這裡需要通過這個方式獲得canvas,不能夠自己申明,為了保證從始至終用的一個canvas。 mCanvas = mSurfaceHolder.lockCanvas(); if (mCanvas != null) { try { mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);//是清理上次畫的內容,這個根據需求選擇是否添加。因為我的需求是跟蹤位置,如果不加,會將上次所畫的內容保留。 Paint paint = new Paint();//聲明畫筆,在此基礎上進行繪製 paint.setColor(Color.RED);//為紅色畫筆 mCanvas.drawCircle(positionX,positionY,10,paint); } catch (Exception e) { e.printStackTrace(); } finally { mSurfaceHolder.unlockCanvasAndPost(mCanvas);//提交後會將提交的畫布更新,然後顯示 } } } public void setPosition(int positionX,int positionY) { this.positionX = positionX; this.positionY = positionY; }}
這是通過繼承surfaceView來進行實現即時的更新UI,然後佔用少量記憶體。值得注意的時候,我們在初始化的時候需要考慮啊init的前三行代碼,第一個設定背景顏色,不要再xml中設定,需要在這設定,否則預設背景為純黑的。然後後兩句是支援為透明的顏色,和保持能夠在布局的最上方。
還有一點就是在擷取畫布這,需要嚴格的按照規定,先擷取,再提交更新,在其中不可重新聲明,並且提交和擷取的需要為同一個canvas。
android之surfaceView學習