前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >View三问—斗鱼真题

View三问—斗鱼真题

作者头像
码上积木
发布2020-10-29 16:41:04
发布2020-10-29 16:41:04
58500
代码可运行
举报
文章被收录于专栏:码上积木码上积木
运行总次数:0
代码可运行

Android作为一种前端语言,view肯定是占据着很重要的位置,熟用view也是我们必须具备的能力,今天就来看看view相关的那些问题:

  • Activity、View、Window三者如何关联?
  • onCreate,onResume,onStart里面,什么地方可以获得宽高
  • 为什么view.post可以获得宽高,有看过view.post的源码吗?

Activity、View、Window三者如何关联?

Activity包含了一个PhoneWindow,而PhoneWindow就是继承于Window的,Activity通过setContentView将View设置到了PhoneWindow上,而View通过WindowManager的addView()、removeView()、updateViewLayout()对View进行管理。Window的添加过程以及Activity的启动流程都是一次IPC的过程。Activity的启动需要通过AMS完成;Window的添加过程需要通过WindowSession完成。

onCreate,onResume,onStart里面,什么地方可以获得宽高

如果在onCreate、onStart、onResume中直接调用View的getWidth/getHeight方法,是无法得到View宽高的正确信息,因为view的measure过程与Activity的生命周期是不同步的,所以无法保证在这些生命周期里view 的measure已经完成。所以很有可能获取的宽高为0。

所以主要有以下三个方法来获取view的宽高:

  • view.post()方法

在该方法里的runnable对象,能保证view已经绘制完成,也就是执行完measure、layout和draw方法了。

代码语言:javascript
代码运行次数:0
复制
        view.post(new Runnable() {
            @Override
            public void run() {
                int width = view.getWidth();
                int hight = view.getHeight();
            }
        });
  • onWindowFocusChanged方法

Activity中可以重写onWindowFocusChanged方法,该方法表示Activity的窗口得到焦点或者失去焦点的时候,所以Activitiy获取焦点时,view肯定绘制完成了,这时候获取宽高也是没问题的:

代码语言:javascript
代码运行次数:0
复制
    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        if(hasFocus){
            int width = view.getWidth();
            int hight = view.getHeight();
        }
    }
  • ViewTreeObserver注册OnGlobalLayoutListener接口

ViewTreeObserver是一个观察者,主要是用来观察视图树的各种变化。OnGlobalLayoutListener的作用是当View树的状态发生改变或者View树中某view的可见性发生改变时,OnGlobalLayoutListener的onGlobalLayout方法将会被回调。因此,此时获取view的宽高也是可以的。

代码语言:javascript
代码运行次数:0
复制
        ViewTreeObserver observer = title_name.getViewTreeObserver();
        observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
               int width = view.getWidth();
               int hight = view.getHeight();
            }
        });

为什么view.post可以获得宽高,有看过view.post的源码吗?

能获取宽高的原因肯定就是因为在此之前view 绘制已经完成,所以View.post() 添加的任务能够保证在所有 View 绘制流程结束之后才被执行。

看看post的源码:

代码语言:javascript
代码运行次数:0
复制
 public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action); 
        }
        // Assume that post will succeed later
        ViewRootImpl.getRunQueue().post(action);  
        return true;
    }

//RunQueue .class
     void post(Runnable action) {
            postDelayed(action, 0);
        }
 
        void postDelayed(Runnable action, long delayMillis) {
            HandlerAction handlerAction = new HandlerAction();
            handlerAction.action = action;
            handlerAction.delay = delayMillis;
 
            synchronized (mActions) {
                mActions.add(handlerAction);
            }
        }

        void executeActions(Handler handler) {
            synchronized (mActions) {
                final ArrayList<HandlerAction> actions = mActions;
                final int count = actions.size();
 
                for (int i = 0; i < count; i++) {
                    final HandlerAction handlerAction = actions.get(i);
                    handler.postDelayed(handlerAction.action, handlerAction.delay);
                }
 
                actions.clear();
            }
        }

所以在执行View.post()的方法时,那些Runnable并没有马上被执行,而是保存到RunQueue里面,然后通过executeActions方法执行,也就是通过handler,post了一个延时任务Runnable。而executeActions方法什么时候会执行呢?

代码语言:javascript
代码运行次数:0
复制

private void performTraversals() {
     getRunQueue().executeActions(attachInfo.mHandler);
    ...
    performMeasure();
    ...
    performLayout();
    ...
    performDraw();
 }

可以看到在performTraversals方法中执行了,但是在view绘制之前,这是因为在绘制之前就把需要执行的runnable封装成Message发送到MessageQueue里排队了,但是Looper不会马上去取这个消息,因为Looper会按顺序取消息,主线程还有什么消息没执行完呢?其实就是当前的这个performTraversals所在的任务,所以要等下面的·performMeasure,performLayout,performDraw·都执行完,也就是view绘制完毕了,才会去执行之前我们post的那个runnable,也就是我们能在view.post方法里的runnable能获取宽高的主要原因了。

这里我说的比较简单,其实里面涉及到的知识点还是很多的,大家可以看看网上的文章,这里推荐两篇:https://www.jianshu.com/p/ea464eb15436 https://www.cnblogs.com/dasusu/p/8047172.html

下次有机会我也会详细说下,今天就到这里了。拜拜。


Android开发者们,快来关注公| 众 |号【码上积木】,每天三问面试题,并详细剖析,助你成为offer收割机。 相信积累的力量。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-10-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码上积木 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Activity、View、Window三者如何关联?
  • onCreate,onResume,onStart里面,什么地方可以获得宽高
  • 为什么view.post可以获得宽高,有看过view.post的源码吗?
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档