View的交互

https://developer.android.com/training/custom-views/making-interactive

绘制一个UI仅是自定义视图中的一部分。你还需要让你的视图可对用户输入有所反馈。

处理输入手势

像其他UI框架一样,安卓支持一个输入事件模型。用户动作被转换成会触发回调的事件,而且你可以重载这些回调事件来自定义如何反馈。最常用的输入事件是touch,它会触发onTouchEvent(android.view.MotionEvent)回调。重载这个方法来处理这个事件。

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

只有Touch事件并不是很有用。现代触摸UI定义了交互手势,比如tapping, pulling, pushing, flinging, and zooming。可以使用GestureDetector方法来转化纯粹的touch事件为手势。

通过传入一个GestureDetector.OnGestureListener的实例来构造一个GestureDetector。如果你只想处理一部分手势,你可以继承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()消息开始。如果onDown()方法返回false,正如GestureDetector.SimpleOnGestureListener所做,系统假设你想忽略其他手势,而GestureDetector.OnGestureListener的其他方法永远不会被调到。唯一一种onDown()返回false的情况是你真的想要忽略所有手势。一旦你实现GestureDetector.OnGestureListener并且创建GestureDetector的实例,你可以用GestureDetector来翻译onTouchEvent()中的touch事件。

@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()一个而该方法无法识别成已知手势的touch事件时,该方法返回false。随后你可以执行你的自定义手势识别代码 。

创建物理上合理的动作

手势是用来控制触屏设备的非常有效的方式,但它们可能会违反直觉,难以记忆,除非它们产生了物理上可信的结果。 一个很好的例子就是fling手势,用户在屏幕上快速移动手指然后提起它。 当UI的反馈是飞快地在fling的方向上快速滑动,随后滑动速度减慢时,该姿势是有合理的,就好像用户已经转动了轮子并让它随着惯性继续转。

然而,模拟轮子的感觉不是那么容易得。让转轮模型正常工作需要很多物理和数学知识。幸运的是,安卓提供helper类来模拟这些行为。Scroller类是用来处理滚轮类型的fling事件的基本。

要启动一个fling,要调用fling()方法,并设置starting velocity、最大值、最小值、x和y值。

对于velocity值,可以使用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计算出的velocity是物理上准确的,很多开发者感觉用这个值做fling动画太快了。推荐将改值除以4到8。

调用fling()启动了fling手势的物理模型。随后,你需要用定期调用Scroller.computeScrollOffset()来更新Scroller。computeScrollOffset()通过读取当前时间,并用物理模型计算当时的x和y位置更新Scroller的内部状态。调用getCurrX()和getCurrY()以得到这些值。

大部分视图将Scroller对象的x和y位置直接传给scrollTo()方法。PieChart例子有一个小小的不同:它用当前的scroll y position来设置图表的旋转角度。

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

Scroller类会自动计算滚动位置,但是它不自动将这些值设置到自定义视图上。你需要手动在自定义视图上设置这些值,并足够频繁地更新坐标,以让动画看起来顺滑。有两种方式可以达成:

  • 在调用 fling()之后调用postInvalidate(),让界面重绘。这样做的话你需要在onDraw() 中计算scroll offsets,并在每次scroll offset改变时调用postInvalidate()。
  • 设置一个ValueAnimator来处理长时间的动画,通过addUpdateListener()来处理动画的更新.

PieChart使用第二种方法。它的设置稍微有些复杂,但是它与动画系统的合作更紧密,而且不需要潜在的冗余视图重绘。却显示ValueAnimator 在API level 11之前不兼容。

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

让状态切换更流畅

用户预期UI可以在状态间流畅地切换。UI元素渐入渐出而不是突然出现或消失。动作顺滑地开始和结束,而不是突兀地。property animation framework(Android 3.0加入)可以轻易地实现顺滑转换。

要使用动画系统,无论什么时候一个影响视图外观的属性变化时,都不需要直接改变这个属性。使用ValueAnimator来进行这个操作. 下例中,更改当先算中的pie slice导致整个chart旋转,以使得选中指针居中。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();

---总结---
如果需要在自定义视图中处理手势:

  1. 在onTouchEvent中实现OnGestureListener,并让onDown方法返回true;
  2. 在onTouchEvent()
    回调中处理该手势。用GestureDetector翻译touch事件
  3. fling事件之后调用scroller的fling方法,使用GestureDetector计算出的velocity的1/4或1/8。
    之后要写代码改变scroller的offset,然后postInvalidate。或直接使用动画。
    4.使用ValueAnimator/ObjectAnimator来改变界面属性值,让显示更流畅。

更多:
Input Events
Property Animation.

你可能感兴趣的:(View的交互)