转载地址:http://blog.csdn.net/liguangzhenghi/article/details/8076121#comments
在原作者blog的基础上略微修改,ViewTreeObserver是用来帮助我们监听某些View的某些变化的。
在 ViewTreeObserver 中,包含了以下几个接口:
interfaceViewTreeObserver.OnGlobalFocusChangeListe
interfaceViewTreeObserver.OnGlobalLayoutListener 当布局发生变化是触发
interface ViewTreeObserver.OnPreDrawListener 事件出发前的准备工作
interfaceViewTreeObserver.OnScrollChangedListener 当发生滑动事件时触发
interfaceViewTreeObserver.OnTouchModeChangeListene
本文将测试除 ViewTreeObserver.OnScrollChangedListener外的四个接口
小结
在Android TouchMode模式下是不存在focus的。所谓的focus是指被选中的意思,类似于windows平台下单击一个文件夹后鼠标的焦点(focus)就在该文件夹上面。既然不存在focus,也就不存在FocusChange的问题。
对于一个拥有触摸屏功能的设备而言, 一旦用户用手点击屏幕, 设备立刻进入touch mode . 这时候被点击的控件只有isFocusableInTouchMode()方法返回true的时候才会 focusable ,通常只有诸如EditText这样的对象才能在选中时获得焦点。而其他对象(如Button),由于它们可以在用户触屏时被直接选中,所以不必获得焦点就可以出发行为,它们只是简单地执行onClick事件而已。可将下面的代码拷贝运行测试一下。
当用户直接使用keys或trackball与UI进行交互的时候, 必须先使目标控件获取焦点(比如按钮),这样用户才会注意到是什么控件接收输入. 然而如果设备支持触摸手势的话, 用户可能使用触摸屏与UI进行交互, 这个时候就没有必要将目标控件高亮显示了(即,获取焦点). 因此就产生了这样一种交互模式叫"touch mode ."任何时候只要用户点击key或滚动trackball, 设备就会退出touch mode ,并且找一个view将焦点置于其上. 此时用户可以不使用触摸手势了.
touch mode 在整个系统运行期间都是有效的(在任何activities中). 如果想要查询当前处于何种状态, 你可以调用View#isInTouchMode()来看看当前是否处于touch mode .
如果想在TouchMode下面使Button使用这个监听器可以使用如下两种方法:
1. 增加一句代码:mImageButton.setFocusableInTouchMode(true);l
2.在main.xml中mImageButton标签中加入一个子标签:android:focusableInTouchMode="true"。
这两种方法的作用都是使得focus在TouchMode下恢复作用。
1.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/full_screen"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
tools:context=".MainActivity" >
<TextView
android:id="@+id/tv_show"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text=""
android:textSize="32px" />
<EditText
android:id="@+id/ed_enter1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="" />
<EditText
android:id="@+id/ed_enter2"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="" />
<TextView
android:id="@+id/tv_display"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="" />
<Button
android:id="@+id/button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="OK" />
</LinearLayout>
注意:给 layout 增加一个 id : full_screen
2.
package com.example.touchmodechangeldg;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewTreeObserver;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends Activity implements OnClickListener,
ViewTreeObserver.OnTouchModeChangeListener, // 用于监听Touch和非Touch模式的转换
ViewTreeObserver.OnGlobalLayoutListener, // 用于监听布局之类的变化,比如某个空间消失了
ViewTreeObserver.OnPreDrawListener, // 用于在屏幕上画 View 之前,要做什么额外的工作
ViewTreeObserver.OnGlobalFocusChangeListener { // 用于监听焦点的变化
private TextView tv_show;
private ViewTreeObserver vto;
private View all;
private EditText ed1;
private EditText ed2;
private TextView tv_display;
private Button button;
private boolean btnClicked;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv_show = (TextView) this.findViewById(R.id.tv_show);
all = this.findViewById(R.id.full_screen); // 得到整个屏幕对象 , 因为顶层 layout
// 的width 和 height
// 都是fill_parent
vto = (ViewTreeObserver) all.getViewTreeObserver(); // 通过getViewTreeObserver获得ViewTreeObserver对象
tv_display = (TextView) this.findViewById(R.id.tv_display);
ed1 = (EditText) this.findViewById(R.id.ed_enter1);
ed2 = (EditText) this.findViewById(R.id.ed_enter2);
button = (Button) this.findViewById(R.id.button);
button.setOnClickListener(this);
vto.addOnTouchModeChangeListener(this); // 增加对应的 Listener
vto.addOnGlobalFocusChangeListener(this); // 增加对应的 Listener
vto.addOnPreDrawListener(this); // 增加对应的 Listener
vto.addOnGlobalLayoutListener(this); // 增加对应的 Listener
}
// onTouchModeChanged 是接口 ViewTreeObserver.OnTouchModeChangeListener中定义的方法。
@Override
public void onTouchModeChanged(boolean isInTouchMode) {
if (isInTouchMode)
tv_show.setText("In touch mode");
else
tv_show.setText("Not in touch mode");
}
// onGlobalLayout 是接口 ViewTreeObserver.OnGlobalLayoutListener中定义的方法。
// Callback method to be invoked when the global layout state or the visibility of views within the view tree changes
@Override
public void onGlobalLayout() {
if (btnClicked) {
if (!ed2.isShown())
ed1.setText(" 第二个 EditText 不见了 ");
else
ed1.setText(" 第二个 EditText 出来了 ");
}
}
// onPreDraw 是接口 ViewTreeObserver.OnPreDrawListener中定义的方法。
@Override
public boolean onPreDraw() {
// 在屏幕上画出 ed1 控件之间 , 给它增加一个提示 , 并改变其字体大小
ed1.setHint(" 在 onPreDraw 方法中增加一个提示信息 ");
ed1.setTextSize((float) 20.0);
// return false; // Return true to proceed with the current drawing
// pass, or falseto cancel.
return true; // 如果此处不返回 true , 则整个界面不能完整显示。
}
@Override
public void onClick(View v) {
// 改变 ed2 的可见性 , 会触发 onGlobalLayout 方法的执行
btnClicked = true;
if (v.getId() == R.id.button) {
if (ed2.isShown())
ed2.setVisibility(View.INVISIBLE);
else
ed2.setVisibility(View.VISIBLE);
}
}
// onGlobalFocusChanged 是接口 ViewTreeObserver.OnGlobalFocusChangeListener中定义的方法。
// 焦点发生变化时,会触发这个方法的执行
@Override
public void onGlobalFocusChanged(View oldFocus, View newFocus) {
// TODO Auto-generated method stub
if (oldFocus != null && newFocus != null) {
tv_display.setText("Focus /nFROM:/t" + oldFocus.toString()
+ "/n TO:/t" + newFocus.toString());
}
}
}
3.
我运行了一下,并没有上面这个界面(Not in touch mode),而是直接出现了下面的界面(In touch mode),再研究一下吧。
可以看到第一个 EditText 中存在字体发生了变化的提示信息,这种效果是在 onPreDraw() 方法中实现的。
用鼠标点击屏幕上的第二个 EditText有两个变化:
一个是有 Not in touch mode 变成了 In touch mode(没发现)
二是显示了焦点变化方面的信息(这个有)
它们分别是 onTouchModeChanged 和 onGlobalFocusChanged 这两个方法所输出的信息。
如果用模拟器右边的键盘进行操作,将交掉移动到第一个 EditText ,则又会回到 Not in touch mode 的状态。
点击 OK 按钮,改变第二个 EditText 的可见性:
第一个 EditText 中的内容是在 onGlobalLayout 方法中设定的。