前面我們介紹了使用RenderScript使另一個檢視範圍內的圖片部分模糊。但是實際上,我們並沒有深入地呼叫這個方法來研究影象模糊行為。原因是我們需要在效能方面進行仔細考慮,這篇文章我們會進行更進一步地的探索。
呼叫這個方法最直白的方法是父佈局的onDraw()
。有經驗的開發者讀到這裡可能會開始搖頭,我們應該保持onDraw
方法的實現儘可能有效。以前的文章中的程式碼包括建立物件、點陣圖操作和切換到renderScript
上下文。其中,OnDraw
會降低幀速率。你可以不相信我的做法,但是可以通過測量並證明它是有效的。在後面的系列中,我們就會這樣做。
如果佈局是靜態的(即我們的佈局不包含任何動畫),在佈局時就不會改變待模糊的位置和範圍。只有在佈局改變時執行該操作才有意義,但前提是佈局的所有檢視大小和位置根據佈局變化測量和計算過。這裡有一個非常實用的技巧,可以註冊一個OnGlobalLayoutListener
監聽函式。當佈局發生改變的時候會呼叫onGlobalLayout()
方法。當我們收到佈局已經改變的通知時,註冊的OnPreDrawListener
監聽函式的onPreDraw()
方法會被呼叫每當執行onDraw
方法。我們要做的第一件事情就是取消註冊onPreDraw()
方法,這樣只有在佈局改變的時候才會被呼叫,而不是每次onDraw
方法觸發時都呼叫。下面可以執行模糊方法,從這個方法的返回值很重要,使用它可以讓我們放棄onDraw
操作,重複之前的佈局。這對在回撥函式中修改佈局非常有幫助,但是這裡不需要這麼做。所以返回true,繼續繪製。
我們的Activity
程式碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
; html-script: false ] public class MainActivity extends Activity { private ImageView mImage; private TextView mText; private OnPreDrawListener mPreDrawListener = new OnPreDrawListener() { @Override public boolean onPreDraw() { ViewTreeObserver observer = mText.getViewTreeObserver(); if(observer != null) { observer.removeOnPreDrawListener(this); } Drawable drawable = mImage.getDrawable(); if (drawable != null && drawable instanceof BitmapDrawable) { Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap(); if (bitmap != null) { blur(bitmap, mText, 25); } } return true; } }; private OnGlobalLayoutListener mLayoutListener = new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { ViewTreeObserver observer = mText.getViewTreeObserver(); if(observer != null) { observer.addOnPreDrawListener( mPreDrawListener); } } }; /** * Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mImage = (ImageView) findViewById(R.id.image); mText = (TextView)findViewById(R.id.text); if (mImage != null && mText != null) { ViewTreeObserver observer = mText.getViewTreeObserver(); if (observer != null) { observer.addOnGlobalLayoutListener( mLayoutListener); } } } private void blur(Bitmap bkg, View view, float radius) { . . . } } |
在父佈局覆蓋onDraw
方法的優點是,可以在佈局層次上任意附加OnPreDrawListener
方法。因此需要自定義一個佈局,這個佈局是標準佈局的子類,這樣就可以覆蓋onDraw
方法。使用predrawlistener
意味著可以非常容易在任意佈局中新增。
最後,模糊圖片實現如下:
在下一篇文章中,我會更深入地回答為什麼要避免在onDraw
中執行模糊操作,並且還會介紹有用的效能測量工具。
這篇文章的程式碼在這裡。