【Interface&navigation】使视图交互(18)

绘制用户界面只是创建自定义视图的一部分。您还需要使视图以与您模仿的真实动作非常相似的方式响应用户输入。对象应始终以与真实对象相同的方式进行操作。例如,图像不应该立即跳出存在,并在其他地方重新出现,因为现实世界中的物体不会这样做。相反,图像应该从一个地方移动到另一个地方。

用户还会感觉到界面中的微妙行为或感受,并对模仿真实世界的微妙事物做出最佳反应。例如,当用户投掷UI对象时,他们应该在开始时感觉到摩擦,从而延迟运动,然后在最后感觉到运动超出投掷的动量。

本课程演示如何使用Android框架的功能将这些现实世界的行为添加到自定义视图中。

除了本课,您还可以在输入事件和 属性动画中找到其他相关信息 。

处理输入手势


和许多其他UI框架一样,Android支持输入事件模型。用户操作将转换为触发回调的事件,您可以覆盖回调以自定义应用程序响应用户的方式。Android系统中最常见的输入事件是触摸,触发onTouchEvent(android.view.MotionEvent)。重写此方法来处理事件:

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

触摸事件本身并不是特别有用。现代触摸UI定义了手势方面的交互,例如点击,拉动,推动,抛掷和缩放。为了将原始触摸事件转换为手势,Android提供了GestureDetector。

GestureDetector通过传入一个实现的类的实例来构造一个GestureDetector.OnGestureListener。如果您只想处理几个手势,则可以扩展GestureDetector.SimpleOnGestureListener而不是实现GestureDetector.OnGestureListener 接口。例如,这段代码创建了一个扩展GestureDetector.SimpleOnGestureListener和覆盖的类onDown(MotionEvent)。

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()消息开头 。如果false从中返回onDown(), GestureDetector.SimpleOnGestureListener系统会假定您要忽略其余的手势,而其他方法则 GestureDetector.OnGestureListener永远不会被调用。你唯一应该回来false的时候onDown() 是你真的想要忽略整个手势。一旦您实现GestureDetector.OnGestureListener 并创建了一个实例GestureDetector,您就可以使用它GestureDetector来解释您收到的触摸事件onTouchEvent()。

@Override
public boolean onTouchEvent(MotionEvent event) {
   boolean result = mDetector.onTouchEvent(event);
   if (!result) {
       if (event.getAction() == MotionEvent.ACTION_UP) {
           stopScrolling();
           result = true;
       }
   }
   return result;
}

当您传递onTouchEvent()一个触摸事件时,它不会将其识别为手势的一部分,它会返回false。然后,您可以运行自己的自定义手势检测代码。

创造物理上合理的运动


手势是控制触摸屏设备的强大方式,但除非它们产生物理上合理的结果,否则它们可能是违反直觉的,难以记忆。这方面的一个很好的例子就是挥动 手势,用户在屏幕上快速移动手指然后提起手指。如果用户界面通过在投掷方向上快速移动来响应,则该姿势是有意义的,然后放慢,就好像用户已经推动飞轮并将其设置为旋转。

但是,模拟飞轮的感觉并不是微不足道的。需要大量的物理和数学才能使飞轮模型正常工作。幸运的是,Android提供了辅助类来模拟这个和其他行为。该 Scroller班级是处理飞轮式抛动手 势的基础。

要开始投掷,请fling()使用起始速度以及投掷的最小和最大x和y值进行调用。对于速度值,您可以使用为您计算的值GestureDetector。

@Override
public 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拨打电话进行更新Scroller.computeScrollOffset()。通过读取当前时间并使用物理模型计算当时的x和y位置来computeScrollOffset()更新Scroller对象的内部状态。调用getCurrX()并getCurrY()检索这些值。

大多数视图直接传递Scroller对象的x和y位置 scrollTo()。PieChart示例稍有不同:它使用当前的滚动y位置来设置图表的旋转角度。

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

该Scroller级计算滚动位置给你,但它不会自动应用这些位置你的看法。您有责任确保经常获得并应用新坐标,以使滚动动画看起来流畅。有两种方法可以做到这一点:

调用postInvalidate()后调用 fling(),以强制重绘。这种技术要求您计算滚动偏移量onDraw() 并在postInvalidate()每次滚动偏移量更改时调用。
设置一个ValueAnimator动画的持续时间,并添加一个侦听器来处理动画更新通过调用addUpdateListener()。
PieChart示例使用第二种方法。这种技术的设置稍微复杂一些,但它与动画系统的关系更密切,不需要潜在的不必要的视图无效。缺点是ValueAnimator 在API级别11之前不可用,所以此技术不能用于运行Android版本低于3.0的设备。

注意:您可以ValueAnimator在针对较低API级别的应用程序中使用。您只需确保在运行时检查当前API级别,并在当前级别小于11时省略对视图动画系统的调用。

       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元素淡入淡出而不是出现和消失。动作开始和结束顺利而不是突然开始和停止。Android 3.0中引入的Android 属性动画框架使平滑过渡变得简单。

要使用动画系统,只要属性发生变化而影响视图外观,请不要直接更改属性。相反,使用ValueAnimator来进行更改。在以下示例中,修改PieChart中当前选定的饼图切片会导致整个图表旋转,以便选择指针位于所选切片的中心。ValueAnimator在几百毫秒的时间内改变旋转,而不是立即设置新的旋转值。

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

如果要更改的值是基本View属性之一,则更容易执行动画,因为视图具有ViewPropertyAnimator针对多个属性的同时动画进行优化的内置。例如:

animate().rotation(targetAngle).setDuration(ANIM_DURATION).start();

联系我

QQ:94297366
微信打赏:https://pan.baidu.com/s/1dSBXk3eFZu3mAMkw3xu9KQ

公众号推荐:

【Interface&navigation】使视图交互(18)

猜你喜欢

转载自blog.51cto.com/4789781/2136961