在安卓开发过程中,突然遇到一个需求:需要模仿newsela的更改文章难度刷新文章内容的动画。实现思路:截取手机当前显示的屏幕,然后先展示给用户,然后通过动画的形式慢慢的让截屏消失,展示截图背后的屏幕内容。
然后一直百度,希望能看到别人实现的效果,但是很失望,一直都没有找到正好符合需求的实现效果,在安卓开源的这么大好情景下,我觉得需要把动画的动态图发出来,希望大家一起和我一样,让安卓开发更快更强。
下面是我学制作gif图的过程,手机下载一个“录屏大师”,然后把录制好的视频通过手机发到电脑,然后通过电脑在线mp4转gif图。
直接贴上实现代码:实现思路就是通过Imageview设置负的buttomMargin来达到慢慢不可见的效果,同时慢慢的改变外层控件的高度来达到不改变显示的布局文件。
布局文件
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<RelativeLayout
android:id="@+id/rl_screen_shot_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.AppCompatImageView
android:id="@+id/iv_screen_shot_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/timg"/>
</RelativeLayout>
<android.support.v7.widget.AppCompatButton
android:id="@+id/btn_start_animation"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_50"
android:text="动态改变图片大小"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"/>
</android.support.constraint.ConstraintLayout>
动画实现代码:
class ChangeBitmapHeightActivity : AppCompatActivity() {
private var mTopToButtom: Boolean = false
private lateinit var mShotScreenLayout: RelativeLayout
private lateinit var mShotScreenView: AppCompatImageView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_change_height)
mShotScreenLayout = findViewById(R.id.rl_screen_shot_layout)
mShotScreenView = findViewById(R.id.iv_screen_shot_image)
val phoneScreenShot = ScreenShotUtil.getPhoneScreenShot(this)
mShotScreenView.setImageBitmap(phoneScreenShot)
findViewById<View>(R.id.btn_start_animation).setOnClickListener {
//动态裁剪图片,然后重新设置给ImageView显示
if(mTopToButtom) {
mTopToButtom = false
refreshPictureButtomToTop(mShotScreenLayout.height,0)
}else{
mTopToButtom = true
refreshPictureTopToButtom(0, mShotScreenLayout.height)
}
}
}
private fun refreshPictureButtomToTop(startY: Int, endY: Int) {
val valueAnimator = ValueAnimator.ofInt(startY, endY)
valueAnimator.duration = 2000 * 6
valueAnimator.addUpdateListener { animation ->
val animatedValue = animation.animatedValue as Int
Log.e(">>>>>>>>>>>>>", "animatedValue:$animatedValue")
val layoutParams = ConstraintLayout.LayoutParams(-1, animatedValue)
mShotScreenLayout.layoutParams = layoutParams
}
valueAnimator.start()
val valueAnimatorTwo = ValueAnimator.ofInt(endY, startY)
valueAnimatorTwo.duration = 2000 * 6
valueAnimatorTwo.addUpdateListener { animation ->
val animatedValue = animation.animatedValue as Int
Log.e(">>>>>>>>>>>>>", "animatedValue:$animatedValue")
val imageViewParams = RelativeLayout.LayoutParams(-1, -1)
imageViewParams.bottomMargin = -animatedValue
mShotScreenView.layoutParams = imageViewParams
}
valueAnimatorTwo.start()
}
private fun refreshPictureTopToButtom(startY: Int, endY: Int) {
val valueAnimator = ValueAnimator.ofInt(startY, endY)
valueAnimator.duration = 2000 * 6
valueAnimator.addUpdateListener { animation ->
val animatedValue = animation.animatedValue as Int
Log.e(">>>>>>>>>>>>>", "animatedValue:$animatedValue")
val layoutParams = ConstraintLayout.LayoutParams(-1, -1)
layoutParams.topMargin = animatedValue
mShotScreenLayout.layoutParams = layoutParams
}
valueAnimator.start()
val valueAnimatorTwo = ValueAnimator.ofInt(startY, endY)
valueAnimatorTwo.duration = 2000 * 6
valueAnimatorTwo.addUpdateListener { animation ->
val animatedValue = animation.animatedValue as Int
Log.e(">>>>>>>>>>>>>", "animatedValue:$animatedValue")
val imageViewParams = RelativeLayout.LayoutParams(-1, -1)
imageViewParams.topMargin = -animatedValue
mShotScreenView.layoutParams = imageViewParams
}
valueAnimatorTwo.start()
}
}
截屏代码实现:
/**
* 截取屏幕的高度
* @return
*/
public static Bitmap getBitmapByView(View view) {
Bitmap bitmap = null;
if (view != null){
bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.RGB_565);
final Canvas canvas = new Canvas(bitmap);
view.draw(canvas);
}
return bitmap;
}
这里需要注意截屏的代码实现不能使用下面这两种:
/**
* 获取屏幕截屏
* @param activity
* @return
*/
public static Bitmap getPhoneScreenShot(Activity activity) {
if(activity != null) {
View view = activity.getWindow().getDecorView();
view.setDrawingCacheEnabled(true);
view.buildDrawingCache();
return view.getDrawingCache();
}else{
return null;
}
}
上面这种截屏获取方式只能获取一次手机截屏,下次获取到的截屏对象是同一个屏幕截图,图片不可变。
在安卓的动画中,我们知道有帧动画可以实现界面动画,比如星星一闪一闪的效果;平移动画可以让一个控件在屏幕的一个点移动到另一个点;旋转动画可以设置一个控件按某一个坐标点进行旋转;还有渐变动画可以让一个控件从透明状态到显示状态。到现在为止,我已经说了4种动画了,这四种动画我们可以知道是比较平常的动画效果的实现方式。当然我们也可以使用AnimationSet类设置多种动画的组合实现,但是前提是需要明确需求,动画的效果不能太过于复杂,否则我们是无法实现那么绚丽多彩的动画功能的,就算生搬硬套,实现后看起来也会很死板,无法再做过多的要求。
安卓3.0之后,我们迎来了安卓的属性动画,属性动画,顾名思义就是可以通过动态的设置控件的属性,从而来实现安卓的动态效果。这样我们会发现,安卓属性动画几乎可以实现前面我们说的所有动画的动画效果,而且实现代码并不复杂,只需要创建一个属性动画,然后设置一个你需要动态设置的控件属性范围,获取到属性动画的参数值设置给你需要实现动画效果的控件,然后开启动画,这样控件的动画效果就实现了。
ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
anim.setDuration(300);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float currentValue = (float) animation.getAnimatedValue();
Log.d("TAG", "cuurent value is " + currentValue);
}
});
anim.start();
属性动画可以设置动画的运行速度:
android:interpolator="@android:anim/accelerate_interpolator" 设置动画为加速动画(动画播放中越来越快)
android:interpolator="@android:anim/decelerate_interpolator" 设置动画为减速动画(动画播放中越来越慢)
android:interpolator="@android:anim/accelerate_decelerate_interpolator" 设置动画为先加速在减速(开始速度最快 逐渐减慢)
android:interpolator="@android:anim/anticipate_interpolator" 先反向执行一段,然后再加速反向回来(相当于我们弹簧,先反向压缩一小段,然后在加速弹出)
android:interpolator="@android:anim/anticipate_overshoot_interpolator" 同上先反向一段,然后加速反向回来,执行完毕自带回弹效果(更形象的弹簧效果)
android:interpolator="@android:anim/bounce_interpolator" 执行完毕之后会回弹跳跃几段(相当于我们高空掉下一颗皮球,到地面是会跳动几下)
android:interpolator="@android:anim/cycle_interpolator" 循环,动画循环一定次数,值的改变为一正弦函数:Math.sin(2* mCycles* Math.PI* input)
android:interpolator="@android:anim/linear_interpolator" 线性均匀改变
android:interpolator="@android:anim/overshoot_interpolator" 加速执行,结束之后回弹