android开发中的UI控制(七)
转载自 http://www.android777.com/index.php/tutorial/android-view/androids-ui-control-g.html
到这里大家应该都会使用一些简单控件、有趣的控件结合布局管理对象来创建一些简单的界面了。可是如果光只有界面而没有对应的UI交互的话那整个应用 就像一幅画一样,只能看不能编辑修改。所以我们要学会处理一些UI事件,让整个应用能“活起来”,而不是看起来就像是几张图拼起来的。
在Android中,我们用拦截事件来处理UI交互。当用户触摸屏幕、点击按钮、输入文字时都会触发对应的事件。我们通过拦截这些事件,在事件发生之前或之后插入一些代码来实现UI交互。
首先我们要知道android使用的是跟java GUI一样的单线程事件模型,系统有将所有UI事件按照发生顺序推入一个EventQueue中,然后有一个UI线程专门将事件从EventQueue中移除,然后执行它,具体看这里 。 为了让每个View都能响应事件,View提供了几个常用的回调方法用来响应事件。比如一个Button对象,当你在触摸屏上点击它时,系统就会产生一个 触摸事件,然后调用Button对象中的onTouchEvent方法。为了要拦截这个事件,你可以扩展Button对象重写里面的 onTouchEvent方法,在里面插入你需要完成的逻辑代码。但是一个界面一般都是由多个View对象组成的,如果每个View对象都需要被扩展然后 重写对应的事件代码,那编码上就很难,也很难维护。所以View对象里也提供了一系列的内部接口,让你可以通过它们去捕捉对应的事件,这些内部的接口就 叫:EventListener。
对应不同的事件类型,android在View里内置了不同的EventListener。你需要将这些不同类型的EventListener注册 到View对象,那么当View对象触发了对应的事件后就会调用注册这个事件的EventListener。几个常用的EventListener有:
点击事件:
View.onClickListener :注册事件后(通过调用View.setOnClickListener),当用户点击或者触摸视图时会调用这个接口定义的方法。
View.onLongClickListener:注册事件后(调用View.setOnLongClickListener),当用户长按住某个视图时调用这个接口。
View.onFocusChangeListener:注册事件后(通过View.setOnFocusChangeListener),当当前视图焦点变化时会调用这个接口。
View.onKeyListener:注册事件后(通过View.setOnKeyListener),当视图接受到键盘事件时会调用这个接口。
View.onToucheListener:注册事件后(通过View.setOnTouchListener),当用户在触摸屏上触摸、释放、移动手指时会调用这个接口。
下面演示如何给一个按钮注册点击事件:
1.直接用匿名接口实现类对按钮添加事件,它的好处是代码简洁,缺点是当有很多个View对象时,代码会比较难维护:
package com.android777.demo.uicontroller; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.Toast; public class ListenerDemoActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.listener); Button b1 = (Button) findViewById(R.id.button1); b1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(ListenerDemoActivity.this, "您点击了按钮", Toast.LENGTH_LONG).show(); } }); } }
2. 将UI事件的处理代码交由Activity来处理,这时候你的Activity需要多实现一个onClickListener接口。它的好处是代码比较简洁,可以由Activity来同时当做好几个View的事件处理对象:
package com.android777.demo.uicontroller; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.Toast; public class ListenerDemoActivity extends Activity implements OnClickListener{ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.listener); Button b1 = (Button) findViewById(R.id.button1); b1.setOnClickListener(this); } @Override public void onClick(View v) { Toast.makeText(ListenerDemoActivity.this, "您点击了按钮", Toast.LENGTH_LONG).show(); } }
3. 在布局文件的xml中定义引用到这个xml文件的Activity中哪个方法要处理对应的按钮事件(这个很简洁明了,不过有点违背了MVC的理念,它在 SDK1.6+ 才有支持):
listener.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="一个按钮" android:id="@+id/button1" android:onClick="onButton1Click" /> </LinearLayout>
java代码:
package com.android777.demo.uicontroller; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.Toast; public class ListenerDemoActivity extends Activity{ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.listener); } //xml中定义的按钮事件代码, 方法签名必须是 public void xxxx(View); public void onButton1Click(View v) { Toast.makeText(ListenerDemoActivity.this, "您点击了按钮", Toast.LENGTH_LONG).show(); } }
三种写法的运行效果都是一样的:
上面的事件处理代码onClick没有返回值。但是有很多其他的事件处理代码需要返回一个boolean值,为什么需要一个返回值呢?这跟这些事件的性质有关,看下面:
onLongClick:它需要一个返回值来指示android事件处理系统,是否在这个方法里已经销毁了这个事件(都完成了,不需要交给其他处 理)或者不处理这个事件,由其他的注册View来处理。如果返回true值,表示这个事件已经处理完了,可以直接销毁,返回false表示这个事件还没处 理完成,由android事件处理系统将这个事件交给其他注册的EventListener进行再处理。
onKey:跟onLongClick差不多,返回true表示处理完成,返回false则交由其他注册的EventListener处理。
onTouch:这个比较不一样,因为onTouch后还有很多其他的动作。如果返回true,就表示事件已经处理完成,则后面发生的移动手指,释放手指动作就都被忽略掉。
有一点需要注意的是KeyEvent键盘事件会被传递到当前有焦点的View对象中,它的传递过程是一个从顶至下的过程,假设你一个View对象嵌套在2层的View对象里,如果上层的View对键盘事件返回true值,则事件就不会传递到当前焦点的View对象中。
触摸模式:
一般的不是触摸屏的手机,你需要使用方向键将当前焦点在多个视图内转换,焦点只会跳到一些具备焦点的View对象。如果手机是触摸屏的,你就需要使 用手指触摸来点击一些View,这时候很多对象将没有焦点可以看到,如输入框还能看到焦点、按钮就看不到焦点状态。当你用手指触摸屏幕产生触摸事件时,系 统就会进入触摸模式。在触摸模式中,只有View.isFocusableInTouchMode()方法返回true的值,才具备焦点。所以你在 EditText里调用这个方法会返回true,而在Button中则返回false,当你触摸按钮时则会触发onClick事件。
当用户使用方向键时则退出触摸模式,系统将按照一般设备来执行。是否触摸模式是由系统来维护的,你可以通过调用Window或Activity中的isInTouchMode方法来判断现在系统所处的模式。