Demo_ ZoomActivity 解读
16lz
2021-01-26
这个是developer.android.com的demo,初学android,做下笔记.
目的: 实现点击缩略图,动画放大为大图,点击大图,反向动画为缩略图.
思路: 最外层用FrameLayout,便于将加载大图的ImageView覆盖在缩略图之上,先将此imageView隐藏,动画时再显示.
layout
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="16dp"> <TextView style="?android:textAppearanceSmall" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/message_zoom_touch_expand" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:orientation="horizontal"> <!-- TouchHighlightImageButton是一个自定义ImageButton,点击时盖上一层半透明图层,以标识focused 和 pressed 状态--> <com.example.android.animationsdemo.TouchHighlightImageButton android:id="@+id/thumb_button_1" android:layout_width="100dp" android:layout_height="75dp" android:layout_marginRight="1dp" android:src="@drawable/thumb1" android:scaleType="centerCrop" android:contentDescription="@string/description_image_1" /> <com.example.android.animationsdemo.TouchHighlightImageButton android:id="@+id/thumb_button_2" android:layout_width="100dp" android:layout_height="75dp" android:src="@drawable/thumb2" android:scaleType="centerCrop" android:contentDescription="@string/description_image_2" /> </LinearLayout> </LinearLayout> <ImageView android:id="@+id/expanded_image" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="invisible" android:contentDescription="@string/description_zoom_touch_close" /></FrameLayout>
ZoomActivity
package com.example.android.animationsdemo;import android.animation.Animator;import android.animation.AnimatorListenerAdapter;import android.animation.AnimatorSet;import android.animation.ObjectAnimator;import android.content.Intent;import android.graphics.Point;import android.graphics.Rect;import android.os.Bundle;import android.support.v4.app.FragmentActivity;import android.support.v4.app.NavUtils;import android.util.Log;import android.view.MenuItem;import android.view.View;import android.view.animation.DecelerateInterpolator;import android.widget.ImageView;public class ZoomActivity extends FragmentActivity { /** * 存放属性动画对象,方便后面中途取消动画时用. */ private Animator mCurrentAnimator; /** * 动画过场时间 */ private int mShortAnimationDuration; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_zoom); //这里没什么好说的,thumb1View ,thumb2View 2个缩略图 final View thumb1View = findViewById(R.id.thumb_button_1); thumb1View.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { zoomImageFromThumb(thumb1View, R.drawable.image1); } }); final View thumb2View = findViewById(R.id.thumb_button_2); thumb2View.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { zoomImageFromThumb(thumb2View, R.drawable.image2); } }); mShortAnimationDuration = getResources().getInteger(android.R.integer.config_shortAnimTime); // mShortAnimationDuration = 2000; 可以改长一点观察动画的详细过程 } //无关,不解释 @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: NavUtils.navigateUpTo(this, new Intent(this, MainActivity.class)); return true; } return super.onOptionsItemSelected(item); } /* * @param thumbView 要放大的缩略图View. * @param imageResId 大图Id. */ private void zoomImageFromThumb(final View thumbView, int imageResId) { // 如果动画正在执行,立马中断它,重新开始 if (mCurrentAnimator != null) { mCurrentAnimator.cancel(); } //载入大图 final ImageView expandedImageView = (ImageView) findViewById(R.id.expanded_image); expandedImageView.setImageResource(imageResId); //重点开始: 计算动画的开始和结束边界,其实就是计算2个矩形 final Rect startBounds = new Rect(); final Rect finalBounds = new Rect(); final Point globalOffset = new Point(); // 获得缩略图的矩形,注意:使用的是绝对坐标,相对于全屏 thumbView.getGlobalVisibleRect(startBounds); //获得大图的(这里是放置大图的FrameLayout,一回事)的矩形,和偏移量 //所谓"偏移量",指绝对坐标和相对坐标之差,之于为什么要这么干,因为属性动画中用的全是相对坐标 findViewById(R.id.container).getGlobalVisibleRect(finalBounds, globalOffset); //用偏移量修正2个矩形 startBounds.offset(-globalOffset.x, -globalOffset.y); finalBounds.offset(-globalOffset.x, -globalOffset.y); // 1.计算高和宽的Scale, // 2.如果缩略图和大图的高宽比不一样,先在startBounds中修正它 // 因为动画过程中出现拉伸的状况就不美了,我没测试过 float startScale; if ((float) finalBounds.width() / finalBounds.height() > (float) startBounds.width() / startBounds.height()) { // Extend start bounds horizontally startScale = (float) startBounds.height() / finalBounds.height(); float startWidth = startScale * finalBounds.width(); float deltaWidth = (startWidth - startBounds.width()) / 2; startBounds.left -= deltaWidth; startBounds.right += deltaWidth; } else { // Extend start bounds vertically startScale = (float) startBounds.width() / finalBounds.width(); float startHeight = startScale * finalBounds.height(); float deltaHeight = (startHeight - startBounds.height()) / 2; startBounds.top -= deltaHeight; startBounds.bottom += deltaHeight; } // 准备开场,先将缩略图隐藏,接下来是大图的动画,跟它无关了. thumbView.setAlpha(0f); expandedImageView.setVisibility(View.VISIBLE); // 设置动画开始的SCALE_X 和SCALE_Y变化的左上角原点,如果不设置的话默认从View的center开始 expandedImageView.setPivotX(0f); expandedImageView.setPivotY(0f); // 到这就没啥好说的了 AnimatorSet set = new AnimatorSet(); set .play(ObjectAnimator.ofFloat(expandedImageView, View.X, startBounds.left, finalBounds.left)) .with(ObjectAnimator.ofFloat(expandedImageView, View.Y, startBounds.top, finalBounds.top)) .with(ObjectAnimator.ofFloat(expandedImageView, View.SCALE_X, startScale, 1f))//最后的Scale肯定是1 .with(ObjectAnimator.ofFloat(expandedImageView, View.SCALE_Y, startScale, 1f)); set.setDuration(mShortAnimationDuration); set.setInterpolator(new DecelerateInterpolator());//用了个加速度插值器 set.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { mCurrentAnimator = null; } @Override public void onAnimationCancel(Animator animation) { mCurrentAnimator = null; } }); set.start(); mCurrentAnimator = set; //放大后再点击,缩回原状 final float startScaleFinal = startScale; expandedImageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (mCurrentAnimator != null) { mCurrentAnimator.cancel(); } AnimatorSet set = new AnimatorSet(); set .play(ObjectAnimator.ofFloat(expandedImageView, View.X, startBounds.left)) .with(ObjectAnimator.ofFloat(expandedImageView, View.Y, startBounds.top)) .with(ObjectAnimator .ofFloat(expandedImageView, View.SCALE_X, startScaleFinal)) .with(ObjectAnimator .ofFloat(expandedImageView, View.SCALE_Y, startScaleFinal)); set.setDuration(mShortAnimationDuration); set.setInterpolator(new DecelerateInterpolator()); set.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { thumbView.setAlpha(1f); expandedImageView.setVisibility(View.GONE); mCurrentAnimator = null; } @Override public void onAnimationCancel(Animator animation) { thumbView.setAlpha(1f); expandedImageView.setVisibility(View.GONE); mCurrentAnimator = null; } }); set.start(); mCurrentAnimator = set; } }); }}
主要知识点:
1. 绝对坐标及相对坐标的获取和转换,getGlobalVisibleRect(Rect r, Point offset);
2. 属性动画使用的一些注意点,比如SCALE_X 和SCALE_Y变化原点的设置等.
附上一个效果图
更多相关文章
- android 通过主题设置activity统一切换动画
- android显示大图片的一些技巧
- Android入门笔记 - 界面开发 - Animation
- Android(安卓)animation - 基础
- Android(安卓)环形布局: FloatingActionButton + ConstraintLayou
- MTK Android(安卓)P 开机无动画,无铃声问题和开机铃声不同步,首次
- Android(安卓)activity简单跳转动画(overridePendingTransition)
- android之两种设置全屏或者无标题的方法
- android studio aar 添加依赖