创建自定义控件3-可交互性

来源:互联网 发布:cms二次开发网站建设 编辑:程序博客网 时间:2024/05/22 03:07

绘制UI仅仅是创建自定义视图的一部分。除此之外,你还需要以一种非常接近现实世界行为的方式来响应用户的输入。所有的对象都应该表现得像现实世界的对象那样。例如,图片不应该突然消失后又出现在别的地方,因为现实中的物体是不会那样的。合乎情理的做法是它应该从一个位置移动到另一个位置。 用户要么体验到UI中精细的操作,要么仅仅感觉到只是界面,相比较而言前者模拟的现实世界会获得最好的用户体验。例如,当用户在操作UI的时候,他们一旦感到界面的滑动响应延迟,将会感到烦躁而疯狂的滑动屏幕。

这篇文章展示了如何结合Android框架的特点来增添现实世界的行为到你所定制的视图中。

处理输入手势-Handle Input Gestures


跟许多其它的UI框架一样,Android也支持输入事件模型。用户的操作会转变成触发回调函数的事件,你可以重写这个回调函数来定制你的应用如何响应用户。在Android系统中最常用的输入事件是触摸事件,它会触发onTouchEvent(android.view.MotionEvent),重写该方法可以处理该事件:

1234
@Override   public boolean onTouchEvent(MotionEvent event) {    return super.onTouchEvent(event);  }

触摸事件本身并不是特别的有用。现在的可触摸UI可以接受许多种手势动作的交互,如 轻拍、拉、推、摇动和缩放。Android系统提供了GestureDetector类,将基本的触摸事件转变成各种不同的手势动作。 通过实现GestureDetector.OnGestureListener接口来构造一个GestureDetector的一个实例。如果你仅仅只是想处理少部分的手势动作,你可以选择继承GestureDetector.SimpleOnGestureListener而不用去实现GestureDetector.OnGestureListener接口。例如,下面的代码就是创建了一个继承GestureDetector.SimpleOnGestureListener的类并重写了onDown(MotionEvent)方法。

1234567
class mListener extends GestureDetector.SimpleOnGestureListener {   @Override   public boolean onDown(MotionEvent e) {            return true;   }}mDetector = new GestureDetector(PieChart.this.getContext(),new mListener());

不管你是否使用了GestureDetector.SimpleOnGestureListener类,你都必须要实现onDown()方法并返回true。这一步是必须的因为所有的触摸操作都从onDown() 方法中的事件开始。如果你在onDown()方法中返回false,就像GestureDetector.SimpleOnGestureListener所设计的那样,系统会认为你不需要其它的手势操作,那么GestureDetector.OnGestureListener接口的其余方法都不会被调用。唯一能够将onDown()方法返回false的情况就是你真的想忽略所有的手势操作。一旦你实现了GestureDetector.OnGestureListener接口并且创造了GestureDetector类的一个实例,你就可以用GestureDetector类来完成你在 onTouchEvent()方法中接收的触屏事件。

 1 2 3 4 5 6 7 8 91011
@Overridepublic boolean onTouchEvent(MotionEvent event) {   boolean result = mDetector.onTouchEvent(event);   if (result) {       if(event.getAction()==MotionEvent.ACTION_UP){           stopScrolling();           result = true;       }   }       return result;}

当你想执行onTouchEvent()方法中的触摸事件而并不想把它当作手势操作的一部分,那就要返回false。你可以执行你自定义的检测手势操作的代码。

创建让人感觉合理的操作-Create Physically Plausible Motion


手势动作是控制触摸屏设备的一种很强有力的方式,但是它会违背人的直觉而让人难以记住,除非它让人得到合理的结果。滑动手指就是一个很好的例子,用户用一根手指快速的在屏幕上滑动然后抬起。只有在UI的响应速度跟得上用户快速的滑动屏幕的频率的时候,这个手势操作才是有意义的,就像用户在推动一个飞轮并想让它旋转起来。

然而,模拟一种飞轮的感觉也是不平凡的。要想得到一个正确的飞轮运转模型是需要大量的物理和数学验证的。庆幸的是,Android提供了帮助类来模拟它以及别的操作行为。Scroller 类是处理具有飞轮风格手势操作的基类。 开始实现滑动操作时,要调用fling()并传入初始的速率和在X轴和Y轴上滑动的最小值和最大值。根据这个速率的值,你就可以用该值并通过GestureDetector类来为你计算。

12345
@Overridepublic boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {   mScroller.fling(currentX, currentY, velocityX / SCALE, velocityY / SCALE, minX, minY, maxX, maxY);   postInvalidate();}

提示: 尽管通过GestureDetector类计算出的速率在理论上是精确的,但是许多开发人员都感到用这个值会让滑动的动画太快。常用的办法就是将X和Y方向的速率除以4到8之间的某一个值。

调用fling()方法会建立滑动操作的物理模型。接下来,你需要适时的调用 Scroller.computeScrollOffset() 方法来更新Scroller类的数据。computeScrollOffset() 方法通过读取当前的时间并用物理模型计算当前的X坐标和Y坐标来更新Scroller对象的内部状态调用getCurrX() 和getCurrY()方法可以获取最新的X坐标和Y坐标。 大部分的视图通过直接调用scrollTo()方法改变Scroller对象的X坐标和Y坐标的值。但是图表是有一点不一样的,它用当前Y轴的坐标来设定图表的张开角度。

1234
if (mScroller.isFinished()) {    mScroller.computeScrollOffset();    setPieRotation(mScroller.getCurrY());}

Scroller类能为你计算出滚动轴的位置,但是它不能自动的为你的视图提供它们的坐标位置。你必须确保你获得并应用的坐标在正常情况下足以能让滚动动画看起来流畅。这里有两种方法可以实现:

    • 为了能重绘视图,在调用fling()之后要调用postInvalidate()。这种技术需要你每次在补偿滚动轴的变化时要在onDraw()方法中计算滚动轴的变化并调用postInvalidate()方法。
    • 建立一个ValueAnimator来动态模拟滑动动作持续的状态,然后调用addUpdateListener()来添加一个监听者进行动画的更新。

图表用的就是第二种方法。这种方法在创建的时候略微会显得更加复杂,但是它工作起来会跟动画系统配合得更密切而且不需要可能不必要的无效视图。它的缺憾就是在低于API11的版本上ValueAnimator是不可用的,所以这种方法是不能用在低于Android3.0系统的设备上的。

提示:ValueAnimator虽然在低于API11的版本不可用,但是你仍然可以在你的应用程序中使用这个类并标上API版本过低。在程序运行的时候你必须确保检查了当前的API版本,如果当前的API版本低于11,在视图动画系统中就要忽略这些调用。

 1 2 3 4 5 6 7 8 91011121314
mScroller = new Scroller(getContext(), null, true);mScrollAnimator = ValueAnimator.ofFloat(0,1);mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {           @Override           public void onAnimationUpdate(ValueAnimator valueAnimator) {               if(mScroller.isFinished()){                   mScroller.computeScrollOffset();setPieRotation(mScroller.getCurrY());               } else {                   mScrollAnimator.cancel();                   onScrollFinished();               }           }       });

让你的界面流畅地转换


用户期望现代的UI能流畅的在状态之间转换。UI元素的淡入淡出会取代直接的出现和消失。各种操作都会按照自然的方式开始和结束而不是突然的启动和停止。在Android3.0系统中引入的property animation framework框架,会让流畅的转换变得简单。 在使用动画系统时,每一个视图属性的改变都将会影响到视图的外观,所以不要直接去改变视图的属性。取而代之的是,你可以用ValueAnimator来实现你对视图属性的改变。在下面的例子中,修改当前在图表中选定的饼图饼片会导致整个图表旋转,以便让你选择的点集中在选定的切片上。ValueAnimator 会在一个短暂的时间内实现旋转的改变,而不是突然地改变旋转的状态。

1234
mAutoCenterAnimator = ObjectAnimator.ofInt(PieChart.this,"PieRotation",0); mAutoCenterAnimator.setIntValues(targetAngle);mAutoCenterAnimator.setDuration(AUTOCENTER_ANIM_DURATION);mAutoCenterAnimator.start();

如果你想改变基本的View 中的一个属性值,不如去改变视图动画,因为视图中有一个嵌入的ViewPropertyAnimator类,它对同步的多属性的动画做了优化。例如:

1
animate().rotation(targetAngle).setDuration(ANIM_DURATION).start();
原创粉丝点击