一、什么是View
View是所有控件的基类,下到各种Button,TextView,ImageView,上到LinearLayout,RelateLayout,甚至你自定义的控件,都是继承View这个基类,所以说,View是代表着界面层的一个抽象控件。除了View,还有一种叫ViewGroup,也就是控件组。ViewGroup本身也继承了View,View可以理解为存放很多View的组合,例如,LinearLayout本身是一个View,它也是一个ViewGroup。ViewGroup里面有子View,这个子View也是一个ViewGroup。
二、android之View坐标系,获取坐标值,改变控件的位置
view的几个方法:
view获取自身宽高:getHeight(),getWidth()
高度=bottom-top
宽度=right - getLeft()
getTop:获取到的,是view自身的顶边到其父布局顶边的距离
getLeft:获取到的,是view自身的左边到其父布局左边的距离
getRight:获取到的,是view自身的右边到其父布局左边的距离
getBottom:获取到的,是view自身的底边到其父布局顶边的距离
motionEvent的方法:
getX():获取点击事件相对控件左边的x轴坐标,即点击事件距离控件左边的距离
getY():获取点击事件相对控件顶边的y轴坐标,即点击事件距离控件顶边的距离
getRawX():获取点击事件相对整个屏幕左边的x轴坐标,即点击事件距离整个屏幕左边的距离
getRawY():获取点击事件相对整个屏幕顶边的y轴坐标,即点击事件距离整个屏幕顶边的距离
三、能够实现View滑动的方法:
- 使用 scrollTo/scrollBy
- 使用动画
- 使用LayoutParams
- 使用Layout
1.使用scrollTo/scrollBy
public void scrollTo(int x, int y) {
if (mScrollX != x || mScrollY != y) {
int oldX = mScrollX;
int oldY = mScrollY;
mScrollX = x;
mScrollY = y;
invalidateParentCaches();
onScrollChanged(mScrollX, mScrollY, oldX, oldY);
if (!awakenScrollBars()) {
postInvalidateOnAnimation();
}
}
}
public void scrollBy(int x, int y) {
scrollTo(mScrollX + x, mScrollY + y);
}
2.使用动画
通过动画我们能够让一个View进行平移,而平移就是一种滑动。使用动画来移动View,主要是操作View的 translationX 和 translationY 属性,既可以采用传统的View动画,也可以采用属性动画,如果采用属性动画的话,为了能够兼容 3.0以下的版本,需要采用开源动画库 nineoldandroids (http://nineoldandroids.com/)
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true"
android:zAdjustment="normal">
<translate
android:duration="100"
android:fromXDelta="0"
android:fromYDelta="0"
android:interpolator="@android:anim/linear_interpolator"
android:toXDelta="100"
android:toYDelta="100"></translate>
</set>
3、改变布局参数
在这里我们将介绍第三种实现View滑动的方法,那就是改变布局参数,即改变 LayoutParams。这个就比较好理解了,比如我们想把一个 Button 向右平移100px,我们只需要将这个 Button 的 LayoutParams 里的 marginLeft 参数的值增加 100px 即可,是不是很简单呢?还有一种情形,为了达到移动 Button 的目的,我们还可以在 Button 的左边放置一个空的 View,这个空 View 的默认宽度为 0,当我们需要向右滑动 Button 时,只需要重新设置空 View 的宽度即可,当空 View 的宽度增大时(假设 Button 的父容器是水平方向的 LinearLayout),Button 就会被自动挤向右边,这样也间接的实现了 View 向右平移的效果。如何重新设置一个 View 的 LayoutParams 呢? 很简单, 如下所示:
scrollTo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) scrollTo.getLayoutParams();
layoutParams.leftMargin +=100;
// scrollTo.requestLayout();
scrollTo.setLayoutParams(layoutParams);
}
});
4、使用layout方法
在 View 进行绘制时,会调用 onLayout() 方法来设置显示的位置。同样我们可以通过修改 View 的 left、top、right、bottom 四个属性来控制 View 的坐标。
三、属性动画
属性动画的使用的主要方式是AnimatorSet和ObjectAnimator配合使用.ObjectAnimator控制一个对象和一个属性,多个ObjectAnimator组合到AnimatorSet可以实现丰富的动画效果.
1.ObjectAnimator单独使用
ObjectAnimator mobjectAnimator=ObjectAnimator.ofFloat(view,"translationX",200);
mobjectAnimator.setDuration(300);
mobjectAnimator.start();
除了设置时长以外,还可以设置插值器.其可以常用的直接使用的属性动画属性值有:
translationX,translationY//平移
rotation,rotationX,rotationX//旋转
PrivotX,PrivotY//支点
alpha//透明度
x,y//View最终位置
2.监听动画过程
mobjectAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
三、组合动画
并且通过以下方法插入新动画:
after(Animator anim)
after(long delay)//延迟指定毫秒后执行
with(Animator anim)
before(Animator anim)
ObjectAnimator Animator1 = ObjectAnimator.ofFloat(view, "translationX", 200);
ObjectAnimator Animator2 = ObjectAnimator.ofFloat(view, "ScaleX", 1.0f, 2.0f);
ObjectAnimator Animator3 = ObjectAnimator.ofFloat(view, "rotationX", 0.0f, 90.0f);
AnimatorSet set=new AnimatorSet();
set.setDuration(2000);
set.play(Animator1).with(Animator2).after(Animator3);
set.start();
四、从源码解析Scroller
1.Scroller的构造函数
/**
* Create a Scroller with the default duration and interpolator.
*/
public Scroller(Context context) {
this(context, null);
}
/**
* Create a Scroller with the specified interpolator. If the interpolator is
* null, the default (viscous) interpolator will be used. "Flywheel" behavior will
* be in effect for apps targeting Honeycomb or newer.
*/
public Scroller(Context context, Interpolator interpolator) {
this(context, interpolator,
context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB);
}
/**
* Create a Scroller with the specified interpolator. If the interpolator is
* null, the default (viscous) interpolator will be used. Specify whether or
* not to support progressive "flywheel" behavior in flinging.
*/
public Scroller(Context context, Interpolator interpolator, boolean flywheel) {
mFinished = true;
if (interpolator == null) {
mInterpolator = new ViscousFluidInterpolator();
} else {
mInterpolator = interpolator;
}
mPpi = context.getResources().getDisplayMetrics().density * 160.0f;
mDeceleration = computeDeceleration(ViewConfiguration.getScrollFriction());
mFlywheel = flywheel;
mPhysicalCoeff = computeDeceleration(0.84f); // look and feel tuning
}
Scroller有三个构造函数,通常情况我们都用第一种,第二种需要传进去一个差值器Interpolator ,如果不传则采用默认的差值器(viscous)。
2.Scroller的startScroll方法
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
mMode = SCROLL_MODE;
mFinished = false;
mDuration = duration;
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mStartX = startX;
mStartY = startY;
mFinalX = startX + dx;
mFinalY = startY + dy;
mDeltaX = dx;
mDeltaY = dy;
mDurationReciprocal = 1.0f / (float) mDuration;
}
在startScroll()方法中并没有调用类似开启滑动的方法,而是保存了传进来的各种参数:startX和startY表示滑动开始的起点,dx和dy表示滑动的距离,duration则表示滑动持续的时间。所以startScroll()方法只是用来做前期准备的并不能使View进行滑动。关键是我们在startScroll()方法后调用了 invalidate()方法,这个方法会导致View的重绘,而View的重绘会调用View的draw()方法,draw()方法又会调用View的computeScroll()方法,我们重写computeScroll()方法:
@Override
public void computeScroll() {
super.computeScroll();
if(mScroller.computeScrollOffset()){
((View) getParent()).scrollTo(mScroller.getCurrX(),mScroller.getCurrY())