最近在看Android中性能優化的,其中提到了LinearLayout會引發overdraw,可是並無具體的分析緣由,我本身查找了一些資料從LinearLayout的繪製等方面來講明爲何使用LinearLayout會引發overdraw和哪些狀況下使用LinearLayout會引發overdraw。但願你們看完以後對view的繪製和測量過程更加了解。html
Android中在屏幕上繪製一個像素會花必定的時間,若是在屏幕的同一個位置屢次繪製就會花大量的時間,屢次在屏幕上同一位置繪製的狀況就成爲overdraw。overdraw會很是影響應用的性能。一個高效的佈局要作到兩點: 一、減小overdraw。java
二、簡化佈局結構。android
看下一個overdraw的例子 git
能夠經過手機的設置來直觀的查看應用的overdraw狀況。 一、打開設置,打開開發者選項。 二、選擇調試GPU過分渲染。 三、打開顯示過分渲染區域。 github
一、減小沒必要要的backgroundcanvas
由於有background的時候會先繪製一遍background而後再在background的基礎上面繪製其餘元素,因此會增長一次overdraw。 全部用戶看不到的background都應該刪除掉。 以下面的這個例子:性能優化
刪除background以前app
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/beach"
android:background="@android:color/white">
</ImageView>
複製代碼
刪除background以後less
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/beach" >
</ImageView>
複製代碼
由於imageView的background在有src的時候根本就不會被用戶觀察到,因此應該刪除掉。ide
二、統一整個app的background顏色
能夠經過設置整個app的統一背景色來防止不一樣的activity設置不一樣的背景色而致使多個background。能夠在AndroidManifest.xml添加:
android:theme="@android:style/Theme.Light"
複製代碼
或者想要的背景色。
clip具備裁剪功能,在自定義view的時候,渲染的圖像可能和用戶觀察到的圖像不同,自定義view的ondraw方法裏面不只僅只渲染用戶可見的圖像並且會渲染到被遮擋的圖像,使用clip方法裁剪出用戶可見的區域,這樣能夠減小overdraw。
使用clipRect()方法
在自定義view中使用 Canvas.clipRect()方法能夠有效的減小overdraw。這個方法能夠爲自定義的view提供一個rectangle 區域,而且只有在這個區域中的內容纔會被繪製。上面的撲克牌就可使用Canvas.clipRect()來減小繪製。以下:
未使用clipRect()的自定義view
/** * Created time 20:54. * * @author huhanjun * @since 2019/6/12 */
public class MyView extends View {
private Paint mPaint;
private Bitmap mBitmap;
private int mPadding = 0;
public MyView(Context context) {
super(context);
}
public MyView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.over_draw);
}
public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint = new Paint();
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.over_draw);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int i = 0; i < 5; i++) {
canvas.save();
canvas.drawBitmap(mBitmap,mPadding,0,mPaint);
canvas.restore();
mPadding += 200;
}
}
}
複製代碼
在開啓檢測overdraw後的顯示效果以下:
使用clipRect()的自定義view
/** * Created time 20:54. * * @author huhanjun * @since 2019/6/12 */
public class MyView extends View {
private Paint mPaint;
private Bitmap mBitmap;
private int mPadding = 0;
public MyView(Context context) {
super(context);
}
public MyView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.over_draw);
}
public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint = new Paint();
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.over_draw);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int i = 0; i < 5; i++) {
canvas.save();
Rect rect = new Rect(mPadding, 0, 200+mPadding, mBitmap.getWidth() + mPadding);
canvas.clipRect(rect);//此處增長clipRect方法
canvas.drawBitmap(mBitmap,mPadding,0,mPaint);
canvas.restore();
mPadding += 200;
}
}
}
複製代碼
運行後的顯示效果以下:
Alpha是圖形界面開發中經常使用的特效,一般咱們會使用如下代碼來實現Alpha特效:
view.setAlpha(0.5f);
View.ALPHA.set(view, 0.5f);
ObjectAnimator.ofFloat(view, "alpha", 0.5f).start();
view.animate().alpha(0.5f).start();
view.setAnimation(new AlphaAnimation(1.0f, 0.5f));
複製代碼
其效果都等同於:
canvas.saveLayer(l, r, t, b, 127, Canvas.CLIP_TO_LAYER_SAVE_FLAG);
複製代碼
渲染帶有透明度像素被稱爲:alpha rendering,alpha rendering會致使overdraw,由於系統會先渲染透明像素,而後渲染透明像素下面的view的像素,最後結合二者,從而產生透明度的效果,所以會致使overdraw。以下圖:
Android Performance Case
getAlpha() returns a value < 1
onSetAlpha() returns false getLayerType() returns LAYER_TYPE_NONE hasOverlappingRendering() returns true 複製代碼
一、Use a customizable 「inactive」 color instead of setting an opacity on the View
二、 Return false from hasOverlappingRendering() and the framework will set the proper alpha on the Paint for you
(注意:在android的View裏有透明度的屬性,當設置透明度setAlpha的時候,android裏默認會把當前view繪製到offscreen buffer中,而後再顯示出來。 這個offscreen buffer 能夠理解爲一個臨時緩衝區,把當前View放進來並作透明度的轉化,而後在顯示到屏幕上。這個過程是消耗資源的,因此應該儘可能避免這個過程。而當繼承了hasOverlappingRendering()方法返回false後,android會自動進行合理的優化,避免使用offscreen buffer。 )
三、Return true from onSetAlpha() and set an alpha on the Paint used to draw the 「gray」 circles
上面已經說到了使用alpha屬性的時候會致使overdraw,那麼應該如何避免這些狀況以減小overdraw呢? 下面分別對textview,imageview,和customview中使用到alpha狀況進行說明:
textview 對於TextView咱們一般須要文字透明效果,而不是View自己透明,因此,直接設置帶有alpha值的TextColor是比較高效的方式。
// 錯誤使用方式
textView.setAlpha(alpha);
//----------------------------------------------------------------
//正確方式
// 如下方式能夠避免建立 offscreen buffer
int newTextColor = (int) (0xFF * alpha) << 24 | baseTextColor & 0xFFFFFF;
textView.setTextColor(newTextColor);
複製代碼
ImageView 一樣的對於只具備src image的ImageView,直接調用setImageAlpha()方法更爲合理。
//一、 錯誤方式, setAlpha方法由View繼承而來,性能不佳
imageView.setAlpha(0.5f);
//-------------------------------------------------------------
//正確方式
// 使用如下方式時,ImageView會在繪製圖片時單獨爲圖片指定Alpha
// 能夠避免建立 offScreenBuffer
imageView.setImageAlpha((int) alpha * 255);
複製代碼
CustomView 相似的,自定義控件時,應該直接去設置paint的alpha。
//錯誤方式
customView.setAlpha(alpha);
//----------------------------------------------------
// 正確方式 But this
paint.setAlpha((int) alpha * 255);
canvas.draw*(..., paint);
複製代碼
未完待續
一、google-developer-training.github.io/android-dev… 二、sriramramani.wordpress.com/2015/05/06/… 三、helw.net/2016/01/27/… 四、www.cnblogs.com/tianzhijiex… 五、www.androidperformance.com/2015/03/31/… 六、www.curious-creature.com/2015/03/25/… 七、www.curious-creature.com/2012/12/01/… 八、yangm90.github.io/android-Alp…