是否了SurfaceView,它是什么?他的继承方式是什么?他与View的区别(从源码角度,如加载,绘制等)。
SurfaceView中采用了双缓冲机制,保证了UI界面的流畅性,同时SurfaceView不在主线程中绘制,而是另开辟一个线程去绘制,所以它不妨碍UI线程; SurfaceView继承于View,他和View主要有以下三点区别:
- View底层没有双缓冲机制,SurfaceView有;
- view主要适用于主动更新,而SurfaceView适用与被动的更新,如频繁的刷新
- view会在主线程中去更新UI,而SurfaceView则在子线程中刷新; SurfaceView的内容不在应用窗口上,所以不能使用变换(平移、缩放、旋转等)。也难以放在ListView或者ScrollView中,不能使用UI控件的一些特性比如View.setAlpha()
View:显示视图,内置画布,提供图形绘制函数、触屏事件、按键事件函数等;必须在UI主线程内更新画面,速度较慢。
SurfaceView:基于view视图进行拓展的视图类,更适合2D游戏的开发;是view的子类,类似使用双缓机制,在新的线程中更新画面所以刷新界面速度比view快,Camera预览界面使用SurfaceView。
GLSurfaceView:基于SurfaceView视图再次进行拓展的视图类,专用于3D游戏开发的视图;是SurfaceView的子类,openGL专用。
GLSurfaceView提供了下列特性:
- 管理一个surface,这个surface就是一块特殊的内存,能直接排版到android的视图view上。
- 管理一个EGL display,它能让opengl把内容渲染到上述的surface上。
- 用户自定义渲染器(render)。
- 让渲染器在独立的线程里运作,和UI线程分离。
- 支持按需渲染(on-demand)和连续渲染(continuous)。
- 一些可选工具,如调试。
非UI线程可以更新UI吗?
可以 当访问UI时,ViewRootImpl会调用checkThread方法去检查当前访问UI的线程是哪个,如果不是UI线程则会抛出异常。
执行onCreate方法的那个时候ViewRootImpl还没创建,无法去检查当前线程.ViewRootImpl的创建在onResume方法回调之后.
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
非UI线程是可以刷新UI的,前提是它要拥有自己的ViewRoot,即更新UI的线程和创建ViewRoot是同一个,或者在执行checkThread()前更新UI.
因为Android的UI控件都是非线程安全的,避免多线程更新UI产生并发问题。
RelativeLayout和LinearLayout性能分析
- RelativeLayout会让子View调用2次onMeasure,LinearLayout只有一次。但LinearLayout在有weight时,也会调用2次onMeasure
- RelativeLayout的子View如果高度和RelativeLayout不同,则会引发效率问题,当子View很复杂时,这个问题会更加严重。如果可以,尽量使用padding代替margin。
- 在不影响层级深度的情况下,使用LinearLayout和FrameLayout而不是RelativeLayout。所以decorview没有用RelativeLayout。
1.1.1. getMeasuredHeight和getHeight方法有什么区别?
getMeasuredHeight(测量高度)形成于view的measure过程,getHeight(最终高度)形成于layout过程。
在有些情况下,view需要measure多次才能确定测量宽高,在前几次的测量过程中,得出的测量宽高有可能和最终宽高不一致,但是最终来说,还是会相同,有一种情况会导致两者值不一样。
如下,此代码会导致view的最终宽高比测量宽高大100px
public void layout(int l,int t,int r, int b){
super.layout(l,t,r+100,b+100);}
draw: View的绘制过程遵循如下几步:
- a.绘制背景 background.draw(canvas)
- b.绘制自己(onDraw)
- c.绘制children(dispatchDraw)
- d.绘制装饰(onDrawScrollBars)
View绘制过程的传递是通过dispatchDraw来实现的,它会遍历所有的子元素的draw方法,如此draw事件就一层一层的传递下去了 ps:view有一个特殊的方法setWillNotDraw,如果一个view不需要绘制内容,即不需要重写onDraw方法绘制,可以开启这个标记,系统会进行相应的优化。默认情况下,View没有开启这个标记,默认认为需要实现onDraw方法绘制,当我们继承ViewGroup实现自定义控件,并且明确知道不需要具备绘制功能时,可以开启这个标记,如果我们重写了onDraw,那么要显示的关闭这个标记
Requestlayout,onlayout,onDraw,DrawChild区别与联系
requestLayout()方法 :会导致调用measure()过程 和 layout()过程 。 说明:只是对View树重新布局layout过程包括measure()和layout()过程,如果view的l,t,r,b没有必变,那就不会触发onDraw;但是如果这次刷新是在动画里,mDirty非空,就会导致onDraw。
onLayout()方法(如果该View是ViewGroup对象,需要实现该方法,对每个子视图进行布局)
onDraw()方法绘制视图本身 (每个View都需要重载该方法,ViewGroup不需要实现该方法)
drawChild()去重新回调每个子视图的draw()方法
View的滑动方式
- layout(left,top,right,bottom):通过修改View四个方向的属性值来修改View的坐标,从而滑动View
- offsetLeftAndRight() offsetTopAndBottom():指定偏移量滑动view
- LayoutParams,改变布局参数:layoutParams中保存了view的布局参数,可以通过修改布局参数的方式滑动view
- 通过动画来移动view:注意安卓的视图动画不能改变view的位置参数,属性动画可以
- scrollTo/scrollBy:注意移动的是view的内容,scrollBy(50,50)你会看到屏幕上的内容向屏幕的左上角移动了,这是参考对象不同导致的,你可以看作是它移动的是手机屏幕,手机屏幕向右下角移动,那么屏幕上的内容就像左上角移动了
- scroller:scroller需要配置computeScroll方法实现view的滑动,scroller本身并不会滑动view,它的作用可以看作一个插值器,它会计算当前时间点view应该滑动到的距离,然后view不断的重绘,不断的调用computeScroll方法,这个方法是个空方法,所以我们重写这个方法,在这个方法中不断的从scroller中获取当前view的位置,调用scrollTo方法实现滑动的效果