View.Post()保证UI带你装逼带你飞
来源:互联网 发布:mac优酷弹幕怎么设置 编辑:程序博客网 时间:2024/06/16 08:11
前言
日常开发中我们可能会遇到如下问题:
1、在onCreate\onStrart()\onResume()中获取View的宽高为0;
2、在onCreate\onStrart()\onResume()中直接调用Scroview.scrollTo(x,y)没有效果;
那么接下来一探究竟:
原因分析:
因为当onCreate()方法被调用的时候会通过LayoutInflater将xml文件填充到ContentView。
填充过程中只包括创建视图,不包括设置视图大小。而设置视图的大小和具体的位置则是通过布局层层遍历获得的。
如下图:
测量过程由measure(int , int)方法完成,该方法从上到下遍历视图树。在递归的过程中,每个视图都会向下层传递尺寸和规格,当measure方法遍历结束时,每个视图都保存了各自的尺寸信息。第二个过程由layout(int, int, int, int)方法完成,该方法也是由上而下遍历视图树。遍历过程中,每个父视图通过测量过程的结果定位所有姿势图的位置信息。
也就是说我们在onCreate\onStrart()\onResume()的时候我们并不知道什么时候布局测量完成,所以接下来我们去寻找一些方法。
解决方案:
方案一:View.Post()/View.PostDelay() [重点]
我们先来看下源码:
/** * <p>Causes the Runnable to be added to the message queue. * The runnable will be run on the user interface thread.</p> * * @param action The Runnable that will be executed. * * @return Returns true if the Runnable was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. * * @see #postDelayed * @see #removeCallbacks */public boolean post(Runnable action) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { return attachInfo.mHandler.post(action); } // Postpone the runnable until we know on which thread it needs to run. // Assume that the runnable will be successfully placed after attach. getRunQueue().post(action); return true;}
重点是这句话:
The runnable will be run on the user interface thread
Runnable是一个接口,不是一个线程,一般线程会实现Runnable。所以如果我们使用匿名内部类是运行在UI主线程的,如果我们使用实现这个Runnable接口的线程类,则是运行在对应线程的。
也就是说我们用View.post(Runnable action)方法里,View获得当前线程(即UI线程)的Handler,然后将action对象post到Handler里。
在Handler里,它将传递过来的action对象包装成一个Message(Message的callback为action),然后将其投入UI线程的消息循环中。
当Handler再次处理该Message时,已经在UI线程里,直接调用runnable的run方法。因此,我们可以毫无顾虑的来更新UI。
也就是说我们通过View.Post()/View.PostDelay()方法就可以实现获取view的宽高,并且Scroview.scrollTo(x,y)可以正常使用了。
view.post(new Runnable() { @Override public void run() { //view的相关操作 }});
所以个人推荐使用View.post()既方便又可以保证指定的任务在视图操作中顺序执行。
方案二:onWindowFocusChanged
使用如下:
@Override public void onWindowFocusChanged(boolean hasFocus) { //view的相关操作}
我们看下官方注释:
/** * Called when the current {@link Window} of the activity gains or loses * focus. This is the best indicator of whether this activity is visible * to the user. The default implementation clears the key tracking * state, so should always be called. * * <p>Note that this provides information about global focus state, which * is managed independently of activity lifecycles. As such, while focus * changes will generally have some relation to lifecycle changes (an * activity that is stopped will not generally get window focus), you * should not rely on any particular order between the callbacks here and * those in the other lifecycle methods such as {@link #onResume}. * * <p>As a general rule, however, a resumed activity will have window * focus... unless it has displayed other dialogs or popups that take * input focus, in which case the activity itself will not have focus * when the other windows have it. Likewise, the system may display * system-level windows (such as the status bar notification panel or * a system alert) which will temporarily take window input focus without * pausing the foreground activity. * * @param hasFocus Whether the window of this activity has focus. * * @see #hasWindowFocus() * @see #onResume * @see View#onWindowFocusChanged(boolean) */public void onWindowFocusChanged(boolean hasFocus) {}
该方法会在view绘制完成之后调用,所以我们在这个时候去获取view宽高,或者Scroview.scrollTo(x,y)都可以正常运行了。
但是该方法如原注释所说,当Activity的窗口得到焦点和失去焦点时均会被调用一次,如果频繁地进行onResume和onPause,那么onWindowFocusChanged也会被频繁地调用。
所以也要结合具体业务场景。
方案三:ViewTreeObserver
使用ViewTreeObserver的众多回调可以完成这个功能,比如使用OnGlobalLayoutListener这个接口,当view树的状态发生改变或者view树内部的view的可见性发生改变时,onGlobalLayout方法将被回调,因此这是获取view的宽高一个很好的时机。需要注意的是,伴随着view树的状态改变等,onGlobalLayout会被调用多次。
view.getViewTreeObserver().addOnGlobalFocusChangeListener(new ViewTreeObserver.OnGlobalFocusChangeListener() { @Override public void onGlobalFocusChanged(View oldFocus, View newFocus) { //view的相关操作 }});
我们看一下View官方注释:
/** * A view tree observer is used to register listeners that can be notified of global * changes in the view tree. Such global events include, but are not limited to, * layout of the whole tree, beginning of the drawing pass, touch mode change.... * * A ViewTreeObserver should never be instantiated by applications as it is provided * by the views hierarchy. Refer to {@link android.view.View#getViewTreeObserver()} * for more information. */public final class ViewTreeObserver { //代码省略。。。}
ViewTreeObserver这个类,这个类是用来注册当view tree全局状态改变时的回调监听器,这些全局事件包括很多,比如整个view tree视图的布局,视图绘制的开始,点击事件的改变等等。还有千万不要在应用程序中实例化ViewTreeObserver对象,因为该对象仅是由视图提供的。
综上,个人比较推荐方案一:View.Post()/View.PostDelay() 。
扫码关注公众号“伟大程序猿的诞生“,更多干货等着你~
扫码关注公众号“伟大程序猿的诞生“,更多干货等着你~
扫码关注公众号“伟大程序猿的诞生“,更多干货等着你~
- View.Post()保证UI带你装逼带你飞
- 光棍节,UI学科带你装逼带你飞!
- Android 自定义View 带你飞(一)
- Android 自定义View 带你飞(二)
- 带你**带你飞
- vim带你装逼带你飞(一)
- vim带你装逼带你飞(一)
- vim带你装逼带你飞(一)
- vim带你装逼带你飞(一)
- View的事件分发,女神带你飞
- 手把手带你入门之Swagger UI
- 手把手带你入门之Swagger UI
- 带你学C带你飞
- 带你学习Android 自定义View
- 自定义列表,带你学习View
- 带你装逼带你飞之IntellJ IDEA使用攻略
- 带你装逼带你飞之IntellJ IDEA使用攻略
- 带你装逼带你飞(将Library发布到JCenter)
- Spring IOC <context:annotation-config>与<context:component-scan/>
- count(*),count(1),count(id),count(rowid)的效率
- SpringBoot系列<六>Logback日志配置
- 171214 L0 vs L1 vs L2 vs Lp
- 搭建高可用mongodb集群(二)—— 副本集
- View.Post()保证UI带你装逼带你飞
- Linux 热插拔(Hot Plug)处理机制系列
- 数据结构 期末总结
- C++ 解析Json——jsoncpp
- shell程序设计
- 118. Pascal's Triangle
- 搭建高可用mongodb集群(三)—— 深入副本集内部机制
- xml学习笔记③PHP DOM--对xml文件进行修改和删除操作
- 洛谷 P1590 失踪的7