Android进阶练习-自定义视图(3)


使视图可交互


     绘制UI界面只是创建自定义视图中的一部分工作,你还应该去响应用户的输入,并且让这种响应更贴近现实生活,比如现实世界中的一些物理现象,一些人们的行为习惯等等,

处理输入手势 


    像其他的UI框架一样,Android也提供了一个输入事件模型。用户的触摸动作被转化为了一些事件,并且Android会进行事件回调,你可以通过覆写回调方法来处理用户的触屏事件,响应用户进行交互。像触屏事件会回调  onTouchEvent(android.view.MotionEvent) 方法,你只需要覆写它来处理该事件
   @Override
   public boolean onTouchEvent(MotionEvent event) {
    return super.onTouchEvent(event);
   }
     触摸事件本身并不是特别的重要,现代触摸用户界面定义了以手势为主的交互动作,像轻敲、拉、推、抛和放大缩小手势,为了将原始触摸事件转化为手势,Android提供了 GestureDetector  类。构建 GestureDetector 时需要传递一个实现了 GestureDetector.OnGestureListener  接口的类的实例,如果你不想处理所有的手势,你可以通过继承 GestureDetector.SimpleOnGestureListener   类来代替实现 GestureDetector.OnGestureListener   接口  
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 ,那么Android系统会认为你想忽略手势识别剩余的操作,并且   GestureDetector.SimpleOnGestureListener 方法将得不到调用,当你确实想忽略整个手势时你可以返回 false 。一旦你实现了 GestureDetector.OnGestureListener  接口并且创建出来了一个实例,那么你就可以使用你的 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     

创建符合物理逻辑的动作


     手势是一种强大的方式来控制触屏设备,但是它们可能违反直觉和难以记住除非它们能够产生符合物理逻辑的结果。一个最好的例子是抛手势,当用户在手机屏幕上快速的滑动手指然后向上或向下抬高他们的手指,在UI上可以表现为在手指移动方向上快速滑动,然后减慢,就像用户在推一个飞轮并让它转动一样

     然而,模拟推飞轮的感觉并不是一件很简单的事,要让一个飞轮模型正确的工作,需要用到大量的物理和数学知识。幸运的是,Android提供了一些工具类来帮助我们模拟这个和其它的一些行为, Scroller 类是处理 flywheel-style   抛手势最基本的一个类

      调用   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   帮我们计算的速率值很准确,但许多的开发者感觉这个速率会让抛动画看起来很快,所以一般来说会把这个速率值缩小4到8倍

      调用 fling()  方法只是为抛手势建立起了一个物理模型,然后,我们应该每隔一个常规合理的时间来调用 Scroller.computeScrollOffset()  更新  Scroller   ,   computeScrollOffset() 方法更新   Scroller   对象的内部状态借助于在当前的时间去渲染它和使用物理模型去计算x轴和y轴的坐标,我们可以调用   getCurrX()   和 getCurrY() 来获取这些值   
if (!mScroller.isFinished()) {
    mScroller.computeScrollOffset();
    setPieRotation(mScroller.getCurrY());
}
     Scroller   为你计算出滚动的位置,但是它并不会自动的把这些位置信息应用到你的视图上,获得和应用新的坐标信息能够让你的滑动看起来顺畅平滑,但这项工作需要你手动来做,有两种方式来做到这点:

1、在调用 fling()  后调用 postInvalidate()  方法,这样可以迫使视图重绘,这项技术需要你在 onDraw()    方法中计算好滚动的偏移量和滚动偏移量每次改变后都要调用   postInvalidate()  方法
2、为抛手势设置一个 ValueAnimator  播放动画一直到手势结束,并且增加一个监听器,在监听器中调用 addUpdateListener()   来处理滚动动画      
    
     提供的例子中使用的是第二种方法,这项技术稍微复杂点,但是它看起来更贴近Android中的动画系统并且不要求潜在的不需要的无效视图,   ValueAnimator 在 API level 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,在不同的状态间转换平滑。UI元素的淡入淡出,让用户动作的开始和结束过渡的尽量平滑,在Android3.0以上版本,可以很简单的做到这些。

     使用Android的动画系统,即使是一个属性的改变也会影响到视图的显示效果,不要直接的去改变属性。可以使用   ValueAnimator 来代替这种改变
mAutoCenterAnimator = ObjectAnimator.ofInt(PieChart.this, "PieRotation", 0);
mAutoCenterAnimator.setIntValues(targetAngle);
mAutoCenterAnimator.setDuration(AUTOCENTER_ANIM_DURATION);
mAutoCenterAnimator.start(); 
     如果你想改变的是视图基础属性的值,制作动画就更简单了,因为视图提供了一个内置的 ViewPropertyAnimator 类 ,并且这个类同时优化了动画的多个属性
animate().rotation(targetAngle).setDuration(ANIM_DURATION).start();



 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值