android获得一个view的高度,Android ViewTreeObserver使用总结及获得View高度的几种方法...
ViewTreeObserver 注册一个观察者来监听视图树,当视图树的布局、视图树的焦点、视图树将要绘制、视图树滚动等发生改变时,ViewTreeObserver都会收到通知,ViewTreeObserver不能被实例化,可以调用View.getViewTreeObserver()来获得。
ViewTreeObserver继承关系:
public final class ViewTreeObserverextendsObject
java.lang.Object
↳android.view.ViewTreeObserver
ViewTreeObserver直接继承自Object.
ViewTreeObserver提供了View的多种监听,每一种监听都有一个内部类接口与之对应,内部类接口全部保存在CopyOnWriteArrayList中,通过ViewTreeObserver.addXXXListener()来添加这些监听,源码如下:
public final class ViewTreeObserver {
// Recursive listeners use CopyOnWriteArrayList
private CopyOnWriteArrayList mOnWindowFocusListeners;
private CopyOnWriteArrayList mOnWindowAttachListeners;
private CopyOnWriteArrayList mOnGlobalFocusListeners;
private CopyOnWriteArrayList mOnTouchModeChangeListeners;
private CopyOnWriteArrayList mOnEnterAnimationCompleteListeners;
// Non-recursive listeners use CopyOnWriteArray
// Any listener invoked from ViewRootImpl.performTraversals() should not be recursive
private CopyOnWriteArray mOnGlobalLayoutListeners;
private CopyOnWriteArray mOnComputeInternalInsetsListeners;
private CopyOnWriteArray mOnScrollChangedListeners;
private CopyOnWriteArray mOnPreDrawListeners;
private CopyOnWriteArray mOnWindowShownListeners;
// These listeners cannot be mutated during dispatch
private ArrayList mOnDrawListeners;
}
以OnGlobalLayoutListener为例,首先是定义接口:
public interface OnGlobalLayoutListener {
/**
* Callback method to be invoked when the global layout state or the visibility of views
* within the view tree changes
*/
public void onGlobalLayout();
}
将OnGlobalLayoutListener 添加到CopyOnWriteArray数组中:
/**
* Register a callback to be invoked when the global layout state or the visibility of views
* within the view tree changes
*
* @param listener The callback to add
*
* @throws IllegalStateException If {@link #isAlive()} returns false
*/
public void addOnGlobalLayoutListener(OnGlobalLayoutListener listener) {
checkIsAlive();
if (mOnGlobalLayoutListeners == null) {
mOnGlobalLayoutListeners = new CopyOnWriteArray();
}
mOnGlobalLayoutListeners.add(listener);
}
移除OnGlobalLayoutListener,当视图树布局发生变化时不会再收到通知了:
/**
* Remove a previously installed global layout callback
*
* @param victim The callback to remove
*
* @throws IllegalStateException If {@link #isAlive()} returns false
*
* @deprecated Use #removeOnGlobalLayoutListener instead
*
* @see #addOnGlobalLayoutListener(OnGlobalLayoutListener)
*/
@Deprecated
public void removeGlobalOnLayoutListener(OnGlobalLayoutListener victim) {
removeOnGlobalLayoutListener(victim);
}
/**
* Remove a previously installed global layout callback
*
* @param victim The callback to remove
*
* @throws IllegalStateException If {@link #isAlive()} returns false
*
* @see #addOnGlobalLayoutListener(OnGlobalLayoutListener)
*/
public void removeOnGlobalLayoutListener(OnGlobalLayoutListener victim) {
checkIsAlive();
if (mOnGlobalLayoutListeners == null) {
return;
}
mOnGlobalLayoutListeners.remove(victim);
}
其他常用方法:
dispatchOnGlobalLayout():视图树发生改变时通知观察者,如果想在View Layout 或 View hierarchy 还未依附到Window时,或者在View处于GONE状态时强制布局,这个方法也可以手动调用。
/**
* Notifies registered listeners that a global layout happened. This can be called
* manually if you are forcing a layout on a View or a hierarchy of Views that are
* not attached to a Window or in the GONE state.
*/
public final void dispatchOnGlobalLayout() {
// NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
// perform the dispatching. The iterator is a safe guard against listeners that
// could mutate the list by calling the various add/remove methods. This prevents
// the array from being modified while we iterate it.
final CopyOnWriteArray listeners = mOnGlobalLayoutListeners;
if (listeners != null && listeners.size() > 0) {
CopyOnWriteArray.Access access = listeners.start();
try {
int count = access.size();
for (int i = 0; i < count; i++) {
access.get(i).onGlobalLayout();
}
} finally {
listeners.end();
}
}
}
dispatchOnPreDraw():通知观察者绘制即将开始,如果其中的某个观察者返回 true,那么绘制将会取消,并且重新安排绘制,如果想在View Layout 或 View hierarchy 还未依附到Window时,或者在View处于GONE状态时强制绘制,可以手动调用这个方法。
/**
* Notifies registered listeners that the drawing pass is about to start. If a
* listener returns true, then the drawing pass is canceled and rescheduled. This can
* be called manually if you are forcing the drawing on a View or a hierarchy of Views
* that are not attached to a Window or in the GONE state.
*
* @return True if the current draw should be canceled and resceduled, false otherwise.
*/
@SuppressWarnings("unchecked")
public final boolean dispatchOnPreDraw() {
boolean cancelDraw = false;
final CopyOnWriteArray listeners = mOnPreDrawListeners;
if (listeners != null && listeners.size() > 0) {
CopyOnWriteArray.Access access = listeners.start();
try {
int count = access.size();
for (int i = 0; i < count; i++) {
cancelDraw |= !(access.get(i).onPreDraw());
}
} finally {
listeners.end();
}
}
return cancelDraw;
}
ViewTreeObserver常用内部类:
内部类接口
备注
ViewTreeObserver.OnPreDrawListener
当视图树将要被绘制时,会调用的接口
ViewTreeObserver.OnGlobalLayoutListener
当视图树的布局发生改变或者View在视图树的可见状态发生改变时会调用的接口
ViewTreeObserver.OnGlobalFocusChangeListener
当一个视图树的焦点状态改变时,会调用的接口
ViewTreeObserver.OnScrollChangedListener
当视图树的一些组件发生滚动时会调用的接口
ViewTreeObserver.OnTouchModeChangeListener
当视图树的触摸模式发生改变时,会调用的接口
获得View高度的几种方式:
我们应该都遇到过在onCreate()方法里面调用view.getWidth()和view.getHeight()获取到的view的宽高都是0的情况,这是因为在onCreate()里还没有执行测量,需要在onResume()之后才能得到正确的高度,那么可不可以在onCreate()里就得到宽高呢?答:可以!常用的有下面几种方式:
1、通过设置View的MeasureSpec.UNSPECIFIED来测量:
int w = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
int h = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
view.measure(w, h);
//获得宽高
int viewWidth=view.getMeasuredWidth();
int viewHeight=view.getMeasuredHeight();
设置我们的SpecMode为UNSPECIFIED,然后去调用onMeasure测量宽高,就可以得到宽高。
2、通过ViewTreeObserver .addOnGlobalLayoutListener来获得宽高,当获得正确的宽高后,请移除这个观察者,否则回调会多次执行:
//获得ViewTreeObserver
ViewTreeObserver observer=view.getViewTreeObserver();
//注册观察者,监听变化
observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
//判断ViewTreeObserver 是否alive,如果存活的话移除这个观察者
if(observer.isAlive()){
observer.removeGlobalOnLayoutListener(this);
//获得宽高
int viewWidth=view.getMeasuredWidth();
int viewHeight=view.getMeasuredHeight();
}
}
});
3、通过ViewTreeObserver .addOnPreDrawListener来获得宽高,在执行onDraw之前已经执行了onLayout()和onMeasure(),可以得到宽高了,当获得正确的宽高后,请移除这个观察者,否则回调会多次执行
//获得ViewTreeObserver
ViewTreeObserver observer=view.getViewTreeObserver();
//注册观察者,监听变化
observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
if(observer.isAlive()){
observer.removeOnDrawListener(this);
}
//获得宽高
int viewWidth=view.getMeasuredWidth();
int viewHeight=view.getMeasuredHeight();
return true;
}
});
android获得一个view的高度,Android ViewTreeObserver使用总结及获得View高度的几种方法...相关推荐
- 【数字图像处理】模拟Matlab的imresize()写一个你自己的imresize()函数,至少应实现‘nearest’和‘bilinear’两种方法
作业要求:模拟Matlab的imresize()写一个你自己的imresize()函数,至少应实现'nearest'和'bilinear'两种方法. 首先理论方面主要参考了一下两个网址 https:/ ...
- 多个html如何套用套一个头部,Vue.js项目中管理每个页面的头部标签的两种方法...
在 Vue SPA 应用中,如果想要修改 HTML 的头部标签,如页面的 title ,我们只能去修改 index.html 模板文件,但是这个是全局的修改,如何为每个页面都设置不一样的 title ...
- android rxbus 一个页面监听,Android RxBus的使用
RxBus的核心功能是基于Rxjava的,在RxJava中有个Subject类,它继承Observable类,同时实现了Observer接口,因此Subject可以同时担当订阅者和被订阅者的角色,这里 ...
- Android自定义一个对话框,完全自定义Android对话框AlertDialog的实现
Android本身封装的AlertDialog.Builder很方便易用,但如果想要自定义弹出对话框的风格,如标题字体背景元素间隔之类的,那就比较困难了. 最近我就遇到了这个问题,一个工程的界面风格全 ...
- android 键盘隐藏监听,安卓监听软键盘弹出与隐藏的两种方法
需求: 现在有一个需求是点击一行文本框,弹出一个之前隐藏的输入框,输入完成后按返回键或者其他的东西隐藏键盘和输入框,将输入框的内容填充到文本框中. 实现: 拿到这个需求的第一反应就是写一个监听来监听键 ...
- Android 免root 备份数据,教你安卓手机免Root恢复手机数据的三种方法
说到安卓手机,人们对它的第一印象就是开源.自由.可定制.市面上大多数系统,比如Flyme.MIUI其实都是由安卓内核再加上一层自己的包装罢了.所以你会看到手机上一堆预装软件卸载不掉,这本质上就是手机厂 ...
- android华为怎么截屏快捷键,华为p10怎么截图 华为p10截屏的三种方法
华为p10怎么截屏和保存图片以及华为p10截屏快捷键在哪是很多朋友问到的,对于刚入手华为p10的朋友来说可能有些基本操作是不知道的,这里我们一起来了解一下华为p10怎么截屏和保存图片以及华为p10截屏 ...
- android view可视区域,Android使用WindowManager构造悬浮view
一般在android显示一个View都是通过Activity的setContentView设置的,但是还有一种方法,可以直接使用WindowManager在整个应用的最上层绘制我们需要显示的view, ...
- android 实现一个图片的闪烁
android 实现一个图片的闪烁 android 实现一个图片的闪烁 android 实现一个图片的闪烁 直接上代码 //实现图片闪烁效果 private void setFlickerAnimat ...
最新文章
- 你掌握了什么概念或技术使你感觉自我提升突飞猛进?
- jsp中的url拼接的参数传递到controller乱码_猿蜕变系列5——一文搞懂Controller的花式编写...
- 云数据中心的网络架构
- 高通平台耳机插拔检测
- AB1601中使用定时器来进行延时的弊端
- java用户角色权限管理 只显示姓_快递物流管理系统SSM,JQUERYEASYUI,MYSQL
- iOS-使用添加的花样字体
- mybatis调用mysql存储过程_秒会mybatis调用存储过程(MySQL)
- 实现列表CListCtrl可点击编辑
- android播放器 重音,如何在SQLite查询中忽略重音(Android)
- 蓝屏蓝屏代码查询器_这是创建您想要的任何颜色的死亡蓝屏的方法
- (转) Csrss进程剖析
- Maven配置pom引入本地依赖
- 如何使用Nginx防御DDoS攻击?
- 泽塔云发布全新AI产品 持续引领超融合云计算创新
- linux服务器上的回收站,为linux系统实现回收站
- 合宙 ESP32C3 使用micropython 驱动配套0.96寸 TFT ST7735 屏幕显示色块和文字
- 【fq_codel】fq_codel on Router
- 美拍应该如何引流?如何利用美拍引流?美拍引流方法
- 用mysql关联4个表_MySQL多表操作
热门文章
- linux为什么要交换内存,Linux系统中交换内存是什么?
- 熟悉和理解linux编程环境,熟悉Linux环境和编程0.doc
- java程序解析perl文件_如何从shell脚本执行perl文件
- python递归函数的意思_Python 递归函数
- 解决win7下nodejs安装运行报错:Node.js is only supported on Windows 8.1, Windows Server 2012 R2... 之不支持高版本问题
- @Scheduled执行阻塞解决办法
- Vue2.0 UI框架Element运用之DateTimePicker(el-date-picker)初始值及时间格式转化等细节问题
- PCL第三方库:Eigen, Flann , Qhull, VTK, Boost简介
- 使用 yield 减少内存消耗
- RabbitMQ使用简述