編輯:關於Android編程
本博客將會介紹動態代理在Android應用中的一種使用場景
代理模式的作用是為其它對象提供一種代理以控制對這個對象的訪問。比如用戶調用了一個“吃飯”的方法,如果不依靠代理,用戶可能自己拿碗飯吃就行,而如果通過代理的話,可能連碗都不需要用戶自己拿,用戶只需要張開嘴,代理來喂就行了,需要注意的是,這裡代理除了負責拿碗和喂飯外還可以做其他的任何事情,比如說幫你把飯吹涼一些,或者擔心你的體重而偷偷幫你倒掉了一半的飯,又或者是往飯裡加點什麼奇奇怪怪的東西,誰知道呢,這就是代理干的活。實際上在java裡面也提供了代理這一神奇的模式,而且還分為靜態和動態兩種,兩者的區別是靜態代理的結構在程序運行前就已經安排好了的,而動態代理則是在程序運行過程中指定的,本文所采用的就是動態代理方法。
在之前的一篇博客《【Android】android鏡像翻轉》中分析過如何對一個View進行鏡像翻轉,也就是實現如下的效果:
鏡像水平翻轉前後效果
對於一般的View而言,直接用view.setScaleY(-1)即可達到這樣的效果,而且針對自定義SurfaceView,也可以使用
canvas.scale(-1,1,canvas.getWidth()/2,canvas.getHeight()/2)
來進行翻轉。但是如果面對的是第三方SurfaceView,且無法直接獲取到SurfaceView用於繪制的canvas對象的話。比如上面左圖實現代碼如下。
public class TestSurfaceView extends SurfaceView implements SurfaceHolder.Callback { public TestSurfaceView(Context context, AttributeSet attrs) { super(context, attrs); getHolder().addCallback(this); } @Override public void surfaceCreated(SurfaceHolder holder) { // 獲取canvas Canvas canvas = holder.lockCanvas(); canvas.drawColor(Color.rgb(220,220,220)); Paint paint = new Paint(); paint.setTextSize(60); // 繪制文字 canvas.drawText("Hello, this is SurfaceView",200,600,paint); // 繪制圓 canvas.drawCircle(300,800,100,paint); // 顯示 holder.unlockCanvasAndPost(canvas); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {} @Override public void surfaceDestroyed(SurfaceHolder holder) {} }
假設TestSurfaceView在第三方Jar包中,且無法對其進行修改。那麼該如何對這個界面進行鏡像翻轉呢,下面給出通過動態代理實現的方案
結合上面的代碼,我們知道,SurfaceView的繪制過程如下:
SurfaceView繪制流程
通常調用代碼如下:
// 獲取canvas畫布 Canvas canvas = holder.lockCanvas(); //繪制內容 ... ... // 解鎖畫布,顯示畫布內容 holder.unlockCanvasAndPost(canvas);holder是什麼呢,我們可以在SurfaceView的源碼裡找到SurfaceHolder的實例mSurfaceHolder以及實現。
public class SurfaceView extends View { ...... private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() { private static final String LOG_TAG = "SurfaceHolder"; @Override public boolean isCreating() { return mIsCreating; } ...... @Override public Canvas lockCanvas() { return internalLockCanvas(null); } @Override public Canvas lockCanvas(Rect inOutDirty) { return internalLockCanvas(inOutDirty); } ...... }; }
代理的目標是要實現對surfaceholder裡的lockCanvas方法進行監控,並為其返回值添加一定的操作,也就是將原先的流程改為如下結構:
動態代理添加訪問控制
首先需要實現的是代理處理器,其代碼如下:
public class TestInvocation implements InvocationHandler { Object mObject ; public TestInvocation(Object object) { mObject = object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 截取lockCanvas方法調用 if ("lockCanvas".equals(method.getName())) { // lockCanvas方法返回值是canvas畫布 Canvas canvas = (Canvas) method.invoke(mObject,args); // 添加鏡像 canvas.scale(-1,1,canvas.getWidth()/2,canvas.getHeight()/2); return canvas; } return method.invoke(mObject,args); } }
完成代理處理器之後就可以為surfaceHolder添加動態代理,這裡需要注意的是surfaceHolder在SurfaceView中,所需先取出來,再為之添加代理,代碼如下:
// 獲取 surfaceView中的 surfaceHolder SurfaceHolder mSurfaceHolder = mTestSurfaceView.getHolder(); // 創建代理接口的實現 TestInvocation testInvocation = new TestInvocation(mSurfaceHolder); // 為 mSurfaceHolder 添加動態代理,並獲取添加代理之後的 newSurfaceHolder SurfaceHolder newSurfaceHolder = (SurfaceHolder) Proxy.newProxyInstance(mSurfaceHolder.getClass().getClassLoader(),mSurfaceHolder.getClass().getInterfaces(),testInvocation);新生成的newSurfaceHolder就是已經添加上動態代理的surfaceHolder,因為在SurfaceView中SurfaceHolder是私有屬性,無法直接替換,所以這裡需要借助反射機制來講newSurfaceHolder替換掉SurfaceView中原先的mSurfaceHolder,代碼如下:
// 獲取mSurfaceHolder的field Field fieldHolder = SurfaceView.class.getDeclaredField("mSurfaceHolder"); // 更改為可訪問權限 fieldHolder.setAccessible(true); // 用添加代理後的 newSurfaceHolder 替換 mSurfaceHolder fieldHolder.set(mTestSurfaceView,newSurfaceHolder);
添加動態代理之後,TestSurfaceView中調用holder的lockCanvas方法所獲取到的canvas都是經過TestInvocation轉置的canvas,從而實現了這個奇怪的需求,也就是實現右側圖片的效果。
Android動態代理實踐
紐扣電池的型號通常在紐扣電池的背面由字母和阿拉伯數字組成,了解一些基本的電池型號知識對生活很有幫助,因為我們日常見到的兒童玩具、電路主板、電子表、遙控器等電
很多的Android入門程序猿來說對於Android自定義View,可能都是比較恐懼的,但是這又是高手進階的必經之路,所有准備在自定義View上面花一些功夫,多寫一些文章
使用SlidingTabLayout需要准備2個類,分別是 SlidingTabLayout,與SlidingTabStrip,,放進項目中時只用修改下包名即可。 效果制
Android中,如果我們想繪制復雜的自定義View或游戲,我們就需要熟悉繪圖API。Android通過Canvas類暴露了很多drawXXX方法,我們可以通過這些方法繪