View的坐标系与Android的坐标系并不冲突,它们是共同存在的,它可以用来更好的控制View
1.View获取自身的宽高
算出View的宽和高的方法
int width = button.getRight() - button.getLeft(); int height = button.getBottom() - button.getTop();
View源码中的 getHight 方法和 getWidth 方法如下:
public final int getHeight(){
return mBottom - mTop;
}
public final int getWidth(){
return mRight - mLeft;
}
2.View自身的坐标
-
getTop() : 获取View顶部到父布局顶距离
-
getLeft() : 获取View左部到父布局左距离
-
getRight() : 获取View右部到父布局左距离
-
getBottom() : 获取View底部到父布局底距离
3.MotionEvent是为我们触摸点提供的方法
-
getX():点击事件距离控件左边的距离
-
getY():点击事件距离控件顶边的距离
-
getRawX():点击事件距离父布局左边的距离
-
getRawY():点击事件距离父布局顶边的距离
CustomView
package com.study.c331viewmove;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import androidx.annotation.Nullable;
public class CustomView extends View {
private int lastX;
private int lastY;
public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public CustomView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(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 offsetX = x - lastX;
int offsetY = y - lastY;
// 方法 1:调用 layout 方法来重新放置它的位置
// layout(getLeft() + offsetX, getTop() + offsetY,
// getRight() + offsetX, getBottom() + offsetY);
// 方法 2: offsetLeftAndRight() 和 offsetTopAndBottom()
// offsetLeftAndRight(offsetX);
// offsetTopAndBottom(offsetY);
// 方法3: LayoutParams
// (1)父控件是 LinearLayout,用 LinearLayout.LayoutParams
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) getLayoutParams();
// (2)如果父控件是RelativeLayout,用 RelativeLayout.LayoutParams
// RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams();
// (3)除了使用布局的 LayoutParams 外, 还可以使用 ViewGroup.MarginLayoutParams
// ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
// layoutParams.leftMargin = getLeft() + offsetX;
// layoutParams.topMargin = getTop() + offsetY;
// setLayoutParams(layoutParams);
// 方法4:scrollBy
((View) getParent()).scrollBy(-offsetX, -offsetY);
break;
}
return true;
}
}
ScrollerCustomView
package com.study.c331viewmove;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Scroller;
import androidx.annotation.Nullable;
public class ScrollerCustomView extends View {
private Scroller mScroller;
public ScrollerCustomView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
mScroller = new Scroller(context);
}
public ScrollerCustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public void computeScroll() {
super.computeScroll();
if (mScroller.computeScrollOffset()) {
((View) getParent()).scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
invalidate(); // 重绘
}
}
public void smoothScrollTo(int destX, int destY) {
int scrollX = getScrollX();
int delta = destX - scrollX;
mScroller.startScroll(scrollX, 0, delta, 0, 2000);
invalidate();// 重绘
}
}
activity_main.xml
translate.xml
MainActivity.java
package com.study.c331viewmove;
import androidx.appcompat.app.AppCompatActivity;
import android.animation.ObjectAnimator;
import android.os.Bundle;
import android.view.animation.AnimationUtils;
import com.study.c331viewmove.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding mBinding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(mBinding.getRoot());
// 设置动画
// 1.通过 xml 设置动画
// mBinding.svCustomview.setAnimation(AnimationUtils.loadAnimation(this, R.anim.translate));
// 2.通过 ObjectAnimator 设置动画
// ObjectAnimator.ofFloat(mBinding.svCustomview, "translationX",0 , 300).setDuration(1000).start();
// 3.通过 scrollBy设置位置。
// mBinding.customview.scrollBy(-50, -50);
// 4. ScrollerCustomView 通过 Scroller 设置动画
mBinding.scvCustomview.smoothScrollTo(-400, 0);
}
}
3.5 属性动画
由于 View 的动画发生后,其相应的位置依然在动画进行前的地方。所以谷歌推出了新的动画框架——AnimatorSet 和 ObjectAnimator。
1.ObjectAnimator
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "translationX", 200); animator.setDuration(300); animator.start();
属性动画的属性值:
-
translationX 和 translationY:用来沿着 X 轴和 Y 轴进行平移。
-
rotation、rotationX 和 rotationY:用来围绕 View 的支点进行旋转。
-
PrivotX 和 PrivotY:控制 View 对象的支点位置,围绕这个支点进行旋转和缩放变换处理。默认位置是 View 对象的中心位置。
-
alpha:透明度,默认值是1(不透明),0 代表完全透明。
-
x 和 y :描述 View 对象在其容器中的最终位置。
package com.study.c34animation;
import android.view.View;
public class MyView {
private View mTarget;
public MyView(View targer) {
super();
mTarget = targer;
}
public int getWidth() {
return mTarget.getLayoutParams().width;
}
public void setWidth(int width) {
mTarget.getLayoutParams().width = width;
mTarget.requestLayout();
}
}
MainActivity.java
package com.study.c34animation;
import androidx.appcompat.app.AppCompatActivity;
import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.graphics.Path;
import android.os.Bundle;
import android.util.Log;
import com.study.c34animation.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding mBinding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(mBinding.getRoot());
// 1.ObjectAnimator 实现变大显示
MyView myViewOfSize = new MyView(mBinding.btnSize);
ObjectAnimator.ofInt(myViewOfSize, "width", 500).setDuration(500).start();
// 2.ValueAnimator 实现值的变化
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 100);
valueAnimator.setTarget(mBinding.btnValue);
valueAnimator.setDuration(5000).start();
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
Float num = (Float) valueAnimator.getAnimatedValue();
mBinding.btnValue.setText(num.toString());
}
});
// 3.实现透明度变化,并添加监听
ObjectAnimator animator = ObjectAnimator.ofFloat(mBinding.btnAlpha, "alpha", 0, 1.5f);
animator.setDuration(5000).start();
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
}
@Override
public void onAnimationEnd(Animator animator) {
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});
// 大部分比较关心 onAnimationEnd 事件
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
}
});
// 4.组合动画
// X轴移动
ObjectAnimator animator1 = ObjectAnimator.ofFloat(mBinding.btnAnimatorSet, "translationX", 0.0f, 200.0f, 200.0f);
// Y轴移动
ObjectAnimator animator2 = ObjectAnimator.ofFloat(mBinding.btnAnimatorSet, "translationY", 0.0f, 200.0f, 200.0f);
// 围绕X 来回转90°
ObjectAnimator animator3 = ObjectAnimator.ofFloat(mBinding.btnAnimatorSet, "rotationX", 0.0f, 90.0f, 0.0f);
// 按x轴方向比例放大2倍
ObjectAnimator animator4 = ObjectAnimator.ofFloat(mBinding.btnAnimatorSet, "scaleX", 1.0f, 2.0f);
// 按Y轴方向比例放大2倍
ObjectAnimator animator5 = ObjectAnimator.ofFloat(mBinding.btnAnimatorSet, "scaleY", 1.0f, 2.0f);
AnimatorSet set = new AnimatorSet();
set.setDuration(1000);
// 现在的动画是 animator1,先播放 animator3 ,animator1 和 animator2 同时播放,之后在一起播放 animator5
set.play(animator1).with(animator2).after(animator3).before(animator4);
set.playTogether(animator4, animator5);// 也可以使用 playTogether 一起播放
set.start();
// 5.组合动画2 PropertyValuesHolder 的使用,只能做到多个动画一起执行
PropertyValuesHolder valuesHolder1 = PropertyValuesHolder.ofFloat("scaleX", 1.0f, 1.5f);
PropertyValuesHolder valuesHolder2 = PropertyValuesHolder.ofFloat("rotationX", 0.0f, 90.0f, 0.0f);
PropertyValuesHolder valuesHolder3 = PropertyValuesHolder.ofFloat("alpha", 1.0f, 0.3f, 1.0f);
ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(mBinding.btnPropertyValuesHolder,
valuesHolder1, valuesHolder2, valuesHolder3);
objectAnimator.setDuration(2000).start();
// 6.使用 xml objectAnimator 实现动画
Animator animatorXml = AnimatorInflater.loadAnimator(this, R.animator.scale);
animatorXml.setTarget(mBinding.btnXml);
animatorXml.start();
}
}
scale.xml
END



