Android系统中有两种坐标系,分别为Android坐标系和View坐标系
1.Android坐标系
将屏幕左上角的顶点作为Android坐标系的原点,这个原点向右是X轴正方向,向下是Y 轴正方向。另外在触控事件中,使用getRawX()和getRawY()方法获得的坐标也是 Android坐标系的坐标。
2.View坐标系
View坐标系是相当于Android坐标系的存在,两者存在,更好的控制View
通过图,就能算出控件的宽和高
View自身的坐标
通过如下方法可以获得View到其父控件(ViewGroup)的距离。
• getTop():获取View自身顶边到其父布局顶边的距离。
• getLeft():获取View自身左边到其父布局左边的距离。
• getRight():获取View自身右边到其父布局左边的距离。
• getBottom():获取View自身底边到其父布局顶边的距离。
3.MotionEvent方法
我们对控件的各种事件,点击,移动,触摸都会触发onTouchEvent(MotionEvent event)这个方法,MotionEvent 提供了获取焦点坐标的各种方法:
• getX():获取点击事件距离控件左边的距离,即视图坐标。
• getY():获取点击事件距离控件顶边的距离,即视图坐标。
• getRawX():获取点击事件距离整个屏幕左边的距离,即绝对坐标。
• getRawY():获取点击事件距离整个屏幕顶边的距离,即绝对坐标。
1.控件手动移动
有三种方法重置位置
1)layout方法
2) offsetLeftAndRight(offerx); offsetTopAndBottom(offery);
3) LayoutParams方法重置
public class CustomView extends View {
private int lastx;
private int lasty;
public CustomView(Context context) {
super(context);
}
public CustomView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//获取手机触摸的坐标
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastx = x;
lasty = y;
break;
case MotionEvent.ACTION_MOVE:
//计算移动距离
int offerx = x - lastx;
int offery = y - lasty;
//调用layout方法重置位置
//1、第一种
// layout(getLeft()+offerx,getTop()+offery,getRight()+offerx,getBottom()+offery);
//2、第二种,也可以调取下面方法刷新位置
offsetLeftAndRight(offerx);
offsetTopAndBottom(offery);
//3、第三种 使用LayoutParams改变控件位置
// FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) getLayoutParams();
// ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
// layoutParams.leftMargin = getLeft() + offerx;
// layoutParams.topMargin = getTop() + offery;
// setLayoutParams(layoutParams);
break;
}
return true;
}
}
2.View的自动移动
1)采用View动画
可以采用View动画来移动,在res目录新建anim文件夹并创建translate.xml:
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true">
set>
调用
view_translate.animation = AnimationUtils.loadAnimation(this,R.anim.translate)
2)scrollTo与scollBy 和Scroller实现控件平移
scrollTo(x,y)表示移动到一个具体的坐标点,而scrollBy(dx,dy)则表示移动的增量为dx、dy,这个过程是瞬间完成了,如果配合Scroller,就有了过渡滑动的效果,和动画一样,创建自定义控件,创建方法初始化Scroller,重写computeScroll()方法,系统会在绘制View的时候在draw()方法中调用该方法
public class CustomView extends View {
private int lastx;
private int lasty;
private Scroller scroller;
public CustomView(Context context) {
super(context);
}
public CustomView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
scroller = new Scroller(context);
}
public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//获取手机触摸的坐标
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastx = x;
lasty = y;
break;
case MotionEvent.ACTION_MOVE:
//计算移动距离
int offerx = x - lastx;
int offery = y - lasty;
offsetLeftAndRight(offerx);
offsetTopAndBottom(offery);
break;
}
return true;
}
//系统会在绘制View的时候在draw()方法中调用该方法
@Override
public void computeScroll() {
super.computeScroll();
if (scroller.computeScrollOffset()){
((View)getParent()).scrollTo(scroller.getCurrX(),scroller.getCurrY());
invalidate();
}
}
public void smoothScrollTo(int destx,int desty){
int scrollx = getScrollX();
int deltax =destx - scrollx;
int scrolly = getScrollY();
int deltay =desty - scrolly;
scroller.startScroll(scrollx,scrolly,deltax,deltay,2000);
}
}
在布局中引用改控件,然后代码中执行移动方法,实现移动效果
customView.smoothScrollTo(-400,-200)
1.ObjectAnimator
ObjectAnimator 是属性动画最重要的类,内部的工作机制是通过寻找特定属性的get和set方法,然后通过方法不断地对值进行改变,从而实现动画效果的,有好多属性值,不同的值,执行不同的方法
• translationX和translationY:用来沿着X轴或者Y轴进行平移。
• rotation、rotationX、rotationY:用来围绕View的支点进行旋转。
• PrivotX和PrivotY:控制View对象的支点位置,围绕这个支点进行旋转和缩放变换处理。默认该支点 位置就是View对象的中心点。
• alpha:透明度,默认是1(不透明),0代表完全透明。
• scaleX scaleY:沿X Y轴缩放
//透明动画
var objectAnimator = ObjectAnimator.ofFloat(tv_animator,"alpha",1f,0f,1f)
objectAnimator.duration = 2000
objectAnimator.start()
//平移动画-X
var objectAnimator1 = ObjectAnimator.ofFloat(tv_animator,"translationX",200f)
objectAnimator1.duration = 2000
objectAnimator1.start()
2.ValueAnimator
ValueAnimator不提供任何动画效果,它更像一个数值发生器,用来产生有一定规律的数字,从而让调 用者控制动画的实现过程。通常情况下,在ValueAnimator的AnimatorUpdateListener中监听数值的变化,从 而完成动画的变换
var valueAnimator = ValueAnimator.ofFloat(0f,100f)
valueAnimator.setTarget(tv_animator1)
valueAnimator.duration = 1000
valueAnimator.start()
valueAnimator.addUpdateListener {
val float = it.getAnimatedValue()
Log.e("---->" ,""+float)
}
3.动画的监听
完整的动画具有start、Repeat、End、Cancel这4个过程
var objectAnimator2 = ObjectAnimator.ofFloat(btn_animator,"translationY",200f)
objectAnimator2.duration = 2000
objectAnimator2.addListener(object : Animator.AnimatorListener {
override fun onAnimationStart(animation: Animator) {
Log.e("---->","动画执行开始")
}
override fun onAnimationEnd(animation: Animator) {
//大部分情况下,我们都要监听的是这个,在动画结束后,执行什么操作
Log.e("---->","动画执行结束")
}
override fun onAnimationCancel(animation: Animator) {
Log.e("---->","动画执行取消")
}
override fun onAnimationRepeat(animation: Animator) {
}
})
objectAnimator2.start()
4.组合动画——AnimatorSet
Builder 类采用了建造者模式,每次调用方法时都返回 Builder 自身用于继续构建。 AnimatorSet.Builder中包括以下4个方法。
• after(Animator anim):将现有动画插入到传入的动画之后执行。
• after(long delay):将现有动画延迟指定毫秒后执行。
• before(Animator anim):将现有动画插入到传入的动画之前执行。
• with(Animator anim):将现有动画和传入的动画同时执行。
AnimatorSet正是通过这几种方法来控制动画播放顺序的。
val objectAnimator3 = ObjectAnimator.ofFloat(tv_animator2,"translationX",0f,200f,0f)
val objectAnimator4 = ObjectAnimator.ofFloat(tv_animator2,"scaleX",1.0f,2.0f,1.0f)
val objectAnimator5 = ObjectAnimator.ofFloat(tv_animator2,"rotationX",0f,90f,0f)
val animatorSet = AnimatorSet()
animatorSet.duration = 1000
//设定执行顺序
animatorSet.play(objectAnimator3).with(objectAnimator4).after(objectAnimator5)
animatorSet.start()
5.组合动画——PropertyValuesHolder
val propertyValuesHolder1 = PropertyValuesHolder.ofFloat("scaleX",1f,1.5f)
val propertyValuesHolder2 = PropertyValuesHolder.ofFloat("rotationX",1f,90f,0f)
val propertyValuesHolder3 = PropertyValuesHolder.ofFloat("alpha",1f,0.3f,1f)
val objectAnimator6 = ObjectAnimator.ofPropertyValuesHolder(tv_animator3,propertyValuesHolder1,propertyValuesHolder2,propertyValuesHolder3)
objectAnimator6.setDuration(1000).start()
6.在XML中使用属性动画
在res文件中新建animator文件,在里面新建一个 scale.xml
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:duration="3000"
android:propertyName="scaleX"
android:valueFrom="1.0"
android:valueTo="2.0"
android:valueType="floatType">
objectAnimator>
val animator = AnimatorInflater.loadAnimator(this,R.animator.scale)
animator.setTarget(tv_animator4)
animator.start()