ViewTreeObserver的使用
2016-04-13 11:02
429 查看
序言
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 View.getViewTreeObserver() for more information.官方文档的描述ViewTreeObserver是用来监听一些全局变化的。
在 ViewTreeObserver 中,包含了以下几个接口:
interface ViewTreeObserver.OnGlobalFocusChangeListener
interface ViewTreeObserver.OnGlobalLayoutListener
interface ViewTreeObserver.OnPreDrawListener
interface ViewTreeObserver.OnScrollChangedListener
interface ViewTreeObserver.OnTouchModeChangeListener
本文将测试除 ViewTreeObserver.OnScrollChangedListener 外的四个接口
xml布局如下
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:gravity="center" android:layout_height="match_parent" android:orientation="vertical" android:id="@+id/layout" tools:context="trs.com.viewtreeobserverdemo.MainActivity"> <TextView android:id="@+id/tv_show" android:layout_width="match_parent" android:layout_height="wrap_content" /> <EditText android:hint="et1" android:tag="et1" android:id="@+id/et_1" android:layout_width="match_parent" android:layout_height="wrap_content" /> <EditText android:tag="et2" android:hint="et2" android:layout_marginTop="10dp" android:id="@+id/et_2" android:layout_width="match_parent" android:layout_height="wrap_content" /> <Button android:text="test" android:id="@+id/btn" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
让MainActivity实现相应接口
public class MainActivity extends AppCompatActivity implements ViewTreeObserver.OnGlobalLayoutListener, ViewTreeObserver.OnPreDrawListener, ViewTreeObserver.OnGlobalFocusChangeListener, ViewTreeObserver.OnTouchModeChangeListener, View.OnClickListener
在onCreat中添加监听
EditText et_1,et_2; TextView tv_show; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ViewTreeObserver vto = findViewById(R.id.layout).getViewTreeObserver(); et_1= (EditText) findViewById(R.id.et_1); et_2= (EditText) findViewById(R.id.et_2); vto.addOnGlobalLayoutListener(this); vto.addOnPreDrawListener(this); vto.addOnGlobalFocusChangeListener(this); vto.addOnTouchModeChangeListener(this); findViewById(R.id.btn).setOnClickListener(this); tv_show= (TextView) findViewById(R.id.tv_show); }
一.OnGlobalFocusChangeListener
首先测试ViewTreeObserver.OnGlobalFocusChangeListener,实现接口方法代码
@Override public void onGlobalFocusChanged(View oldFocus, View newFocus) { if(oldFocus!=null) { tv_show.setText("Focus change from " + oldFocus.getTag() + " to " + newFocus.getTag()); }else{ tv_show.setText( newFocus.getTag()+"get focus"); } }
注意:在第一次进入页面的时候没有oldFoucs
效果
这个接口很简单就是监听focus的变化。二.OnPreDrawListener
OnPreDrawListener接口是在绘制界面前调用代码
@Override public boolean onPreDraw() { et_1.setHint("set hint on onPreDraw "); //Return true to proceed with the current drawing pass, or false to cancel. //返回 true 继续绘制,返回false取消。 return true; }
效果
如果返回false的话,效果是这样的,界面没有绘制。
关于OnPreDrawListener的使用,有一个例子就是CoordinatorLayout调用Behavior的onDependentViewChanged就是通过注册OnPreDrawListener接口,在绘制的时候检查界面是否发生变化,如果变化就调用Behavior的onDependentViewChanged。
源码
@Override public void onAttachedToWindow() { super.onAttachedToWindow(); resetTouchBehaviors(); if (mNeedsPreDrawListener) { if (mOnPreDrawListener == null) { mOnPreDrawListener = new OnPreDrawListener(); } //注册OnPreDrawListener final ViewTreeObserver vto = getViewTreeObserver(); vto.addOnPreDrawListener(mOnPreDrawListener); } if (mLastInsets == null && ViewCompat.getFitsSystemWindows(this)) { // We're set to fitSystemWindows but we haven't had any insets yet... // We should request a new dispatch of window insets ViewCompat.requestApplyInsets(this); } mIsAttachedToWindow = true; } class OnPreDrawListener implements ViewTreeObserver.OnPreDrawListener { @Override //分发OnDependentViewChanged dispatchOnDependentViewChanged(false); return true; } } void dispatchOnDependentViewChanged(final boolean fromNestedScroll) { final int layoutDirection = ViewCompat.getLayoutDirection(this); final int childCount = mDependencySortedChildren.size(); for (int i = 0; i < childCount; i++) { final View child = mDependencySortedChildren.get(i); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); // Check child views before for anchor for (int j = 0; j < i; j++) { final View checkChild = mDependencySortedChildren.get(j); if (lp.mAnchorDirectChild == checkChild) { offsetChildToAnchor(child, layoutDirection); } } //判断是否发生变化 // Did it change? if not continue final Rect oldRect = mTempRect1; final Rect newRect = mTempRect2; getLastChildRect(child, oldRect); getChildRect(child, true, newRect); if (oldRect.equals(newRect)) { continue; } recordLastChildRect(child, newRect); // Update any behavior-dependent views for the change for (int j = i + 1; j < childCount; j++) { final View checkChild = mDependencySortedChildren.get(j); final LayoutParams checkLp = (LayoutParams) checkChild.getLayoutParams(); final Behavior b = checkLp.getBehavior(); if (b != null && b.layoutDependsOn(this, checkChild, child)) { if (!fromNestedScroll && checkLp.getChangedAfterNestedScroll()) { // If this is not from a nested scroll and we have already been changed // from a nested scroll, skip the dispatch and reset the flag checkLp.resetChangedAfterNestedScroll(); continue; } //调用onDependentViewChanged final boolean handled = b.onDependentViewChanged(this, checkChild, child); ... } } } }
三.OnGlobalLayoutListener
Interface definition for a callback to be invoked when the global layout state or the visibility of views within the view tree changes.
当在一个视图树中全局布局发生改变或者视图树中的某个视图的可视状态发生改变时,所要调用的回调函数的接口类
代码
1.在点击时改变EditText的可视性。@Override public void onClick(View v) { if(et_1.isShown()){ et_1.setVisibility(View.GONE); }else{ et_1.setVisibility(View.VISIBLE); } }
2.在onGlobalLayout显示EditText的可见性
@Override public void onGlobalLayout() { if(et_1.isShown()){ tv_show.setText("EditText1 显示"); }else{ tv_show.setText("EditText1 隐藏"); } }
效果
注意:在测试的时候发现使用
et_1.setVisibility(View.INVISIBLE);
时并不会触发OnGlobalLayoutListener而只能使用
et_1.setVisibility(View.GONE);
补充
可以使用OnGlobalLayoutListener获取控件宽高。private int mHeaderViewHeight; private View mHeaderView; ..... mHeaderView.getViewTreeObserver().addOnGlobalLayoutListener( new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { mHeaderViewHeight = mHeaderView.getHeight(); mHeaderView.getViewTreeObserver() .removeGlobalOnLayoutListener(this); } });
关于OnTouchModeChangeListener的使用目前还没有搞清楚,以后再回来补充吧。
相关文章推荐
- java.lang.IllegalStateException: Cannot forward after response has been committed
- Java数据类型和MySql数据类型对应表
- Velocity配置详解(三)
- Android之WebView加载网页
- MySQL存储过程
- 存储过程-求当前日期是这个月的第几周
- seajs官方文档地址
- 权限和特征的对应
- 数据存储全方案-文件存储、sharedPreferences、sqlite
- ubuntu下swoole安装
- dyld: Library not loaded: @rpath/libswiftCore.dylib 错误的解决
- 401,404的布尔值居然是False,又被坑了
- 二叉树的非递归遍历算法
- Python常见错误归纳
- putty pscp.exe使用
- SVN创建分支/合并分支/切换分支
- 一个自定义函数的例子
- LeetCode152:Maximum Product Subarray
- 学习笔记:HSB、HSL
- 摇一摇js代码