view是Android中所有控件的基类,是一种界面层的抽象,除了view还有viewGroup,viewGroup内部包含了多个view,viewGroup也继承了view,这就意味view本身就可以是一个控件也可以是多个控件组成的一组控件,这种关系形成了view树的结构。
view的位置由它的4个顶点来决定,分别对应view的4个属性:top、left、right、bottom,top是相对于view的父view的左上角纵坐标,left是相对于父view左上角横坐标,同理right和bottom分别对应右下角横坐标和右下角纵坐标,这4个属性属于相对坐标。很容易得到view的宽width和高height分别为:
width = right-left;
height = bottom-top;
可以通过这4个属get方法得到这4个属性值,left = getLeft();以此类推。从Android3.0开始,view额外增加,x,y,translationX,translationY,其中x y 是view左上角的坐标,而translationX translationY是view左上角相对于父容器的偏移量,这几个参数也是相对于父容器的坐标,translationX 和translationY的默认值是0,和其他的位置参数一样,view也为他们提供了get 和set 方法,换算关系
x = left+translationX y = right+translationY
MotionEvent ,手指接触屏幕的时候所产生的一些列事件,典型的有ACTION_DOWN手指刚接触屏幕,ACTION_MOVE手指移动,ACTION_UP手指放开,ACTION_CANNCEL惭怍取消等。通过MotionEvent对象可以得到点击事件发生的x和y坐标,系统提供了两组方法 getX/getY 和 getRawX/getRawY,区别在于,前者返回的是相对当前View的左上角x y坐标,后者返回的是相对手机屏幕左上角的x y坐标。
TouchSlop是系统所能识别的被认为滑动的最小距离,通过这个方式获取ViewConfiguration.get(getContext()).getScaledTouchSlop(),方法获取。
VelocityTracker,用于追踪手指在滑动过程中的速度,计算公式:速度 = (终点位置-起点位置)/ 时间段,分X轴和Y轴,使用方式,在onTouchEvent中
VelocityTracker tracker = VelocityTracker.obain();
tracker.addMoveEvent(event);
获取滑动的速度
tracker.computeCurrentVelocity(1000);//计算当前速度,其中1000是时间间隔
int xTracker = tracker.getXVelocity();//水平速度
int yTracker = tracker.getYVelocity();//垂直速度
不使用的时候,调用 tracker.clear();tracker.recycle();方法重置回收内存。
GestureDetector 手势检测类,用于辅助检测用户的点击,滑动,长按,双击等行为。
关于View的滑动
view的滑动可以有3种方式,第一种是通过view本身的scrollTo和scrollBy方法,第二种是通过动画,第三种是通过LayoutParams重新布局,第一种方式的时候有两个参数需要注意,scrollX和scrollY,可以通过getScrollX和getScrollY方法获取,分别是view左边缘到view内容左边缘的距离(当view内容左边缘在view左边缘的左边时为正)和view上边缘到view内容上边缘的距离(当view内容上边缘在view上边缘的上边时为正)。值得注意的是第一种方式只能改变view内容的位置,而不能改变View在布局中的位置。
用动画的方式可以使用view动画也能使用属性动画:ObjectAnimator.ofFloat(view,"translationX",0,100).setDuration(1000).start();值得注意的是动画的方式是对view的影像做操作,不会真正改变view的位置参数,如果希望动画完成后的状态保留,需要设置fiilAfter为true。动画过后控件的位置信息不会发生改变,也就是动画完成后看似控件移动到了目标位置,实际上位置信息还是原来,例如点击事件,点击目标位置没有作用,所以动画不能用于有交互的地方。
弹性滑动
实现弹性滑动方式很多,例如,Scroller,Handler#postDelay,Thread#sleep。
使用Scroller,
//滑动到指定的位置
public void smooth(int dealX,int dealyY){
int scrollX = getScrollX();
int deltaX = dealX-scrollX;
scroller.startScroll(scrollX, 0, deltaX, 0, 1000);
invalidate();
}
@Override
public void computeScroll() {
if(scroller.computeScrollOffset()){
scrollTo(scroller.getCurrX(), scroller.getCurrY());
postInvalidate();
}
}
有趣的是,Scroller本身并不能实现View的滑动,需要配合View的computeScroll方法才能完成弹性滑动的效果,它不断让view重绘,而每一次重绘滑动的起始时间有一个时间间隔,通过这个时间间隔Scroller就可以得到View当前的滑动位置。 知道了滑动位置就通过scrollTo来进行滑动。view每一次重绘都会导致view进行小幅度的滑动,达到弹性滑动效果。
通过动画
ObjectAnimator.ofFloat(view,"translationX",0,100).setDuration(1000).start();我们可以通过动画的特性来实现一些动画不能实现的效果。
private void scollerAnimation(){
final int startX = 0;
final int detlaX = 100;
ValueAnimator animator = ValueAnimator.ofInt(0,1).setDuration(1000);
animator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float fraction = animation.getAnimatedFraction();
View view = new View(getContext());
view.scrollTo(startX+(int)(detlaX+fraction),0 );//滑动计算的距离
}
});
}
我们可以在onAnimationUpdate中做我们想要做的操作。
使用延时策略
顾名思义,就是用延时策略来发送一系列的消息,可以用handler或者view的postDelay方法。
int mCount = 0;
final int MESSAGE_SCROLL_TO = 1;
final int FRAME_COUNT = 30;
final int DELAYED_TIME = 33;
final Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case MESSAGE_SCROLL_TO:
mCount++;
if (mCount <= FRAME_COUNT) {
float fraction = mCount / (float) FRAME_COUNT;
int scrollX = (int) (fraction + 100);
View view = new View(getContext());
view.scrollTo(scrollX, 0);
handler.sendEmptyMessageDelayed(MESSAGE_SCROLL_TO,
DELAYED_TIME);
}
break;
default:
break;
}
}
};