Android官网培训课:缩放View

本节课讲述如何实现触屏缩放动画,这种动画很有用处,像在图片集中将一个缩略图放大成一个全尺寸填满屏幕的图片。

创建View


创建一个layout布局文件,其中包含你要缩放的内容的小尺寸版本和大尺寸版本。下面的历次创建了一个ImageButton以用于可点击的缩略图和一个ImageView以用于显示放大了的图片:

<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">

        <ImageButton
            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" />

    </LinearLayout>

    <!-- 该ImageView在初始化后会被隐藏,其会保存前面图片的缩小版和扩大版。该ImageView会占满整个屏幕。为了实现缩放动画,该view的边界会从缩略图按钮的边界变换到最终的布局边界。
         -->

    <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>

建立缩放动画


一旦应用了layout布局,就建立触发缩放动画的事件处理器。下面的例子把View.OnClickListener监听器添加到ImageButton来在用户点击图片按钮时执行缩放动画。

public class ZoomActivity extends FragmentActivity {
    // 保存一个对当前animator的引用,确保在中途它能被删掉
    private Animator mCurrentAnimator;

    // 系统”短“动画持续时间,以毫秒为单位。该持续时间是短暂动画或频繁发生动画的理想持续时间。
    private int mShortAnimationDuration;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_zoom);

        // 绑定缩略图view的点击事件

        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);
            }
        });

        // 重新找回并缓存系统的默认”短“动画持续时间
        mShortAnimationDuration = getResources().getInteger(
                android.R.integer.config_shortAnimTime);
    }
    ...
}

缩放View


现在你需要在适当的时点将正常尺寸的view变为缩放的view。通常,你需要从正常尺寸的view边界变换到大尺寸view的边界。下面的方法讲解了如何实现一个从缩略图放大到大尺寸view的几个步骤:

  1. 把高解析度大图赋值到隐藏状态的”放大“ImageView。下面的例子中直接在UI线程加载了大图,实际上你应该在另一个独立的线程去加载图片以防阻碍了UI主线程然后在UI线程设置位图,理想状态下位图不应该大于图片尺寸。
  2. 计算ImageView的开始边界和结束边界。
  3. Animate each of the four positioning and sizing properties XY, (SCALE_X, and SCALE_Y) simultaneously, from the starting bounds to the ending bounds. These four animations are added to an AnimatorSet so that they can be started at the same time.
  4. 当用户点击已经放大了的图片时,以一个类似于放大动画但逆向的动画来返回缩小view。可以通过向ImageView上添加View.OnClickListener监听器来实现。当点击时,ImageView缩小回缩略图尺寸然后设置其可见性visibility为GONE以隐藏自己。
private void zoomImageFromThumb(final View thumbView, int imageResId) {
    // 若已经有animation在进行中,立即删除该animation,然后推进新的animation
    if (mCurrentAnimator != null) {
        mCurrentAnimator.cancel();
    }

    // 加载高分辨率的放大图片
    final ImageView expandedImageView = (ImageView) findViewById(
            R.id.expanded_image);
    expandedImageView.setImageResource(imageResId);

    // 计算放大图片动画的开始边界和终止边界。该步骤设计较多的数学计算
    final Rect startBounds = new Rect();
    final Rect finalBounds = new Rect();
    final Point globalOffset = new Point();

    // 开始边界即为缩略图的全局可视正方形,终止边界即为container view的全局可视正方形。The start bounds are the global visible rectangle of the thumbnail,
    // 同时设置container viewr的偏移量为边界的起点,因为这是positioning animation 参数(X,Y)的起点。
    thumbView.getGlobalVisibleRect(startBounds);
    findViewById(R.id.container)
            .getGlobalVisibleRect(finalBounds, globalOffset);
    startBounds.offset(-globalOffset.x, -globalOffset.y);
    finalBounds.offset(-globalOffset.x, -globalOffset.y);

    // 用”center crop”技术调整开始边界与终止边界的宽高比一样。这样可以防止动画执行期间的不佳的拉伸。同时计算开始的缩放比例因子(终止的比例因子总是1.0)
    float startScale;
    if ((float) finalBounds.width() / finalBounds.height()
            > (float) startBounds.width() / startBounds.height()) {
        // 水平方向扩展开始边界
        startScale = (float) startBounds.height() / finalBounds.height();
        float startWidth = startScale * finalBounds.width();
        float deltaWidth = (startWidth - startBounds.width()) / 2;
        startBounds.left -= deltaWidth;
        startBounds.right += deltaWidth;
    } else {
        // 垂直方向扩展开始边界
        startScale = (float) startBounds.width() / finalBounds.width();
        float startHeight = startScale * finalBounds.height();
        float deltaHeight = (startHeight - startBounds.height()) / 2;
        startBounds.top -= deltaHeight;
        startBounds.bottom += deltaHeight;
    }

    // 隐藏缩略图,显示放大view。当动画开始时,将把放大view安放在缩略图的位置。
    thumbView.setAlpha(0f);
    expandedImageView.setVisibility(View.VISIBLE);

    // 设置轴心点,为了SCALE_X和SCALE_Y转换到放大view的左上角(默认轴心点是view的中心)
    expandedImageView.setPivotX(0f);
    expandedImageView.setPivotY(0f);

    // 构建并运行平行的四个转换动画,即为依次对 (X, Y, SCALE_X, and SCALE_Y)四个属性执行转换.set中存放四个animator。
    AnimatorSet set = new AnimatorSet();
    set
            .play(ObjectAnimator.ofFloat(expandedImageView, View.X,         //View的X属性从startBounds.left变到finalBounds的left
                    startBounds.left, finalBounds.left))
            .with(ObjectAnimator.ofFloat(expandedImageView, View.Y,
                    startBounds.top, finalBounds.top))
            .with(ObjectAnimator.ofFloat(expandedImageView, View.SCALE_X,   //View的SCALE_X和SCALE_Y(X轴和Y轴的放大倍数,0.0代表放大为0,1.0代表正常)
            startScale, 1f)).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; //做好缓存

    // 逆向的操作,从大图到小图,先设定AnimatorSet,然后按设定执行AnimatorSet,然后隐藏大图,显示小图,不详细讲了。
    // 
    final float startScaleFinal = startScale;
    expandedImageView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            if (mCurrentAnimator != null) {
                mCurrentAnimator.cancel();
            }

            // Animate the four positioning/sizing properties in parallel,
            // back to their original values.
            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;
        }
    });
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值