如何分别在Android、iOS、Windows Phone三大平台进行“触摸屏”开发?

来源:互联网 发布:淘宝假酒多吗 编辑:程序博客网 时间:2024/04/27 15:46

《移动开发全平台解决方案——Android/IOS/Windows Phone》

源代码下载地址:http://www.devdiv.com/thread-85477-1-1.html

当当网有详细目录:

http://product.dangdang.com/product.aspx?product_id=22470281&ref=search-1-pub

第4章 触 摸 屏

触摸屏给用户带来了全新的体检。几年前,它还是高端手机的专利,现在,触摸屏、多点触摸已成为手机必须支持的内容。手机对触摸屏的支持极大地方便了用户操作,多点触摸、手势的引入成就了很多经典应用,如滑动页面、浏览器、图片放大等。Android、iOS、Windows Phone对触摸屏的支持非常好,本章将介绍如何设计触摸屏程序、如何支持手势等。

单点是指每次一个指头触控屏幕;而两个以上的指头同时触屏称为多点触摸。一系列的触摸点形成的路径称为手势。

4.1 Android触摸屏

输入是用户交互的一个重要组成部分,触摸屏和键盘是Android系统的标配输入设备,多数游戏、应用都离不开这两个输入设备。本节给出一个小程序,演示基本的触摸屏和按键处理,虽然不能覆盖全部的情况,但至少可以起到抛砖引玉的作用。

4.1.1 Android输入处理

在Android系统中,输入事件一般是由View类来处理的,通过实现一些事件监听接口,就能够处理这些事件,并且决定是否拦截下来。

1)事件监听接口及设置监听

Android系统提供了几个事件监听接口,见表4-1。

表4-1 Android系统事件监听接口

事件监听接口

方  法

功  能

View.OnClickListener

onClick(View)

监听在View上面的屏幕单击事件

View.OnLongClickListener

onLongClick(View)

监听在View上面的屏幕长按事件

View.OnKeyListener

onKey(View)

监听在View上面的按键事件

View.OnTouchListener

onTouch(View,MotionEvent)

监听在View上面的屏幕长按事件

 View.OnKeyListener只有在View被选中的情况下才会监听到事件,所以这个按键处理的方法不是很常用。

如果要监听某个View窗口中的输入事件,只要实现对应的接口,并且在View中设置监听即可。比如:

public class TouchDemo extends Activity

implements OnClickListener {

//..省略部分代码

mTouchView.setOnClickListener(this);

//..省略部分代码

         @Override

         public void onClick(View arg0) {

                            if(arg0.getId() == R.id.tv1) {

                                     mLogView.append("onClick event\n");

                                     mLogView.scrollBy(0, 10);

                            }

         }

}

}

上面的代码实现了一个简单的监听接口(interface),并将其添加到View窗口的监听队列中去。

 除了监听接口以外,View类里面也有默认的处理方法,在实现View类的时候进行重载就可以了:

l      onKeyDown(int, KeyEvent)——处理按键按下事件;

l      onKeyUp(int, KeyEvent)——处理按键弹起事件;

l      onTrackballEvent(MotionEvent)——处理轨迹球运动事件;

l      onTouchEvent(MotionEvent)——处理触摸事件。

2)按键事件处理

上一节已介绍过可以在View中处理按键事件,只要实现了OnKeyEventListener接口即可,同时也可以使用View类的onKeyDown()或者onKeyUp()。但是我们往往希望按键事件是在整个程序屏幕上都可以响应的,所以这个时候在Activity中进行响应会更加方便。

Activity提供了“boolean onKeyDown(int keyCode,KeyEvent event)”方法,可以监听到按键,而KeyEvent类提供了按键编码的常量定义,例如KeyEvent.KEYCODE_MENU表示菜单按键。

3)输入事件拦截器

在了解了触屏和按键操作的处理以后,我们来完成一个输入事件拦截器的小应用,如图4-1所示。

拦截功能区

触摸区

事件日志区

图4-1

程序实现了对click事件longclick、touch轨迹的拦截,从图4-1中可以看出,实现了所有4种拦截全部打开,而触摸的轨迹在屏幕上面也形成了“test”字样。也就是说,这个小程序可以实现单击、长按、触摸事件的拦截,而要实现按键、多点触摸、方向的拦截只需在这个小程序的基础上进行一定的扩充即可。

下面来看具体的程序实现:

(1)布局文件main.xml的片段

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"

         Android:id="@+id/mainview"

  Android:orientation="vertical"

  Android:layout_width="fill_parent"

  Android:layout_height="fill_parent"

  >

 

         <LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"

           Android:orientation="horizontal"

           Android:layout_width="fill_parent"

           Android:layout_height="200px"

           > 

                   <TextView 

                            Android:id="@+id/tv0"

                     Android:layout_width="200px"

                     Android:layout_height="200px"

                     Android:text="event logs\n"

                     />

        

                   <LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"

                     Android:orientation="vertical"

                     Android:layout_width="fill_parent"

                     Android:layout_height="fill_parent"

                     > 

                            <CheckBox Android:id="@+id/box_click"

                                                Android:text="拦截click"

                                                Android:layout_width="fill_parent"

                                                Android:layout_height="wrap_content"

                            />

                            <CheckBox Android:id="@+id/box_longclick"

                                                Android:text="拦截longclick"

                                                Android:layout_width="fill_parent"

                                                Android:layout_height="wrap_content"

                            />

                            <CheckBox Android:id="@+id/box_touch"

                                                Android:text="拦截touch"

                                                Android:layout_width="fill_parent"

                                                Android:layout_height="wrap_content"

                            />

                            <CheckBox Android:id="@+id/box_touch_track"

                                                Android:text="touch轨迹"

                                                Android:layout_width="fill_parent"

                                                Android:layout_height="wrap_content"

                            />

                   </LinearLayout>

         </LinearLayout> 

<devdiv.example.TouchDemo.SimpleView 

         Android:id="@+id/tv1"

  Android:layout_width="320px"

  Android:layout_height="200px"

  Android:text="@string/hello"

  />

</LinearLayout>

(2)主类(实现了所有的事件监听器)

public class TouchDemo extends Activity

implements OnClickListener, OnLongClickListener, OnTouchListener

,OnCheckedChangeListener{

         private TextView mLogView;

         private SimpleView mTouchView;

         private CheckBox mBoxClick;

         private CheckBox mBoxLongClick;

         private CheckBox mBoxTouch;

         private CheckBox mBoxTouchTrack;

  @Override

  public void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.main);

    mLogView = (TextView)findViewById(R.id.tv0);

    mTouchView = (SimpleView)findViewById(R.id.tv1);

    mTouchView.setBackgroundColor(Color.CYAN);

    mTouchView.setOnClickListener(this);

    mTouchView.setOnLongClickListener(this);

    mTouchView.setOnTouchListener(this);

    mLogView.setTextSize((float) 10.0);

    

    mBoxClick = (CheckBox)findViewById(R.id.box_click);

    mBoxLongClick = (CheckBox)findViewById(R.id.box_longclick);

    mBoxTouch = (CheckBox)findViewById(R.id.box_touch);

    mBoxTouchTrack = (CheckBox)findViewById(R.id.box_touch_track);

    mBoxTouchTrack.setOnCheckedChangeListener(this);

  }

 

         @Override

         public void onClick(View arg0) { // 单击

                   if(mBoxClick.isChecked()) { // 确定用户是否选择了监听单击事件

                            if(arg0.getId() == R.id.tv1) {

                                     mLogView.append("onClick event\n"); // 记录到日志窗口

                                     mLogView.scrollBy(0, 10); // 滚动日志

                            }

                   }

         }

 

         @Override

         public boolean onLongClick(View arg0) { // 长按

                   if(mBoxLongClick.isChecked()) {

                            if(arg0.getId() == R.id.tv1) {

                                     mLogView.append("onLongClick event\n");

                            }

// 下面进行随机返回处理,以便于观察事件处理之间的关系,请参考后面的小结

                            if(System.currentTimeMillis() % 2 == 0) {

                                     mLogView.append("onLongClick return true\n");

                                     return true;

                            } else {

                                     mLogView.append("onLongClick return false\n");

                                     return false;

                            }

                   }

                   return false; // 默认不处理,交由后面的环节去处理

         }

 

         @Override

         public boolean onTouch(View arg0, MotionEvent ev) {

                   if(mBoxTouchTrack.isChecked()) {

                            // FIXME: 多点触摸版本

// 参考后面对多点触摸API的描述,很容易就能够修改成支持多点触摸的版本

                            final int historySize = ev.getHistorySize();

                            for (int h = 0; h < historySize; h++) {

                                     mTouchView.DrawPoint(ev.getHistoricalX(h), ev.getHistoricalY(h));

                            }

                            mTouchView.DrawPoint(ev.getX(), ev.getY());

                   }

                   if(mBoxTouch.isChecked()) {

                            if(arg0.getId() == R.id.tv1) {

                                     mLogView.append("onTouch event point(" + ev.getX() +"," + ev.getY() + ")" );

                                     mLogView.scrollBy(0, 10);

                            }

// 同样,随机返回处理,只是用于实验,真实的场景不必如此

                            if(System.currentTimeMillis() % 2 == 0) {

                                     mLogView.append("onTouch return true\n");

                                     mLogView.scrollBy(0, 10);

                                     return true;

                            } else {

                                     mLogView.append("onTouch return false\n");

                                     mLogView.scrollBy(0, 10);

                                     return false;

                            }

                   }

                   return false;

         }

 

         @Override

         public void onCheckedChanged(CompoundButton arg0, boolean arg1) {

                   if(arg0 == mBoxTouchTrack) {

                            if(!arg1) {

                                     mTouchView.InitCache(); // 清空轨迹面板

                            }

                   }

         }

 

         @Override

         public boolean onKeyDown(int keyCode, KeyEvent event)

  {

                   mLogView.append("keyCode " + keyCode + " action " + event.getAction());

                   if (keyCode == KeyEvent.KEYCODE_MENU) { // 按下菜单按键的处理

                            mLogView.append("按下菜单");

                            return true;

                   }

    return super.onKeyDown(keyCode, event);

  }

}

(3)触摸类(负责接收触摸事件并画出轨迹)

public class SimpleView extends View {

         private final static int WIDTH = 320;

         private final static int HEIGHT = 200;

         private Bitmap mBitmapCache;

         private Canvas mCanvasCache;

         private Paint mPaint;

        

         public SimpleView(Context context) {

                   super(context);

                   Construct(context);

         }

        

         public SimpleView(Context context,AttributeSet attr) { 

                   super(context,attr); 

                   Construct(context);

         }

 

         private void Construct(Context context) {

                   // FIXME: onMesured的时候要重新创建cache

                   mBitmapCache = Bitmap.createBitmap(WIDTH,HEIGHT, Bitmap.Config.ARGB_8888);

                   mCanvasCache = new Canvas(mBitmapCache);

                  

                   mPaint = new Paint();

                   mPaint.setAlpha(0x40);

                   mPaint.setColor(Color.BLUE);

         }

         // 在缓冲中划一个点,并且通知刷新画面

         public void DrawPoint(float x, float y)

         {

                   mCanvasCache.drawPoint(x, y, mPaint);

                   invalidate();

         }

        

         public void InitCache() {

                   mCanvasCache.drawColor(Color.WHITE);

                   invalidate();

         }

        

         protected void onDraw(Canvas canvas) {

                   super.onDraw(canvas);

                   canvas.drawBitmap(mBitmapCache, 0, 0, mPaint);

         }

 

}

 

4.1.2 Android多点触摸与手势

单点触摸不能完成缩放、滑动等动作,这就需要多点触摸与手势的支持。

1)多点触摸

Android从2.0版本开始支持多点触摸,在API中体现出来就是一个MotionEvent事件里面包含若干个点的信息。因为不同硬件支持的点数不一样,所以需要通过API获取得到的点的个数。下面是一个SDK文档中的例子,并做了简单的注释:

 void printSamples(MotionEvent ev) {

  final int historySize = ev.getHistorySize(); // 获取历史采样的集合大小

  final int pointerCount = ev.getPointerCount(); // 获取事件点数

  for (int h = 0; h < historySize; h++) { // 对历史采样进行记录

  // 历史采样的时间点

    System.out.printf("At time %d:", ev.getHistoricalEventTime(h));

    for (int p = 0; p < pointerCount; p++) { //

      System.out.printf(" pointer %d: (%f,%f)",

        ev.getPointerId(p), ev.getHistoricalX(p, h), ev.getHistoricalY(p, h)); // 打印每个点的坐标

    }

  }

  // 处理当前的点,输出时间以及每个点的坐标

  System.out.printf("At time %d:", ev.getEventTime());

  for (int p = 0; p < pointerCount; p++) {

    System.out.printf(" pointer %d: (%f,%f)",

      ev.getPointerId(p), ev.getX(p), ev.getY(p));

  }

 }

 

2)手势

手势是用户输入的某个或者某系列参数动作,比如按下、上拉、下拉、划圈等。通过对onTouch(MotionEvent)方法里面的MotionEvent进行一段时间的采样并且进行模式检测,即可得到手势。具体的手势检测算法,对于某些应用来说可能是独特的,但是对于常用的软件,使用到的手势一般只有某个方向的滑动、快速滑动、按下、长按等,若是这类常规需求,可以考虑使用Android提供的GestureDetector类进行手势检测。

“GestureDetector”检测手势的输入源是MotionEvent,为了能够把MotionEvent输入到GestureDetector里面进行检测,需实现“GestureDetector.OnGestureListener”接口,一般在Activity里面实现。如果没有特殊要求,按照SDK的推荐,实现一个子接口“GestureDetector.SimpleOn GestureListener”,它有如下一些手势事件:

         boolean onDoubleTap(MotionEvent e)——双击。

         boolean onDoubleTapEvent(MotionEvent e)——双击按下和弹起都触发,通过e.getAction()判断按下或者弹起。

         boolean onDown(MotionEvent e)——按下。

         boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)——滑动,其中后两个参数是X轴和Y轴的速度,在弹起的时候触发。

         void onLongPress(MotionEvent e)——长按,按住的时候触发。

         boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)——滑动,滑动过程中触发。

         void onShowPress(MotionEvent e)——若按下较长时间未滑动,则触发该事件。

         boolean onSingleTapConfirmed(MotionEvent e)——如果只有按下事件而后面不带滑动事件,就触发该事件。

         boolean onSingleTapUp(MotionEvent e)——按下并且弹起的时候触发。

创建GestureDetector,把onTouch得到的MotionEvent交由GestureDetector.onTouchEvent(event)处理。示例代码如下:

public boolean onTouch(View view, MotionEvent event) {

  return mGestureDetector.onTouchEvent(event);

}

 屏幕单击、长按、滑动、键盘单击等只不过是一些离散的输入信号,它们被组织成各种事件、手势的过程,有潜在的相互影响,比如在一个事件被当做长按处理以后,便不会产生单击事件,这一点从上面的小程序中的返回值可以看出,如图4-2所示。

图4-2

第一次长按的处理函数中,返回了“true”,结果监听器就监听不到单击事件,而第二次长按处理函数中返回了“false”,接着就发生了一次单击事件。所以当程序需要进行很复杂的输入事件、手势处理的时候,需要仔细研究一下它们产生的顺序以及相互之间的影响。

4.2 iOS触摸屏

iOS的触摸屏用户体验是非常好的,iOS也是最早支持多点触摸的操作系统。多点触摸、手势等在iOS开发中得到了广泛应用。这一节着重介绍iOS触摸屏的相关操作。

4.2.1 iOS输入处理

触摸这种操作方式,带给我们更加人性化的体验。例如,可避免因长时间按压物理键盘而导致的手指酸痛,而且采用电容屏,可以屏蔽一些非人为的输入操作。触摸,给屏幕带来更加灵活的可操作性,摆脱了物理键盘的功能限制,用户可以使用屏幕上的每一像素,只要手指足够小并可以单击到。

4.2.2 iOS多点触摸与手势

iOS赋予用户至少3.5英寸的宽广视野,在当时可谓令人眼前一亮。在这不大不小的舞台上,手指可以灵活地跳动,而在此之前,传统的触屏手机都只能用单指,而且很多都是电阻屏。但是自从iOS 横空出世之后,一切都改变了。

1)多点触摸

iOS彻底打破了传统手机的操作模式,多点触摸使之更为人性化。多点触摸的实现代码如下:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{

         NSUInteger numTouches = [touches count];

}

上述方法传递一个NSSet实例与一个UIEvent实例,可以通过获取touches参数中的对象来确定当前有多少根手指触摸,touches中的每个对象都是一个UITouch事件,表示一个手指正在触摸屏幕。倘若该触摸是一系列轻击的一部分,则还可以通过询问任何UITouch对象来查询相关的属性。

同鼠标操作一样,iOS也可以有单击、双击甚至更多类似的操作,有了这些,在这个有限大小的屏幕上,可以完成更多的功能。正如上文所述,通过访问它的touches属性来查询:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{

         NSUInteger numTaps = [[touches anyObject] tapCount];

}

2)手势

手势是指从一个或多个手指接触屏幕开始,到手指离开屏幕为止所发生的所有事件。无论这个过程耗时多长,只要还有手指停留在屏幕上,就处于某个手势之中,除非发生意外情况。

有了手势之后,屏幕才可以感应到我们的手在做什么动作。很多场合,一些控件已经能够支持双指拉开放大、捏合缩小的动作,图片的多指旋转功能灵活、方便,符合我们的生活习惯,诸如此类的功能都是多指技术应用于现实的最好证明。可以通过以下方式检测手势:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{

         If ([touches count]==2) { // 检测触摸点个数

                   NSArray *twoTouches = [touches allObjects];  // 获取触摸点数组

                   UITouch *first = [twoTouches objectAtIndex:0];  // 第一个触摸点

                   UITouch *second = [twoTouched objectAtIndex;1];  // 第二个触摸点

CGFloat initialDistance = distanceBetweenPoints(

[first locationInView:self.view],[second locationInView:self.view]);  // 计算两个触摸点之间的距离

}

}

iPhone iOS 4.x可以通过设置开启屏幕的缩放功能:三指连按两次便可切换放大,在放大的情况下还可以通过三指来移动屏幕;再次三指连按两次便可恢复正常状态。

iPad iOS 4.3已经可以支持四指操作,通过各种手势,可以实现诸多以前无法实现的效果:四指向上滑动可以显示后台运行的程序;四指向左滑动可以向左切换已打开的程序,四指向右滑动则可以向右切换已打开的程序;在程序打开的情况下,四指捏合可以关闭当前程序。

4.3 Windows Phone触摸屏

Windows Phone与Windows Mobile相比,一个明显的区别是Windows Phone支持电容屏,不需要手写笔即可操作,支持多点触摸;当然,在这之前Android与iOS早已经支持了这些功能,不过Windows Phone的多点触摸支持做得很出色,以笔者的经验来看,采用Windows Phone的HTC MOZART手机要比iPhone 3GS多点触摸更容易操作。Windows Phone的多点触摸屏可以支持4个指头同时触摸,甚至更多。当然这对程序员来说不一定是好事,因为这意味着需要更多的程序来实现。要测试多点触摸程序,Windows Phone的手机必不可少,但是如果PC显示器支持多点触摸,那么使用Windows Phone模拟器也可以。

本节主要介绍两方面的内容:一是在Silverlight中如何处理低级别触摸屏事件(XNA中低级别触屏处理的方式与Silverlight类似);二是介绍Manipulation Event及Routed Event。

4.3.1 Windows Phone输入处理

本部分先介绍在Silverlight中是如何处理低级别触摸屏事件的,并且以开发中一个常见的问题作为例程。在正式介绍之前,先来了解一下什么是element。

1)element的含义

Silverlight编程中,element有两个含义:一是XML中的标签;二是界面中显示的对象,即UIElement。UIElement是非常重要的一个基类,所有的控件,如Grid、TextBlock、Button、Image等都集成于UIElement之中。

2)Silverlight触屏

Silverlight触屏的接口分为低级别和高级别两类,高级别的通过UIElement类的三个事件处理来实现——ManipulationStarted、ManipulationDelta和ManipulationCompleted。后面我们会介绍高级别事件处理接口;低级别事件处理接口则通过Touch.FrameReported实现。

Silverlight触屏接口最重要的一个类是TouchPoint,一个TouchPoint代表一个指头触摸屏幕。TouchPoint有4个只读属性(见表4-2)。

表4-2 TouchPoint的只读属性

属性

类型

说  明

Action

TouchAction

可以是Down、Move或者Up

Position

Point

表示触摸坐标,它是一个相对坐标,相对于UIElement左上角的位置

Size

Size

Windows Phone中目前这个还没有实际作用

TouchDevice

TouchDevice

TouchDevice对象有两个只读属性:

l    Id、int类型用来区分是哪个指头

l    DirectlyOver、UIElement类型表示最上层的UI Element是哪一个

要想使用低级别触屏接口,需要在程序中注册如下事件处理函数:

Touch.FrameReported += OnTouchFrameReported;

 一般可以把这行代码放到页面的构造函数中。

OnTouchFrameReported函数原型如下:

void OnTouchFrameReported(object sender, TouchFrameEventArgs args) { }

在这个处理函数中,可以通过TouchFrameEventArgs的以下三个方法获得所需的信息:

(1)GetTouchPoints,获得所有的触摸坐标,返回的是TouchPointCollection,多点触摸可以通过它来判断。

(2)GetPrimaryTouchPoint,只返回一个TouchPoint。

(3)SuspendMousePromotionUntilToucnUp,关于这个成员函数的具体含义可查阅MSDN。

3)Siliverlight应用程序

现在来创建一个Siliverlight应用程序DevDivTouchMultiPage,步骤如下。

1 打开MainPage.xaml.cs文件,在MainPage的构造函数中添加如下代码:

Touch.FrameReported += OnTouchFrameReported;

2 打开MainPage.xaml文件,添加一个TextBlock控件。

3 找到如下代码:

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"></Grid>

将其修改为:

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">

   <TextBlock Height="76" HorizontalAlignment="Left" Margin="62,147,0,0" Name="textBlock1" Text="TextBlock" VerticalAlignment="Top" Width="242" />

 </Grid>

4 为MainPage类添加一个成员函数:

void OnTouchFrameReported(object sender, TouchFrameEventArgs args)

{

  TouchPoint primaryTouchPoint = args.GetPrimaryTouchPoint(null);

  if (primaryTouchPoint != null && primaryTouchPoint.Action == TouchAction.Down)

  {

    if (primaryTouchPoint.TouchDevice.DirectlyOver == textBlock1)

    {

      Debug.WriteLine("button1 is pressed!");

    }

  }

}

5 在Visual Studio中按F5键调试,运行效果如图4-3所示。

图4-3

6 选中TextBlock文字,再查看Visual Studio的Output窗口,如图4-4所示。

图4-4

可以看到Output窗口显示“button1 is pressed!”。

4)使用低级别触屏接口容易遇到的问题

(1)多页面相关支持。前面的章节中已经简单介绍过,一个Silverlight程序显示界面的根节点是一个Frame,叫做RootFrame,在其上面可以显示页面。一个应用程序可以包含多个页面,默认加载的是“MainPage”页面,页面之间可以跳转。

为刚创建的“DevDivTouchMultiPage”添加一个“Windows Phone Portrait Page”,方法是右击工程,选择Add→New Item→Windows Phone Portait Page,这样,工程中就增加了Page1.xaml和Page1.xaml.cs。

修改MainPage.xaml,增加一个Button,并且为这个Button增加一个Click处理函数,当单击这个Button的时候,跳转到Page1页面中去。

将MainPage.xaml中的如下代码:

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">

   <TextBlock Height="76" HorizontalAlignment="Left" Margin="62,147,0,0" Name="textBlock1" Text="TextBlock" VerticalAlignment="Top" Width="242" />

 </Grid>

修改为:

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">

   <TextBlock Height="76" HorizontalAlignment="Left" Margin="62,147,0,0" Name="textBlock1" Text="TextBlock" VerticalAlignment="Top" Width="242" />

   <Button Content="Button" Height="72" HorizontalAlignment="Left" Margin="20,323,0,0" Name="button1" VerticalAlignment="Top" Width="430" Click="button1_Click"

/>

 </Grid>

修改MainPage.xaml.cs,为MainPage类增加一个成员函数:

private void button1_Click(object sender, RoutedEventArgs e)

{

  this.NavigationService.Navigate(new Uri("/Page1.xaml", UriKind.Relative));

}

代码很简单,通过NavigationService进行页面跳转。

执行程序,单击【Button】按钮之前和之后的界面对比如图4-5所示。

 

图4-5

可以看到单击【Button】按钮以后,页面跳转成功了。

(2)触屏处理事件的响应。不要退出程序,进入Visual Studio,打开“MainPage.xaml.cs”,在“OnTouchFrameReported”中设置一个断点,然后点手机或者模拟器Page1页面上的任意位置,这时候发生了意想不到的事情:我们明明点的是Page1的屏幕,执行的却是MainPage的“OnTouchFrameReported”,如图4-6所示。

图4-6

原来,我们调用的“Touch.FrameReported”注册触屏处理事件是针对应用程序的,而不是针对特定页面的,所以点Page1时会在MainPage中接收到触屏事件。关于这个问题的解决办法,后文会详细介绍。

【案例】 下面来做另一个有趣的小实验,实验之前先补充两点知识:首先,页面创建以后,程序退出之前是不销毁的,即从MainPage切换到Page1,MainPage不销毁;其次,可以在页面中加载事件,添加事件处理函数。步骤如下。

1 为MainPage添加页面处理函数,修改MainPage.xaml文件,将下列代码:

<phone:PhoneApplicationPage

  x:Class="DevDivTouchMultiPage.MainPage"

  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

  xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"

  xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"

  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

  mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"

  FontFamily="{StaticResource PhoneFontFamilyNormal}"

  FontSize="{StaticResource PhoneFontSizeNormal}"

  Foreground="{StaticResource PhoneForegroundBrush}"

  SupportedOrientations="Portrait" Orientation="Portrait"

  shell:SystemTray.IsVisible="True">

修改为:

<phone:PhoneApplicationPage

  x:Class="DevDivTouchMultiPage.MainPage"

  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

  xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"

  xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"

  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

  mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"

  FontFamily="{StaticResource PhoneFontFamilyNormal}"

  FontSize="{StaticResource PhoneFontSizeNormal}"

  Foreground="{StaticResource PhoneForegroundBrush}"

  SupportedOrientations="Portrait" Orientation="Portrait"

  shell:SystemTray.IsVisible="True"

  Loaded="PhoneApplicationPage_Loaded">

2 修改MainPage.xaml.cs文件,为MainPage类增加成员函数:

private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)

  {

    Touch.FrameReported += OnTouchFrameReported;

  }

同时把MainPage构造函数中的“Touch.FrameReported += OnTouchFrameReported;”去掉。

3 调试执行程序,在Visual Studio中按F5键开始执行,然后单击MainPage中的【Button】按钮切换到Page1,再按返回键(左箭头键),返回MainPage,在“OnTouchFrameReproted”上添加断点,如图4-7所示。

图4-7

4 用手触摸“MainPage”上任意一点,奇怪的事情发生了:每按一下屏幕,断点会执行两次,好像是按了两次屏幕一样。

 每当页面加载的时候,都会调用“Loaded”事件处理函数,也就是说,刚启动应用程序主页面加载时,调用一次OnTouchFrameReported,当从Page1切换回MainPage时,主页面又加载一次,则又调用一次OnTouchFrameReported,所以触摸屏幕的时候就会收到多个消息;如果在MainPage和Page1之间不断切换,就会发现,每切换一次都会多收到一次触屏事件。但是,如果把“Touch.FrameReported += OnTouchFrameReported;”加到MainPage的构造函数中就不会发生这样的问题了,因为主页面只构造一次,所以构造函数只会执行一次,FrameReported事件处理函数也只注册了一次。

(3)问题解决。回过头来我们去解决一开始遇到的问题,即在Page1中触摸屏幕却在MainPage中收到消息。我们知道,“Touch.FrameReported += OnTouchFrameReported”是注册事件处理函数,如果在显示Page1的时候不想处理触屏事件,自然想取消注册,这其实也很简单,只需要调用:

Touch.FrameReported -= OnTouchFrameReported;

于是我们修改buton1_Click,修改后代码为:

private void button1_Click(object sender, RoutedEventArgs e)

{

  Touch.FrameReported -= OnTouchFrameReported;

  this.NavigationService.Navigate(new Uri("/Page1.xaml", UriKind.Relative));

}

问题就解决了。

5)Silverlight里面高级别触屏处理涉及的3个事件

Silverlight里面高级别触屏处理涉及的三个事件分别为ManipulationStarted、ManipulationDelta和ManipulationCompleted。

Touch.FrameReported处理的是整个应用程序的触屏事件,而Manipulation事件是针对特定的UIElement的。其应用示例步骤如下。

1 创建一个Silverlight工程DevDivHelloManipulation。

2 打开MainPage.xaml文件,在其中加一个Button,然后为这个Button加上Manipulation处理函数,代码如下:

     <Button Content="Button" Height="72" HorizontalAlignment="Left" Margin="111,154,0,0" Name="button1" VerticalAlignment="Top" Width="160"

          ManipulationStarted="button1_ManipulationStarted"/>

3 打开MainPage.xaml.cs文件,为MainPage类加上成员函数:

private void button1_ManipulationStarted(object sender, ManipulationStartedEventArgs e)

{

  Debug.WriteLine("button1_ManipulationStarted called!");

}

4 调试执行程序。单击Button发现有Log输出,如果点屏幕其他地方则不会输出Log。

 Manipulation Event是针对特定UIElement的,而不是针对整个Application的。

6)Silverlight中routed event事件处理

一般来说,Manipulation事件由最上层的控件来处理,但是当它不能处理这个事件或者不愿意处理的时候,可以交给它的父控件来处理,就这样一直向上递交,直到Visual tree的顶点。

 界面上的所有UIElement都会因父子关系而形成一个树状结构,一个页面的根节点就是PhoneApplicationPage,而每一个Sliverlight应用程序都有一个Root,它是PhoneApplicationFrame,我们称之为Root Frame,所以页面中如果所有的控件都不处理Manipulation事件,则可以交给PhoneApplicationPage来处理,只需要PhoneApplicationPage的子类重载OnManipulationStarted即可。

                     ManipulationStarted事件的“event”参数是ManipulationStartedEventArgs,它继承于RoutedEventArgs。而RoutedEventArgs定义了OriginalSource属性,通过它可以知道事件是从哪个Element来的。

4.3.2 Windows Phone多点触摸与手势

Windows Phone中无论是低级别的处理函数,还是ManipulationEvent,都是支持多点触摸的。多点触摸的检测和上一节的例子差别不大,只是处理参数稍有不同。手势就是把触摸路径组合起来,在此不再举例说明。

 

 

 

 

 

 

 

 

原创粉丝点击