3.View体系和自定义View
3.2坐标系
3.2.1 Android 坐标系

3.2.2 View坐标系
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():点击事件距离父布局顶边的距离
3.3View的滑动
3.3.1 自定义View 实现移动效果
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
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<com.study.c331viewmove.CustomView
android:id="@+id/sv_customview"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_margin="50dp"
android:background="@android:color/holo_red_light" />
<com.study.c331viewmove.ScrollerCustomView
android:id="@+id/scv_customview"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_margin="50dp"
android:background="@android:color/holo_blue_light" />
</LinearLayout>
translate.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true">
<translate
android:duration="1000"
android:fromXDelta="0"
android:interpolator="@android:anim/accelerate_interpolator"
android:toXDelta="300">
</translate>
</set>
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 对象在其容器中的最终位置。
MyView
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
<?xml version="1.0" encoding="utf-8"?>
<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>
本文详细介绍了Android中的View坐标系,包括Android坐标系和View坐标系,并展示了如何获取View的宽高和坐标。同时,讲解了MotionEvent的方法,如getX()和getY()。接着,通过自定义View实现滑动效果,使用了layout()、offsetLeftAndRight()、offsetTopAndBottom()和scrollBy()方法。此外,文章还探讨了Scroller在滑动动画中的应用。最后,重点介绍了属性动画,包括ObjectAnimator的各种属性和使用方法,以及如何通过组合动画实现复杂的视图变换效果。
1530

被折叠的 条评论
为什么被折叠?



