Android 对一个View进行缩放处理(放大或缩小View)案例
来源:互联网 发布:真牛皮包淘宝网 编辑:程序博客网 时间:2024/06/06 03:17
前言
本文根据Google官方Demo练习理解
源码
1 activity_zoom.xml
<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" /> <!-- This is an example layout containing thumbnail image buttons that, when pressed, zoom in to show more detail. All of the zooming and animation logic is in the ZoomActivity class. --> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:orientation="horizontal" > <!-- These buttons don't have any decorations (3D bevel, etc.), but it's still important to show feedback on touch or focus. The custom "ToughHighlightImageButton" ImageButton subclass helps achieve this by drawing the standard system "pressed" and "focused" overlay upon user interaction. --> <com.example.zoomanimation.TouchHighlightImageButton android:id="@+id/thumb_button_1" android:layout_width="100dp" android:layout_height="75dp" android:layout_marginRight="1dp" android:contentDescription="@string/description_image_1" android:scaleType="centerCrop" android:src="@drawable/thumb1" /> <com.example.zoomanimation.TouchHighlightImageButton android:id="@+id/thumb_button_2" android:layout_width="100dp" android:layout_height="75dp" android:contentDescription="@string/description_image_2" android:scaleType="centerCrop" android:src="@drawable/thumb2" /> </LinearLayout> </LinearLayout> <!-- This initially-hidden ImageView will hold the expanded/zoomed version of the images above. Without transformations applied, it takes up the entire screen. To achieve the "zoom" animation, this view's bounds are animated from the bounds of the thumbnail buttons above, to its final laid-out bounds. The implementation of this animation is in the ZoomActivity class. --> <ImageView android:id="@+id/expanded_image" android:layout_width="match_parent" android:layout_height="match_parent" android:contentDescription="@string/description_zoom_touch_close" android:visibility="invisible" /></FrameLayout>
2 strings.xml
<?xml version="1.0" encoding="utf-8"?><resources> <string name="app_name">ZoomAnimation</string> <string name="hello_world">Hello world!</string> <string name="action_settings">Settings</string> <string name="message_zoom_touch_expand">Touch a photo to expand it.</string> <string name="description_image_1">Image 1</string> <string name="description_image_2">Image 2</string> <string name="description_zoom_touch_close">Expanded image (touch to close)</string></resources>
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: * * <ol> * <li>Assign the high-res image to the hidden "zoomed-in" (expanded) image * view.</li> * <li>Calculate the starting and ending bounds for the expanded view.</li> * <li>Animate each of four positioning/sizing properties (X, Y, SCALE_X, * SCALE_Y) simultaneously, from the starting bounds to the ending bounds.</li> * <li>Zoom back out by running the reverse animation on click.</li> * </ol> * * @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)案例
- Android view点击放大缩小
- 自定义View放大缩小
- 让一个正方形View以四个角中的任意一角为起点放大或缩小
- 自定义view触摸放大缩小
- 自定义view圆放大缩小
- Android中显示图片进行放大或缩小
- View 界面的放大和缩小
- 自定义View之图片放大、缩小、移动
- view 放大缩小动画效果
- android 图片放大缩小 多点缩放
- android 图片放大缩小 多点缩放
- android 图片浏览功能 图片放大缩小 使用 photoview 双击或双指缩放的ImageView
- 功能及需求:pictureBox里图像的缩放,要求不保存缩放后的图像,只对原图像进行查看式缩放,且图像缩小然后放大,处理图像失真的问题。
- drawdib对图像进行放大缩小不成功
- 对系统图片进行放大或者缩小
- matlab对图片进行放大和缩小
- 读入图像对其进行放大缩小
- sql sever增删查改之--------------查询
- mysql学习笔记(2)
- Mac下静态库和动态库的创建和使用
- Android开发之动画
- spring基础与重点学习小结
- Android 对一个View进行缩放处理(放大或缩小View)案例
- HTTP协议详解(真的很经典)
- 欢迎使用CSDN-markdown编辑器
- Springmvc同一请求下根据不同的逻辑返回页面或者json
- 安卓开源项目周报0215
- 64位linux下,C程序调用 ImageMagick API方法
- Vue自定义过滤器
- iOS开发:使用FMDB插入单引号值" ' "数据到SQLite数据库中
- 超实用Docker入门学习教程