view变化监听器ViewTreeObserver介绍

转载地址:http://blog.csdn.net/liguangzhenghi/article/details/8076121#comments

在原作者blog的基础上略微修改,ViewTreeObserver是用来帮助我们监听某些View的某些变化的。

 

在 ViewTreeObserver 中,包含了以下几个接口:

interfaceViewTreeObserver.OnGlobalFocusChangeListener 当布局中的处理焦点发生变化时触发

interfaceViewTreeObserver.OnGlobalLayoutListener 当布局发生变化是触发

interface ViewTreeObserver.OnPreDrawListener  事件出发前的准备工作

interfaceViewTreeObserver.OnScrollChangedListener 当发生滑动事件时触发

interfaceViewTreeObserver.OnTouchModeChangeListener 当接触模式发生变化时触发

本文将测试除 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.    创建一个 Android Project ,修改 main.xml 使之如下:

<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.    Activity 对应的 Java 代码如下:

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 方法中设定的。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值