Demo_ ZoomActivity 解读

版权所有,禁止匿名转载;禁止商业使用。

这个是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开发,Demo_ ZoomActivity

0 0