前言

  本文根据Google官方Demo练习理解


源码

1 activity_zoom.xml

                                                                                    

2 strings.xml

<?xml version="1.0" encoding="utf-8"?>    ZoomAnimation    Hello world!    Settings    Touch a photo to expand it.    Image 1    Image 2    Expanded image (touch to close)

3 TouchHighlightImageButton.java

package com.example.zoomanimation;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Rect;import android.graphics.drawable.Drawable;import android.util.AttributeSet;import android.widget.ImageButton;public class TouchHighlightImageButton extends ImageButton {    private Drawable mForegroundDrawable;    private Rect mCachedBounds = new Rect();    public TouchHighlightImageButton(Context context) {        super(context);        init();    }    public TouchHighlightImageButton(Context context, AttributeSet attrs) {        super(context, attrs);        init();    }    public TouchHighlightImageButton(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        init();    }    private void init() {        // Reset default ImageButton background and padding.        setBackgroundColor(0);        setPadding(0, 0, 0, 0);        // Retrieve the drawable resource assigned to the        // android.R.attr.selectableItemBackground        // theme attribute from the current theme.        TypedArray a = getContext().obtainStyledAttributes(new int[] { android.R.attr.selectableItemBackground });        mForegroundDrawable = a.getDrawable(0);        mForegroundDrawable.setCallback(this);        a.recycle();    }    @Override    protected void drawableStateChanged() {        super.drawableStateChanged();        // Update the state of the highlight drawable to match        // the state of the button.        if (mForegroundDrawable.isStateful()) {            mForegroundDrawable.setState(getDrawableState());        }        // Trigger a redraw.        invalidate();    }    @Override    protected void onDraw(Canvas canvas) {        // First draw the image.        super.onDraw(canvas);        // Then draw the highlight on top of it. If the button is neither        // focused        // nor pressed, the drawable will be transparent, so just the image        // will be drawn.        mForegroundDrawable.setBounds(mCachedBounds);        mForegroundDrawable.draw(canvas);    }    @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        // Cache the view bounds.        mCachedBounds.set(0, 0, w, h);    }}

4 ZoomActivity.java

package com.example.zoomanimation;import android.animation.Animator;import android.animation.AnimatorListenerAdapter;import android.animation.AnimatorSet;import android.animation.ObjectAnimator;import android.app.Activity;import android.graphics.Point;import android.graphics.Rect;import android.os.Bundle;import android.view.View;import android.view.animation.DecelerateInterpolator;import android.widget.ImageView;public class ZoomActivity extends Activity {    /**     * Hold a reference to the current animator, so that it can be canceled     * mid-way.     */    private Animator mCurrentAnimator;    /**     * The system "short" animation time duration, in milliseconds. This     * duration is ideal for subtle animations or animations that occur very     * frequently.     */    private int mShortAnimationDuration;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_zoom);        // Hook up clicks on the thumbnail views.        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);            }        });        // Retrieve and cache the system's default "short" animation time.        mShortAnimationDuration = getResources().getInteger(android.R.integer.config_shortAnimTime);    }    /**     * "Zooms" in a thumbnail view by assigning the high resolution image to a     * hidden "zoomed-in" image view and animating its bounds to fit the entire     * activity content area. More specifically:     *     * 
    *
  1. Assign the high-res image to the hidden "zoomed-in" (expanded) image * view.
  2. *
  3. Calculate the starting and ending bounds for the expanded view.
  4. *
  5. Animate each of four positioning/sizing properties (X, Y, SCALE_X, * SCALE_Y) simultaneously, from the starting bounds to the ending bounds.
  6. *
  7. Zoom back out by running the reverse animation on click.
  8. *
* * @param thumbView * The thumbnail view to zoom in. * @param imageResId * The high-resolution version of the image represented by the * thumbnail. */ private void zoomImageFromThumb(final View thumbView, int imageResId) { // If there's an animation in progress, cancel it immediately and // proceed with this one. if (mCurrentAnimator != null) { mCurrentAnimator.cancel(); } // Load the high-resolution "zoomed-in" image. final ImageView expandedImageView = (ImageView) findViewById(R.id.expanded_image); expandedImageView.setImageResource(imageResId); // Calculate the starting and ending bounds for the zoomed-in image. // This step // involves lots of math. Yay, math. final Rect startBounds = new Rect(); final Rect finalBounds = new Rect(); final Point globalOffset = new Point(); // The start bounds are the global visible rectangle of the thumbnail, // and the // final bounds are the global visible rectangle of the container view. // Also // set the container view's offset as the origin for the bounds, since // that's // the origin for the positioning animation properties (X, Y). thumbView.getGlobalVisibleRect(startBounds); findViewById(R.id.container).getGlobalVisibleRect(finalBounds, globalOffset); startBounds.offset(-globalOffset.x, -globalOffset.y); finalBounds.offset(-globalOffset.x, -globalOffset.y); // Adjust the start bounds to be the same aspect ratio as the final // bounds using the // "center crop" technique. This prevents undesirable stretching during // the animation. // Also calculate the start scaling factor (the end scaling factor is // always 1.0). 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; } // Hide the thumbnail and show the zoomed-in view. When the animation // begins, // it will position the zoomed-in view in the place of the thumbnail. thumbView.setAlpha(0f); expandedImageView.setVisibility(View.VISIBLE); // Set the pivot point for SCALE_X and SCALE_Y transformations to the // top-left corner of // the zoomed-in view (the default is the center of the view). expandedImageView.setPivotX(0f); expandedImageView.setPivotY(0f); // Construct and run the parallel animation of the four translation and // scale properties // (X, Y, SCALE_X, and SCALE_Y). 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)) .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; // Upon clicking the zoomed-in image, it should zoom back down to the // original bounds // and show the thumbnail instead of the expanded image. 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; } }); }}

分析

对zoomImageFromThumb方法添加Log,打印数据理解下:

private void zoomImageFromThumb(final View thumbView, int imageResId) {        // If there's an animation in progress, cancel it immediately and        // proceed with this one.        if (mCurrentAnimator != null) {            mCurrentAnimator.cancel();        }        // Load the high-resolution "zoomed-in" image.        final ImageView expandedImageView = (ImageView) findViewById(R.id.expanded_image);        expandedImageView.setImageResource(imageResId);        // Calculate the starting and ending bounds for the zoomed-in image.        // This step        // involves lots of math. Yay, math.        final Rect startBounds = new Rect();        final Rect finalBounds = new Rect();        final Point globalOffset = new Point();        // The start bounds are the global visible rectangle of the thumbnail,        // and the        // final bounds are the global visible rectangle of the container view.        // Also        // set the container view's offset as the origin for the bounds, since        // that's        // the origin for the positioning animation properties (X, Y).        thumbView.getGlobalVisibleRect(startBounds);        findViewById(R.id.container).getGlobalVisibleRect(finalBounds, globalOffset);        Log.d("ZoomActivity", "before startBounds =" + startBounds);        Log.d("ZoomActivity", "before finalBounds =" + finalBounds);        int[] thumbViewLocation = new int[2];        thumbView.getLocationOnScreen(thumbViewLocation);        int thumbViewX = thumbViewLocation[0];        int thumbViewY = thumbViewLocation[1];        int[] containerLocation = new int[2];        findViewById(R.id.container).getLocationOnScreen(containerLocation);        int containerX = containerLocation[0];        int containerY = containerLocation[1];        Log.d("ZoomActivity", "getLocationOnScreen thumbViewX =" + thumbViewX + "   thumbViewY=" + thumbViewY);        Log.d("ZoomActivity", "getLocationOnScreen containerX =" + containerX + "   containerY=" + containerY);        startBounds.offset(-globalOffset.x, -globalOffset.y);        finalBounds.offset(-globalOffset.x, -globalOffset.y);        Log.d("ZoomActivity", "after startBounds =" + startBounds);        Log.d("ZoomActivity", "after finalBounds =" + finalBounds);        // Adjust the start bounds to be the same aspect ratio as the final        // bounds using the        // "center crop" technique. This prevents undesirable stretching during        // the animation.        // Also calculate the start scaling factor (the end scaling factor is        // always 1.0).        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;        }        // Hide the thumbnail and show the zoomed-in view. When the animation        // begins,        // it will position the zoomed-in view in the place of the thumbnail.        thumbView.setAlpha(0f);        expandedImageView.setVisibility(View.VISIBLE);        // Set the pivot point for SCALE_X and SCALE_Y transformations to the        // top-left corner of        // the zoomed-in view (the default is the center of the view).        expandedImageView.setPivotX(0f);        expandedImageView.setPivotY(0f);        // Construct and run the parallel animation of the four translation and        // scale properties        // (X, Y, SCALE_X, and SCALE_Y).        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))                .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;        // Upon clicking the zoomed-in image, it should zoom back down to the        // original bounds        // and show the thumbnail instead of the expanded image.        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;            }        });    }


Log:

01-05 00:05:33.585 16826 16826 D ZoomActivity: before startBounds =Rect(32, 246 - 232, 396)
01-05 00:05:33.585 16826 16826 D ZoomActivity: before finalBounds =Rect(0, 144 - 720, 1184)
01-05 00:05:33.586 16826 16826 D ZoomActivity: getLocationOnScreen thumbViewX =32   thumbViewY=246
01-05 00:05:33.586 16826 16826 D ZoomActivity: getLocationOnScreen containerX =0   containerY=144
01-05 00:05:33.586 16826 16826 D ZoomActivity: after startBounds =Rect(32, 102 - 232, 252)
01-05 00:05:33.586 16826 16826 D ZoomActivity: after finalBounds =Rect(0, 0 - 720, 1040)


A,

从Log知:getGlobalVisibleRect方法返回的Rect对象的左上角坐标与getLocationOnScreen方法是一致的,也就理解了:

getGlobalVisibleRect获取全局坐标系的一个视图区域, 返回一个填充的Rect对象,该Rect是基于总整个屏幕的。


对view进行offset处理是为了去除相对屏幕的偏移量,使”子View“的Rect的左上角坐标是相对"父View",这样利于计算”子View“相对”父View“的最终显示位置


B,

  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;
        }
   为了使用相同的宽高比例,避免出现较大的不正常拉伸 ,需要进行计算:
         

   1.满足if判断说明:“屏幕宽高比例” 大于 “缩略图宽高比例”,说明最终显示的“宽度”较大,为了较少变形拉伸,因此需要在“宽度”上进行调整,此时就应该拿相对比较接近的"原始高度"因素进行计算(为了较少偏差),首先通过"高度"计算出“缩放因子”(startScale),使用同样的缩放因子(startScale)计算出“应该有的原始宽度”,然后用“应该有的原始宽度”减去“真实的原始宽度“就是总的宽度偏差,然后除以2算出单侧的偏差(deltaHeight),然后对”真实的原始宽度“,用”单侧的偏差(deltaHeight)“进行微调 

   2.满足else判断说明:屏幕宽高比例” 小于 “缩略图宽高比例”,说明最终显示的“高度”较大为了较少变形拉伸,因此需要在“高度”上进行调整,类似上面分析...


上面使用了:”相同的宽高比例“及"中心剪切技术"等概念。


原文地址:http://blog.csdn.net/yelangjueqi/article/details/56290791


效果图:

Android 对一个View进行缩放处理(放大或缩小View)案例_第1张图片

Android 对一个View进行缩放处理(放大或缩小View)案例_第2张图片

更多相关文章

  1. Android缩放drawable
  2. iPhone、Android 社交 App 使用比例大調查(資訊圖表)
  3. Android之UI学习篇六:ImageView实现图片旋转和缩放
  4. Android中获取文本宽度和高度
  5. Android 图片点击 全屏缩放预览
  6. android ImagView缩放方法之一(Bitmap)
  7. 移动端页面宽度自适应

随机推荐

  1. 打造一个Android(安卓)APP 序
  2. Android(安卓)10 手势导航源码分析
  3. 四、Android中控件的继承 通用行为和属性
  4. android并发网络请求的处理
  5. Android(安卓)生成jar包(2)
  6. Android(安卓)NDK——必知必会之Native线
  7. android解决dialog和popupwindow的BadTok
  8. Android(安卓)集成新浪微博分享及授权 (
  9. Android(安卓)9.0 flash播放器播放swf源
  10. Android(安卓)Intent用法总结