内容可参看Android开发艺术探索一书。
1.View基础知识
-
View的位置可通过系统通过的API方法来获取View控件的位置参数。如上图所示,其中left、top是相对于父控件的距离,left = getLeft()、top=getTop()、right=getRight()、bottom=getBottom(), 而translationX、translationY是View控件的父控件相对于屏幕左上角的距离值。因此可以得到View控件相对于屏幕的距离:
x = left + translationX; y = top + translationY;
2.MotionEvent
典型的事件类型有:
ACTION_DOWN —— 手指刚接触屏幕;
ACTIONG_MOVE——手指在屏幕上移动;
ACTION_UP——手指在屏幕上松开的一瞬间。
通过两组方法可获得点击事件发生的x和y坐标。 getX/getY返回的是相对于当前View左上角的x和y坐标,getRawX/getRawY返回的是相对于手机屏幕左上角的x和y坐标。
3.View的滑动
方式:
(1)通过View本身提供的scrollTo/scrollBy方法来实现滑动(不影响内部元素的点击事件,它只能滑动View的内容,不能滑动View本身,适合对View内容的滑动);
(2)通过动画给View施加平移效果来实现滑动(View动画会影响View的点击事件,属性动画不影响,主要适用于没有交互的View和复杂的动画);
(3)通过改变View的LayoutParams使得View重新布局从而实现滑动(不影响点击事件,适用于有交互的View)。
- scroBy实现View的相对滑动,scrollTo实现View的绝对滑动。
bt_scroll.scrollTo(3,30); //是bt_scroll内部的元素进行滑动
- 使用动画
a.View动画,需要先在res/anim/ 下新建一个xml 文件,定义动画,同时在Activity中加载此动画布局,并开始动画
mAnimation=AnimationUtils.loadAnimation(getApplicationContext(),R.anim.scroll_anim);
bt_scroll.startAnimation(mAnimation); //bt_scroll执行该动画
也可通过java代码实现动画
//创建一个透明度渐变动画,在 2000ms 内,将 VIew 的透明度由 0.1f 变为 1.0f
AlphaAnimation alphaAnimation=new AlphaAnimation(0.1f,1.0f);
alphaAnimation.setDuration(2000);
alphaAnimation.setRepeatCount(4);
alphaAnimation.setRepeatMode(Animation.REVERSE);
view.startAnimation(alphaAnimation);
View动画是对View的影像做操作,它并不能改变View的位置参数,包括宽高,并且如果希望动画后的状态得以保证还必须将 fillAfterd的属性设置为true,否则动画完成后其动画效果会消失。
View动画并不能真正改变View的位置,因此像Button这样的view在移动后,点击当前的位置并不能响应点击事件,在原位置才能响应,解决办法:方式(1)使用属性动画不会遇到这种问题;方式(2)间接解决方案:在新位置预先创建一个和目标Button一模一样的Button,不仅外观一样而且onClick事件也一样,当目标Button完成后平移动画后就把目标Button隐藏,同时把预先创建的Button显示出来。
b.属性动画:如将一个View在100ms内从原始位置向右平移100像素
ObjectAnimator.ofFloat(bt_scroll,"translationX",0,100).setDuration(100).start();
安卓3.0以下无法使用动属性动画,这时可以使用动画兼容库nineoldandroids来实现动画,但本质仍是View动画。
- (3)改变布局参数
即改变LayoutParams。如:
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams)bt_scroll.getLayoutParams();
params.width += 100;
params.leftMargin += 100;
bt_scroll.requestLayout();
//或者bt_scroll.setLayoutParams(params);
4.View的弹性滑动
实现渐进式滑动,共同思想:将一次大的滑动分成若干次小的滑动,并在一个时间段内完成,方式有: Scroller、Handler#postDelayed、Thread#sleep。
(1)Scroller:
需要配合View的computeScroll方法才能完成弹性滑动的效果,它不断的让View重绘,而每一次重绘距滑动起始时间会有一个时间间隔,通过这个时间间隔就可以得出View当前的滑动位置,知道了滑动位置就可以通过scrollTo方法来完成View的滑动。
(2)通过动画:
如将内容从右往左滑动100像素,在动画的每一帧到来时获取动画完成的比例,然后在根据这个比例计算当前View所要滑动的距离。
final int startX = 0;
final int deltaX = 100;
final ValueAnimator animator = ValueAnimator.ofInt(0,1).setDuration(1000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float fraction = animator.getAnimatedFraction();
bt_scroll.scrollTo(startX + (int)(deltaX *fraction),0);
}
});
animator.start();
(3)使用延时策略:
核心思想: 通过发送一系列延时消息从而到达一种渐进式的效果
方法:Handler或View的postdelayed方法,可以通过它来延时发送一个消息,然后在消息中进行View的滑动;
线程的sleep方法,通过在while循环中不断的滑动View和sleep
如通过Handler的postDelayed方法:
private static final int MESSAGE_SCROLL_TO = 1; //标记位
private static final int FRAME_COUNT = 30; //计数
private static final int DELAYED_TIME = 33; //每次延迟33ms后执行移动,总执行时间=33*30 + 系统的消息调度时间 ,大约等于1s,不太精确。
private int mCount = 0;
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case MESSAGE_SCROLL_TO:{
mCount++;
if(mCount <= FRAME_COUNT){
float fraction = mCount / (float) FRAME_COUNT; //每次执行移动的比例
int scrollX = (int) (fraction * 100); //每次移动到的像素位置
bt_scroll.scrollTo(scrollX, 0);
mHandler.sendEmptyMessageDelayed(MESSAGE_SCROLL_TO,DELAYED_TIME);
}
break;
}
default:
break;
}
}
};