Android 动画基础知识学习
标签: Android 动画基础知识学习 JavaScript博客 51CTO博客
2023-07-13 18:24:39 100浏览
1.Android中的三种动画
- View动画
通过场景里的对象不断做图像变换(平移,缩放,旋转,透明度)从而产生动画效果,是一种渐近式动画,并支持自定义。
- 帧动画
帧动画其实也属于View动画。通过顺序播放一系列图像从而产生动画效果,可以简单理解为图片切换动画效果,但图片过多过大会导致OOM
- 属性动画
属相动画通过动态地改变对象的属性从而达到动画效果。
重点在于属性动画的学习
2.View动画
View
动画的作用对象是View
。支持四种典型动画效果:
- 平移动画
TranslateAnimation
- 缩放动画
ScaleAnimation
- 旋转动画
RotateAnimation
- 透明度动画
AlphaAnimation
对于View动画,建议采用xml来定义动画,这样可读性更好
View
动画的四种变换
名称 |
标签 |
子类 |
效果 |
平移动画 |
|
|
移动 |
缩放动画 |
|
|
放大或缩小 |
旋转动画 |
|
|
旋转 |
透明度动画 |
|
|
改变 |
Animation
属性:
xml属性 |
jav代码 |
作用 |
|
|
是否在壁纸上运行 |
|
|
动画的持续时间 |
|
|
动画结束后是否停留在结束位置 |
|
|
动画结束时是否还原开始位置 |
|
|
同上,与fillBefore相同 |
|
|
设置插值器 |
|
|
重复次数 |
|
|
有两种重复类型, |
|
|
开启动画 |
|
|
表示被设置动画的内容运行时在Z轴上的位置( |
View
动画既可以是单个动画,也可以是一些列动画组成。
<set>
标签标示动画集合,对应于AnimationSet
类,可以包含若干动画,也可以嵌套其他的动画集合。
android:interpolator
- 动画集合所采用的的插值器,插值器影响动画的速度,比如非匀速动画就需要通过插值器来控制动画的播放过程。默认为
@android:anim/accelerate_decelerate_interpolator
- ,即加速加速插值器。
android:shareInterpolator
- 集合中的动画是否和集合共享同一个插值器。如果集合不指定插值器,子动画就需要单独指定所需的插值器或者使用默认值。
2.1TranslateAnimation平移动画
平移动画
可以简单实现抖动效果,转成gif
掉帧有点严重,没有抖起来在res
下创建anim
文件夹,文件名translate_animation.xml
xml
文件代码:
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="false"
android:duration="100"
android:repeatMode="restart"
>
<translate
android:repeatCount="2"
android:fromXDelta="-10"
android:fromYDelta="-5"
android:toXDelta="10"
android:toYDelta="5" />
</set>
java
代码:
Button bt = (Button) view.findViewById(R.id.bt_translate_fragment_translate);
ImageView iv = (ImageView)view.findViewById(R.id.iv_translate_fragment_translate);
//初始化动画
Animation animation = AnimationUtils.loadAnimation(context, R.anim.translate_animation);
//点击按钮开始动画
bt.setOnClickListener((v) -> iv.startAnimation(animation));
Button bt = (Button) view.findViewById(R.id.bt_translate_fragment_translate);
ImageView iv = (ImageView)view.findViewById(R.id.iv_translate_fragment_translate);
//初始化动画
Animation animation = AnimationUtils.loadAnimation(context, R.anim.translate_animation);
//点击按钮开始动画
bt.setOnClickListener((v) -> iv.startAnimation(animation));
android:fromXDelta
- x的起始坐标值,可以为数值、百分数、百分数
p
- 。以
View
- 的左上角为坐标系原点。负为左,正为右。
1.0 坐标图
- 数值: 10表示以当前View左上角坐标加10px为初始点
- 百分数: 50%表示以当前View的左上角加上当前View宽高的50%做为初始点
- 百分数
p
- : 50%p表示以当前View的左上角加上父控件宽高的50%做为初始点
android:toXDelta
- x的结束坐标值
android:fromYDelta
- y的起始坐标值。负为上,正为下
android:toYDelta
- y的结束坐标值
需要注意的是,TranslateAnimation动画并不会改变View的位置布局属性。
例如,利用TranslateAnimation
把一个Button
改变了,点击移动后的Button
是无效的,而点击Button
移动前的原始空白位置会响应Button
的点击事件。
2.2ScaleAnimation缩放动画
缩放动画
xml
代码:
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:fillAfter="false"
android:repeatMode="reverse">
<scale
android:fromXScale="0.5"
android:fromYScale="0.5"
android:pivotX="-100"
android:pivotY="-100"
android:repeatCount="2"
android:toXScale="1"
android:toYScale="1" />
</set>
android:fromXScale
android:fromYScale
android:toXScale
android:toYScale
android:pivotX
- 缩放的轴点的x轴的坐标。轴点为
View
- 的左上角
android:pivotY
默认情况下轴点为View的中心点
感觉书上这里和我实际测试有些出入,我感觉默认是View
的左上角。不晓得是我哪里搞错了,希望可以指出。感觉坐标系就是自己上面画的那个1.0坐标图
。
2.2RotateAnimation旋转动画
旋转动画
xml
代码:
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:repeatMode="reverse">
<rotate
android:fromDegrees="0"
android:pivotX="235"
android:pivotY="150"
android:repeatCount="2"
android:toDegrees="360" />
</set>
android:fromDegrees
android:toDegrees
android:pivotX
android:pivotY
2.4AlphaAnimation透明度动画
透明度动画
xml
代码:
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:repeatMode="reverse">
<alpha
android:fromAlpha="1.0"
android:repeatCount="2"
android:toAlpha="0.1" />
</set>
android:fromAlpha
- 透明度的起始值,
1.0
- 代表最不透明,值越小越透明
android:toAlpha
3. 帧动画
帧动画是顺序播放一组预先定义好的图片,类似播放电影。需要用到AnimationDrawable
这个类。随便百度的吾王,一点没有表现出吾王美如画
。
帧动画
帧动画使用步骤:
- 先在
drawable
- 文件下,定义一个
animation-list
- 文件,文件名字
frames_animation.xml
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item
android:drawable="@drawable/a"
android:duration="500" />
<item
android:drawable="@drawable/b"
android:duration="500" />
<item
android:drawable="@drawable/c"
android:duration="500" />
</animation-list>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item
android:drawable="@drawable/a"
android:duration="500" />
<item
android:drawable="@drawable/b"
android:duration="500" />
<item
android:drawable="@drawable/c"
android:duration="500" />
</animation-list>
- 将
Drawable
- 作为
View
- 的背景播放
private void initView() {
ImageView iv = (ImageView) findViewById(R.id.iv_frames_animation_activity);
Button bt_start= (Button) findViewById(R.id.bt_start_frames_animation_activity);
Button bt_stop= (Button) findViewById(R.id.bt_stop_frames_animation_activity);
iv.setBackgroundResource(R.drawable.frames_animation);
AnimationDrawable animation = (AnimationDrawable) iv.getBackground();
bt_start.setOnClickListener((v)-> animation.start());
bt_stop.setOnClickListener((v)->animation.stop());
}
private void initView() {
ImageView iv = (ImageView) findViewById(R.id.iv_frames_animation_activity);
Button bt_start= (Button) findViewById(R.id.bt_start_frames_animation_activity);
Button bt_stop= (Button) findViewById(R.id.bt_stop_frames_animation_activity);
iv.setBackgroundResource(R.drawable.frames_animation);
AnimationDrawable animation = (AnimationDrawable) iv.getBackground();
bt_start.setOnClickListener((v)-> animation.start());
bt_stop.setOnClickListener((v)->animation.stop());
}
帧动画使用很简单,但很容易出现OOM。尽量避免使用较大较多的图片。
4.View动画的特殊使用场景
View
动画除了四种基本使用场景外,还可以在ViewGroup
中控制子元素的出场效果,在Activity
中可以实现不同Activity
之间的切换效果。
4.1 LayoutAnimation简单介绍
LayoutAnimation作用于ViewGroup,为ViewGroup指定一个动画,这样当它的子元素出场时,都会具有这种动画效果。这种效果常常用于ListView上。
挖坑:RecyclerView
子item
的动画效果如何实现?
LayoutAnimation动画
使用步骤:
1.指定子View
的动画
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:repeatMode="reverse">
<rotate
android:fromDegrees="0"
android:pivotX="235"
android:pivotY="150"
android:repeatCount="2"
android:toDegrees="360" />
</set>
用的是2.2
旋转的动画
2. 定义LayoutAnimation
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:animation="@anim/rotate_animation"
android:animationOrder="reverse"
android:delay="1" />
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:animation="@anim/rotate_animation"
android:animationOrder="reverse"
android:delay="1" />
android:animation
指定子元素入场显示的动画
android:animationOrder
子元素动画的顺序。有: nomal,reverse,random
nomal 顺序显示,排在前面的子元素先显示动画;
reverse 逆序显示,排在后面的子元素先显示动画;
random
android:delay
子元素开始动画的时间延迟。比如子元素的入场动画周期为300ms,0.5就表示每个子元素都需要延迟150ms。第一个进来延迟150ms,播放入场动画,第二个子元素延迟300ms播放入场动画。依次类推。
3.ViewGroup
使用LayoutAniimation
采用布局文件的形式,指定android:layoutAnimation
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:layoutAnimation="@anim/layout_anim"
android:orientation="vertical"
>
<ImageView style="@style/img" />
<ImageView style="@style/img" />
<ImageView style="@style/img" />
</LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:layoutAnimation="@anim/layout_anim"
android:orientation="vertical"
>
<ImageView style="@style/img" />
<ImageView style="@style/img" />
<ImageView style="@style/img" />
</LinearLayout>
也可以通过代码来实现:
Animation animation= AnimationUtils.loadAnimation(context,R.anim.resId);
LayoutAnimationController controller = new LayoutAnimationController(animation);
controller.setDelay(1);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
目标ViewGroup.setLayoutAnimation(controller);
Animation animation= AnimationUtils.loadAnimation(context,R.anim.resId);
LayoutAnimationController controller = new LayoutAnimationController(animation);
controller.setDelay(1);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
目标ViewGroup.setLayoutAnimation(controller);
4.2Activity的切换效果
使用overidePendingTransition(int enterAnim, int exitAnim)
可以改变Activity
的的默认切换效果。这个方法 必须在startActivity()
或者finish()
之后才有效果。enterAnim
Activity
- 被打开时所需的动画资源
id
exitAnim
Activity
- 被暂停时所需的动画资源
id
启动一个Activity
时:
Intent intent = new Intent(MainActivity.this, activity);
startActivity(intent);
overridePendingTransition(R.anim.enter_anim,R.anim.exit_anim);
Intent intent = new Intent(MainActivity.this, activity);
startActivity(intent);
overridePendingTransition(R.anim.enter_anim,R.anim.exit_anim);
退出一个Activity
时:
@Override
public void finish() {
super.finish();
overridePendingTransition(R.anim.enter_anim,R.anim.exit_anim);
}
@Override
public void finish() {
super.finish();
overridePendingTransition(R.anim.enter_anim,R.anim.exit_anim);
}
1.属性动画
属性动画可以对任意对象的属性进行动画不仅仅是View
,动画默认时间间隔是300ms
,默认帧率是100ms/帧
。
作用:在一个时间间隔内完成对一个对象从属性值到另一个属性值的改变。
三个常用类:ValueAnimator,ObjectAnimator,AnimatorSet
属性动画
Java代码
private void initView() {
Button bt = (Button) findViewById(R.id.bt_object_animation_activity);
ImageView iv = (ImageView) findViewById(R.id.iv_object_animation_activity);
//改变背景属性
ValueAnimator colorAnim = ObjectAnimator.ofInt(iv, "backgroundColor", Color.parseColor("#FF4081"), Color.CYAN);
colorAnim.setRepeatCount(2);
colorAnim.setRepeatMode(ObjectAnimator.REVERSE);
colorAnim.setDuration(1000);
colorAnim.setEvaluator(new ArgbEvaluator());//估值器
//动画集合
AnimatorSet set = new AnimatorSet();
set.playTogether(
ObjectAnimator.ofFloat(iv, "rotationX", 0, 360),//绕x轴旋转360度
ObjectAnimator.ofFloat(iv, "rotation", 0, -90),//逆时针旋转90度
ObjectAnimator.ofFloat(iv, "translationX", 0, 90),//右移
ObjectAnimator.ofFloat(iv, "scaleY", 1, 0.5f),//y轴缩放到一半
ObjectAnimator.ofFloat(iv, "alpha", 1, 0.25f, 1)//透明度变换
);
//延迟一秒开始
set.setStartDelay(1000);
bt.setOnClickListener((v) -> {
//改变属性 位置 向下移动iv高的二分之一
ObjectAnimator.ofFloat(iv, "translationY", iv.getHeight() / 2).start();
//背景属性改变开始
colorAnim.start();
//集合动画
set.setDuration(3000).start();
});
}
private void initView() {
Button bt = (Button) findViewById(R.id.bt_object_animation_activity);
ImageView iv = (ImageView) findViewById(R.id.iv_object_animation_activity);
//改变背景属性
ValueAnimator colorAnim = ObjectAnimator.ofInt(iv, "backgroundColor", Color.parseColor("#FF4081"), Color.CYAN);
colorAnim.setRepeatCount(2);
colorAnim.setRepeatMode(ObjectAnimator.REVERSE);
colorAnim.setDuration(1000);
colorAnim.setEvaluator(new ArgbEvaluator());//估值器
//动画集合
AnimatorSet set = new AnimatorSet();
set.playTogether(
ObjectAnimator.ofFloat(iv, "rotationX", 0, 360),//绕x轴旋转360度
ObjectAnimator.ofFloat(iv, "rotation", 0, -90),//逆时针旋转90度
ObjectAnimator.ofFloat(iv, "translationX", 0, 90),//右移
ObjectAnimator.ofFloat(iv, "scaleY", 1, 0.5f),//y轴缩放到一半
ObjectAnimator.ofFloat(iv, "alpha", 1, 0.25f, 1)//透明度变换
);
//延迟一秒开始
set.setStartDelay(1000);
bt.setOnClickListener((v) -> {
//改变属性 位置 向下移动iv高的二分之一
ObjectAnimator.ofFloat(iv, "translationY", iv.getHeight() / 2).start();
//背景属性改变开始
colorAnim.start();
//集合动画
set.setDuration(3000).start();
});
}
轴点默认为View
的中心点。常用的propertyName
:rotationX
rotationY
rotation
translationX
translationY
scaleX
scaleY
alpha
width
height
1.2 常用方法介绍
1.2.1 ObjectAnimator类
ObjectAnimator.ofFloat(Object target, String propertyName, float... values)
Constructs and returns an ObjectAnimator that animates between float values.
- 返回一个根据方法内的具体的
values
- 创建的
ObjectAnimator
- 对象
Object target
- ,目标控件
View
- 的对象
String propertyName
- ,属性的名字
float... values
ofPropertyValuesHolder(Object target, PropertyValuesHolder... values)
Constructs and returns an ObjectAnimator that animates between the sets of values specified in PropertyValueHolder objects
- 返回一个由
PropertyValueHolder
- 对象创建的
ObjectAnimator
- 对象
public void byPropertyValuesHolder(ImageView iv) {
PropertyValuesHolder pvh_1 = PropertyValuesHolder.ofFloat("rotationX", 0, 360);
PropertyValuesHolder pvh_2 = PropertyValuesHolder.ofFloat("rotation", 0, -90);
PropertyValuesHolder pvh_3 = PropertyValuesHolder.ofFloat("alpha", 1, 0.25f, 1);
ObjectAnimator.ofPropertyValuesHolder(iv, pvh_1, pvh_2, pvh_3).setDuration(1000).start();
}
public void byPropertyValuesHolder(ImageView iv) {
PropertyValuesHolder pvh_1 = PropertyValuesHolder.ofFloat("rotationX", 0, 360);
PropertyValuesHolder pvh_2 = PropertyValuesHolder.ofFloat("rotation", 0, -90);
PropertyValuesHolder pvh_3 = PropertyValuesHolder.ofFloat("alpha", 1, 0.25f, 1);
ObjectAnimator.ofPropertyValuesHolder(iv, pvh_1, pvh_2, pvh_3).setDuration(1000).start();
}
ofInt(Object target, String propertyName, int... values)
Constructs and returns an ObjectAnimator that animates between int values.
- 返回一个由
int
- 值属性创建的
ObjectAnimator
- 对象
setTarget(Object target)
- 设置动画目标
View
1.2.2 ValueAnimator类
ValueAnimator
- 的
setEvaluator(new ArgbEvaluator())
- 设置估值器
addUpdateListener(ValueAnimator.AnimatorUpdateListener listener)
Adds a listener to the set of listeners that are sent update events through the life of an animation.
- 添加一个监听,可以用来在动画过程中改变属性
setRepeatCount(int num)
- 设置动画的循环次数,默认为0,-1为无限循环
setStartDelay(long startDelay)
- 设置动画开始的延迟时间
cancel()
- 取消一个正在进行的动画。取消前,动画进行到哪个状态,取消后,就保持在那个状态。
end()
- 结束动画。调用结束方法后,
View
- 会跳转到结束状态。如果动画设置了循环次数
setRepeatCount()
- 和重复模式
setRepeatMode(ObjectAnimator.REVERSE)
- ,结束状态就要根据具体设置分析。
1.2.3 AnimatorSet类
playTogether(Animator... items)
Sets up this AnimatorSet to play all of the supplied animations at the same time.
- 同时播放所有的
Animator
- 动画对象。
playSequentially(Animator... items)
Sets up this AnimatorSet to play each of the supplied animations when the previous animation ends.
- 顺序播放
Animator
- 动画对象
setInterpolator(TimeInterpolator interpolator)
Sets the TimeInterpolator for all current child animations of this AnimatorSet.
- 设置插值器
1.2.4 Animator类
直接子类:AnimatorSet
和 ValueAnimator
间接子类:ObjectAnimator
和 TimeAnimator
Animator
类的方法子类都可以直接使用。addListener(Animator.AnimatorListener listener)
Adds a listener to the set of listeners that are sent events through the life of an animation, such as start, repeat, and end.
- 为动画添加一个监听过程的接口。如果不想实现
AnimatorListener
- 接口中的所有方法也可以继承
AnimatorListenerAdapter
- 。
set.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
toast("动画开始");
}
@Override
public void onAnimationEnd(Animator animator) {
toast("动画结束");
}
@Override
public void onAnimationCancel(Animator animator) {
toast("动画取消");
}
@Override
public void onAnimationRepeat(Animator animator) {
toast("动画重建");
}
});
set.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
toast("动画开始");
}
@Override
public void onAnimationEnd(Animator animator) {
toast("动画结束");
}
@Override
public void onAnimationCancel(Animator animator) {
toast("动画取消");
}
@Override
public void onAnimationRepeat(Animator animator) {
toast("动画重建");
}
});
实现了接口中全部的方法。
继承AnimatorListenerAdapter
:
set.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
toast("动画结束");
}
});
set.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
toast("动画结束");
}
});
根据实际需求,实现不同的方法。
addPauseListener(Animator.AnimatorPauseListener listener)
Adds a pause listener to this animator
- 为动画增加暂停监听
2.插值器和估值器
TimeInterpolator
,时间插值器。用来根据时间流逝的百分比来计算出当前属性的值变化的百分比。
常用插值器:
名称 |
作用 |
|
线性插值器。匀速动画 |
|
加速减速插值器 |
|
匀减速插值器。动作越来越慢 |
|
回弹插值器。到达平移后,回弹 |
|
循环插值器。在两点间往还运动 |
|
路径插值器。根据单一方向定义的路径坐标运动 |
|
超越插值器。超出后,再返回来 |
|
预期插值器。先反向运动再根据指定的方向运动 |
都是Interpolator
的子类
TypeEvaluator
,类型估值算法(估值器)。用来根据当前属性变化改变的百分比来计算改变后的属性值。IntEvaluator
- ,针对整型属性
FloatEvaluator
- ,针对浮点型属性
ArgbEvaluator
- ,针对
Color
- 属性
2.1简单Demo
模拟小球下落
private void initView() {
Button bt = (Button) findViewById(R.id.bt_interpolator_activity);
ImageView iv = (ImageView) findViewById(R.id.iv_interpolator_activity);
LinearLayout root = (LinearLayout) findViewById(R.id.root_interpolator_activity);//根布局
AnimatorSet set = new AnimatorSet();
set.setInterpolator(new BounceInterpolator());
set.setDuration(3000);
//利用View的post方法拿到根布局的高度
root.post(() -> {
//计算下降高度
int height = root.getHeight() - iv.getHeight() - bt.getHeight();
//设置动画
set.play(ObjectAnimator.ofFloat(iv, "translationY", height));
});
bt.setOnClickListener(v ->set.start());
}
private void initView() {
Button bt = (Button) findViewById(R.id.bt_interpolator_activity);
ImageView iv = (ImageView) findViewById(R.id.iv_interpolator_activity);
LinearLayout root = (LinearLayout) findViewById(R.id.root_interpolator_activity);//根布局
AnimatorSet set = new AnimatorSet();
set.setInterpolator(new BounceInterpolator());
set.setDuration(3000);
//利用View的post方法拿到根布局的高度
root.post(() -> {
//计算下降高度
int height = root.getHeight() - iv.getHeight() - bt.getHeight();
//设置动画
set.play(ObjectAnimator.ofFloat(iv, "translationY", height));
});
bt.setOnClickListener(v ->set.start());
}
利用BounceInterpolator
可以很方便的做出模拟小球下落的动画。也可以根据需求进行自定义插值器。
2.2 对任意属性做动画
对Object
的属性abc
做动画,必须满足2个条件:object
- 必须提供
setAbc()
- 的方法。如果动画的时候没有传递初始值,还要提供
getAbc()
- 方法。因为系统要去
abc
- 的初始值。如果不满足,程序直接
Crash
object
- 的
setAbc
- 对属性
abc
- 所做的改变必须能够通过某种方法反映出来,比如
UI
- 改变之类的。这条不满足,动画无效但程序不会
Crash
2.2.1 改变Button的宽度
例如,想要利用属性对话来改变一个Button
的宽度。
private void initView() {
Button bt = (Button) findViewById(R.id.bt_button_activity);
bt.setOnClickListener((v) -> performAnimate(bt));
}
private void performAnimate(Button bt) {
ObjectAnimator.ofInt(bt, "width", 500).setDuration(1000).start();
}
private void initView() {
Button bt = (Button) findViewById(R.id.bt_button_activity);
bt.setOnClickListener((v) -> performAnimate(bt));
}
private void performAnimate(Button bt) {
ObjectAnimator.ofInt(bt, "width", 500).setDuration(1000).start();
}
实际测试,这段代码完全不起作用。
2.2.2不起作用的原因
Button
继承的TextView
/**
* Makes the TextView exactly this many pixels wide.
* You could do the same thing by specifying this number in the
* LayoutParams.
*
* @see #setMaxWidth(int)
* @see #setMinWidth(int)
* @see #getMinWidth()
* @see #getMaxWidth()
*
* @attr ref android.R.styleable#TextView_width
*/
@android.view.RemotableViewMethod
public void setWidth(int pixels) {
mMaxWidth = mMinWidth = pixels;
mMaxWidthMode = mMinWidthMode = PIXELS;
requestLayout();
invalidate();
}
/**
* Return the width of the your view.
*
* @return The width of your view, in pixels.
*/
@ViewDebug.ExportedProperty(category = "layout")
public final int getWidth() {
return mRight - mLeft;
}
/**
* Makes the TextView exactly this many pixels wide.
* You could do the same thing by specifying this number in the
* LayoutParams.
*
* @see #setMaxWidth(int)
* @see #setMinWidth(int)
* @see #getMinWidth()
* @see #getMaxWidth()
*
* @attr ref android.R.styleable#TextView_width
*/
@android.view.RemotableViewMethod
public void setWidth(int pixels) {
mMaxWidth = mMinWidth = pixels;
mMaxWidthMode = mMinWidthMode = PIXELS;
requestLayout();
invalidate();
}
/**
* Return the width of the your view.
*
* @return The width of your view, in pixels.
*/
@ViewDebug.ExportedProperty(category = "layout")
public final int getWidth() {
return mRight - mLeft;
}
getWidth()
方法View
的方法,是可以获取Button
高度的。setWidth()
方法TextView
和子类的专属方法。是用来设置TextView
的最大宽度和最小宽度的,并不是用来设置TextView
的宽度的方法。TextView
的宽度对应于XML
的android:layout_width
,setWidth
方法对应的是android:width
。也就是说:Button
的setWidth()
和getWidth()
对应的就不是一个属性。只满足的条件1,不满足条件2
2.2.3 解决办法
有三种解决办法:
- 如果有权限,给对象加上
get
- 和
set
- 方法
- 用一个类包装原始对象,间接提供
get
- 和
set
- 方法
- 采用
ValueAnimation
- ,监听动画过程,实现属性的改变
get
和set
方法往往拿不到权限。
利用包装类方法:
private void initView() {
Button bt = (Button) findViewById(R.id.bt_button_activity);
bt.setOnClickListener((v) -> performAnimate(bt,bt.getWidth()));
}
private void performAnimate(Button bt,int width) {
ButtonWrapper wrapper = new ButtonWrapper(bt);
wrapper.setWidth(width);
ObjectAnimator.ofInt(wrapper, "width", 500).setDuration(1000).start();
}
private static class ButtonWrapper {
private View target;
public ButtonWrapper(View target) {
this.target = target;
}
public int getWidth() {
return target.getLayoutParams().width;
}
public void setWidth(int width) {
target.getLayoutParams().width = width;
target.requestLayout();
}
}
private void initView() {
Button bt = (Button) findViewById(R.id.bt_button_activity);
bt.setOnClickListener((v) -> performAnimate(bt,bt.getWidth()));
}
private void performAnimate(Button bt,int width) {
ButtonWrapper wrapper = new ButtonWrapper(bt);
wrapper.setWidth(width);
ObjectAnimator.ofInt(wrapper, "width", 500).setDuration(1000).start();
}
private static class ButtonWrapper {
private View target;
public ButtonWrapper(View target) {
this.target = target;
}
public int getWidth() {
return target.getLayoutParams().width;
}
public void setWidth(int width) {
target.getLayoutParams().width = width;
target.requestLayout();
}
}
拿到Button
的宽度后,设置给ButtonWrapper
。这样动画开始后,Button
会从原始大小开始变化。
利用ValueAnimation
方法:
private void initView() {
Button bt = (Button) findViewById(R.id.bt_button_activity);
bt.setOnClickListener((v) -> performAnimate(bt,bt.getWidth(),500));
}
private void performAnimate(Button bt,int start, int end) {
ValueAnimator valueAnimator = ValueAnimator.ofInt(1,100);
//IntEvaluator对象,估值的时候使用
IntEvaluator intEvaluator = new IntEvaluator();
valueAnimator.addUpdateListener((animator -> {
//获取当前动画的进度值 , 整型, 1到100
int currentValue = (int) animator.getAnimatedValue();
//获取当前进度的占整个动画过程的比例,浮点型, 0到1
float fraction = animator.getAnimatedFraction();
//直接利用整型估值器,通过比例计算宽度,然后Button设置
bt.getLayoutParams().width = intEvaluator.evaluate(fraction,start,end);
bt.requestLayout();
}));
//开启动画
valueAnimator.setDuration(1000).start();
}
private void initView() {
Button bt = (Button) findViewById(R.id.bt_button_activity);
bt.setOnClickListener((v) -> performAnimate(bt,bt.getWidth(),500));
}
private void performAnimate(Button bt,int start, int end) {
ValueAnimator valueAnimator = ValueAnimator.ofInt(1,100);
//IntEvaluator对象,估值的时候使用
IntEvaluator intEvaluator = new IntEvaluator();
valueAnimator.addUpdateListener((animator -> {
//获取当前动画的进度值 , 整型, 1到100
int currentValue = (int) animator.getAnimatedValue();
//获取当前进度的占整个动画过程的比例,浮点型, 0到1
float fraction = animator.getAnimatedFraction();
//直接利用整型估值器,通过比例计算宽度,然后Button设置
bt.getLayoutParams().width = intEvaluator.evaluate(fraction,start,end);
bt.requestLayout();
}));
//开启动画
valueAnimator.setDuration(1000).start();
}
监控动画过程,利用IntEvaluator
估值器
3.最后
好博客就要一起分享哦!分享海报
此处可发布评论
评论(0)展开评论
展开评论