安卓面试题到处攒,一到面试就忘个干净?来看看这份超详细的整理


安卓面试题到处攒,一到面试就忘个干净?来看看这份超详细的整理

文章插图
 
稳住,今天周一,长文面试题奉上 。
更多BAT面试解析资料包内容私信我【资料】查看
1、说说View/ViewGroup的绘制流程View的绘制流程是从ViewRoot的performTraversals开始的,它经过measure,layout,draw三个过程最终将View绘制出来 。performTraversals会依次调用performMeasure,performLayout,performDraw三个方法,他们会依次调用measure,layout,draw方法,然后又调用了onMeasure,onLayout,dispatchDraw 。
measure :对于自定义的单一view的测量,只需要根据父 view 传递的MeasureSpec进行计算大小 。
对于ViewGroup的测量,一般要重写onMeasure方法,在onMeasure方法中,父容器会对所有的子View进行Measure,子元素又会作为父容器,重复对它自己的子元素进行Measure,这样Measure过程就从DecorView一级一级传递下去了,也就是要遍历所有子View的的尺寸,最终得出出总的viewGroup的尺寸 。Layout和Draw方法也是如此 。
layout :根据 measure 子 View 所得到的布局大小和布局参数,将子View放在合适的位置上 。
对于自定义的单一view,计算本身的位置即可 。
对于ViewGroup来说,需要重写onlayout方法 。除了计算自己View的位置,还需要确定每一个子View在父容器的位置以及子view的宽高(getMeasuredWidth和getMeasuredHeight),最后调用所有子view的layout方法来设定子view的位置 。
draw :把 View 对象绘制到屏幕上 。
draw()会依次调用四个方法:
1)drawBackground(),根据在 layout 过程中获取的 View 的位置参数,来设置背景的边界 。2)onDraw(),绘制View本身的内容,一般自定义单一view会重写这个方法,实现一些绘制逻辑 。
3) dispatchDraw(),绘制子View 4)onDrawScrollBars(canvas),绘制装饰,如 滚动指示器、滚动条、和前景.
2、说说你理解的MeasureSpecMeasureSpec是由父View的MeasureSpec和子View的LayoutParams通过简单的计算得出一个针对子View的测量要求,这个测量要求就是MeasureSpec 。
首先,MeasureSpec是一个大小跟模式的组合值,MeasureSpec中的值是一个整型(32位)将size和mode打包成一个Int型,其中高两位是mode,后面30位存的是size
// 获取测量模式int specMode = MeasureSpec.getMode(measureSpec)// 获取测量大小int specSize = MeasureSpec.getSize(measureSpec)// 通过Mode 和 Size 生成新的SpecModeint measureSpec=MeasureSpec.makeMeasureSpec(size, mode);其次,每个子View的MeasureSpec值根据子View的布局参数和父容器的MeasureSpec值计算得来的,所以就有一个父布局测量模式,子视图布局参数,以及子view本身的MeasureSpec关系图:
安卓面试题到处攒,一到面试就忘个干净?来看看这份超详细的整理

文章插图
 
其实也就是getChildMeasureSpec方法的源码逻辑,会根据子View的布局参数和父容器的MeasureSpec计算出来单个子view的MeasureSpec 。
最后是实际应用时:
对于自定义的单一view,一般可以不处理onMeasure方法,如果要对宽高进行自定义,就重写onMeasure方法,并将算好的宽高通过setMeasuredDimension方法传进去 。对于自定义的ViewGroup,一般需要重写onMeasure方法,并且调用measureChildren方法遍历所有子View并进行测量(measureChild方法是测量具体某一个view的宽高),然后可以通过getMeasuredWidth/getMeasuredHeight获取宽高,最后通过setMeasuredDimension方法存储本身的总宽高 。
3、Scroller是怎么实现View的弹性滑动?
  • 在MotionEvent.ACTION_UP事件触发时调用startScroll()方法,该方法并没有进行实际的滑动操作,而是记录滑动相关量(滑动距离、滑动时间)
  • 接着调用invalidate/postInvalidate()方法,请求View重绘,导致View.draw方法被执行
  • 当View重绘后会在draw方法中调用computeScroll方法,而computeScroll又会去向Scroller获取当前的scrollX和scrollY;然后通过scrollTo方法实现滑动;接着又调用postInvalidate方法来进行第二次重绘,和之前流程一样,如此反复导致View不断进行小幅度的滑动,而多次的小幅度滑动就组成了弹性滑动,直到整个滑动过成结束 。
mScroller = new Scroller(context);@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_UP:// 滚动开始时X的坐标,滚动开始时Y的坐标,横向滚动的距离,纵向滚动的距离mScroller.startScroll(getScrollX(), 0, dx, 0);invalidate();break;}return super.onTouchEvent(event);}@Overridepublic void computeScroll() {// 重写computeScroll()方法,并在其内部完成平滑滚动的逻辑if (mScroller.computeScrollOffset()) {scrollTo(mScroller.getCurrX(), mScroller.getCurrY());invalidate();}}


推荐阅读