【Android问答】Android面试常问问题

今年秋招颗粒无收,校招公司基本上都跪在了算法,社招小公司因为面试没有体现出深度表现平平,自然也因为期望太高而告吹。仔细总结一下,发现主要原因是没有模拟问答练习,在面试时不能牢记于心倒背如流,自然也不能达到好效果了。
下面这些问题基本是面试必问,但是网上的答案都是通篇大论,没有人给出篇幅适中而又不失水准的“答案”。
一、Android中View的绘制流程。
答:总的来说分为测量、布局和绘制。
1、测量
测量是为了确定View自身和其子View的大小。入口是final方法measure,传入的参数表示父布局根据自身情况以及当前View的LayoutParams初步建议当前View应该设置的大小。这个参数可能有三种测量模式:EXACTLY、AT_MOST和UNSPECIFIED,分别表示父布局通过初步判断建议子View的大小应该是一个确定值、最大值和不确定值。
measure方法主要逻辑是判断传入参数(父布局建议大小)是否发生变化以及能否使用测量缓存来决定是否真正调用onMeasure去测量。
View的默认onMeasure方法通常情况下直接设置测量大小为父布局传入的建议大小(在measure方法中根据LayoutMode可能稍做修改),只有当父布局传入的测量模式是UNSPECIFIED时,才设置为View或其背景的最小大小。所以自定义View如果不重写onMeasure方法wrap_content属性失效。而对于ViewGroup(默认不重写onMeasure),需要重写onMeasure方法遍历并测量子View的大小以及确定布局自身的大小。
2、布局
布局是为了确定View自身和子View的位置。View的默认layout方法主要逻辑是根据传入的上下左右四个参数确定View的位置(调用setFrame,期间有可能调用invalidate),如果位置发生了变化则调用onLayout并通知相应的监听器。因为View没有子View所以onLayout是空方法。而对于ViewGroup,它重写了layout方法并将其设为final,相对于View.layout增加了布局动画
逻辑。并将onLayout方法设为abstract,因为布局有子View需要确定子View的位置。
3、绘制
绘制过程主要有六步:绘制背景、保留渐变图层信息、调用onDraw绘制View自身内容、调用dispatchDraw绘制子View、绘制渐变效果、绘制装饰内容(滚动条等)。之前版本View的onDraw方法是abstract的,但是7.0后变成了空方法。ViewGroup重写了dispatchDraw方法遍历并绘制子View,通常不用再重写。
二、View的requestLayout方法。
答:它自底向上调用父View的requestLayout,最终会触发一次ViewRootImpl类的schedualTraversals。会重新调用measure和layout过程,在layout过程中可能间接调用invalidate,从而引起某些View重绘。
三、invalidate和postInvalidate的区别。
答:与requestLayout类似,它们也是自底向上调用父View的invalidateChildInParent,最终触发一次ViewRootImpl的schedualTraversals,因为mLayoutRequested为false,不会调用measure和layout过程,只会重绘View自身(如果ViewGroup则还会重绘子View)。
触发invalidate的情况通常有以下几种:

  • 不做其他任何操作,直接调用invalidate方法.请求重新draw,只会绘制调用者本身。
  • 触发setSelection方法。请求重新draw,只会绘制调用者本身。
  • 触发setVisibility方法。 当View可视状态在INVISIBLE和VISIBLE间转换时会间接调用invalidate方法,从而重绘该View。当View的可视状态在INVISIBLE\VISIBLE 和GONE状态间转换时会间接调用requestLayout和invalidate方法,由于View树大小发生了变化,所以会请求measure过程以及draw过程,从而发生了变化的View都会重绘。
  • 触发setEnabled方法,可能重绘当前View。
  • 触发requestFocus方法。请求View树的draw过程,只绘制“需要重绘”的View。
    总的来说,当布局的大小和位置都没有发生变化时,调用invalidate只会重绘当前View,如果大小和位置任一发生了变化,则会间接调用requestLayout重新进行measure和layout,并重绘发生了变化的所有View。
    postInvalidate通过Handler与invalidate效果一样。

四、Touch事件的分发机制。
答:其实Touch事件的分发机制很简单,它的伪代码如下

public boolean dispatchTouchEvent(MotionEvent ev) {
    boolean result = false;            
    if (!onInterceptTouchEvent(ev)) {   // 如果没有拦截交给子View
        result = child.dispatchTouchEvent(ev);
    }
    if (!result) {  // 如果事件没有被子View消费,询问自身onTouchEvent
        result = onTouchEvent(ev);
    }
    return result;
}

总的来说,如果没有拦截,则将事件优先交给子View处理。如果子View没有消费,再本自身onTouchEvent有没有消费。如果自身也没有消费,则将事件回传给父布局处理。
也就是说,如果不重写各个方法,某个View的dispatchTouchEvent返回true,则它的所有父布局的dispatchTouchEvent都返回true。
某个ViewGroup的dispatchTouchEvent返回值其实代表这个ViewGroup及其子View是否消费ActionDown事件。如果返回false表明这个ViewGroup及其子View对ActionDown不感兴趣,那么之后的ActionUp等事件都不会再传过来。
五、各种Listener的优先级。
答:如果用户注册了onTouchListener,说明用户要自己处理触摸事件了,所以onTouchListener优先级最高。单击事件(onClickListener) 需要两个两个事件(ACTION_DOWN 和 ACTION_UP )才能触发,如果先分配给onClick判断,等它判断完,用户手指已经离开屏幕,必然造成 View 无法响应其他事件,所以onClickListener优先级最低。onLongClickListener类似,优先级要比onClickListener高,onTouchEvent不需要ActionUp事件,优先级应该比onLongClickListener高。所以优先级顺序应该是 onTouchListener > onTouchEvent > onLongClickListener > onClickListener。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值