注:本篇文章是对官方开发文档的翻译,加上自己的理解和分析。

地址:https://developer.android.com/training/animation/zoom

本篇文章所实现的功能:

触摸一张ImageView后开始展示缩放动画,将视图从缩略图放大为填充屏幕的全尺寸图像,并且点击大图后还能够原路返回,缩放回去。无图无真相,先来看看效果图:

实现的代码中你需要知道的一些理论知识(大神请忽略):

首先是View的两个方法:

getGlobalVisibleRect(Rect r)  →  得到这个view的可视区域的矩形

getGlobalVisibleRect(Rect r, Point globalOffset)  →  比上面的那个多了个参数,这个参数的内容是view的左上角相对屏幕左上角的偏移量

一开始看到这我是有点迷糊的,getGlobalVisibleRect中第二个参数到底是干啥用的?抱着这个疑问,我决定打个Log来看看,先看看用了哪些代码来打印:

//下面这个container就是当前的根布局的id,finalBounds就是新建的一个Rect,

findViewById(R.id.container)

.getGlobalVisibleRect(finalBounds, globalOffset);

//globalOffset里的偏移量

Log.i("TAG", "globalOffset.x:" + globalOffset.x);

Log.i("TAG", "globalOffset.y:" + globalOffset.y);

startBounds.offset(-globalOffset.x, -globalOffset.y);

finalBounds.offset(-globalOffset.x, -globalOffset.y);

//获取状态栏高度

int height = 0;

int resourceId = getApplicationContext().getResources().getIdentifier("status_bar_height", "dimen", "android");

if (resourceId > 0) {

height = getApplicationContext().getResources().getDimensionPixelSize(resourceId);

}

Log.i("TAG", "状态栏高度:" + height);

//获取actionBar的高度

int actionBarHeight = 0;

TypedValue tv = new TypedValue();

if (this.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) {

actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data, getResources().getDisplayMetrics());

Log.i("TAG", "actionBar高度:" + actionBarHeight);

}

打印的log:

到这里就一目了然了,根部局(也就是上面gif中白色的区域)的globalOffset.y = 状态栏高度 + actionBar高度 = 160,他们的关系就像下面这张图一样:

由于根部局container是铺满了除状态栏和actionBar之外的所有区域的,所以自然globalOffset.x就等于0了,这个不难理解。

在说完View的这两个方法之后,接下来开始进入正题,如何实现文章一开始时那个动图的效果呢?

1.在布局中准备一个ImageButton用于显示缩略图,再准备一个ImageView用于展示放大后的图片,一开始这个ImageView设置为不可见

2.给thumbView(ImageButton)设置点击事件,点击后开始放大,并将缩略图设置为invisible

public class MainActivity extends AppCompatActivity {

//动画的持续时间

private int mShortAnimationDuration = 1000;

//当前正在进行的动画

private AnimatorSet mCurrentAnimation;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

final ImageButton thumbView = findViewById(R.id.ib_small);

thumbView.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

zoomImageFromThumb(thumbView, R.mipmap.wang);

}

});

}

3.也就是核心部分,zoomImageFromThumb都做了哪些事儿,注释写的很清楚,大家先瞅瞅

/**

* 缩放动画

*

* @param thumbView 缩略图

* @param imageResId 图片资源ID

*/

private void zoomImageFromThumb(final View thumbView, int imageResId) {

//如果当前有动画就取消,并执行当前的动画

if (mCurrentAnimation != null) {

mCurrentAnimation.cancel();

}

//加载大图到预设好的ImageView中

final ImageView expandedImageView = findViewById(

R.id.iv_big);

expandedImageView.setImageResource(imageResId);

//计算放大图像的起始边界和结束边界

final Rect startBounds = new Rect();

final Rect finalBounds = new Rect();

final Point globalOffset = new Point();

//起始边界是缩略图的全局可见矩形,最后的边界是容器container的全局可见矩形

//将容器视图container的偏移量设置为区域的起源,因为定位动画的起源属性是(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将起始边界调整为与最终边界相同的纵横比边界

//这可以防止在动画期间的不良拉伸。还计算开始缩放比例

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;

}

//隐藏缩略图并显示放大视图

//当动画开始时,它会将放大的视图定位在原来缩略图的位置

thumbView.setAlpha(0f);

expandedImageView.setVisibility(View.VISIBLE);

//设置SCALE_X和SCALE_Y转换的轴心点为到放大后视图的左上角(默认值是视图的中心)

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

.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) {

mCurrentAnimation = null;

}

@Override

public void onAnimationCancel(Animator animation) {

mCurrentAnimation = null;

}

});

set.start();

mCurrentAnimation = set;

//从这往下就是给大图设置点击监听,完成缩小回去的过程

final float startScaleFinal = startScale;

expandedImageView.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View view) {

if (mCurrentAnimation != null) {

mCurrentAnimation.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);

mCurrentAnimation = null;

}

@Override

public void onAnimationCancel(Animator animation) {

thumbView.setAlpha(1f);

expandedImageView.setVisibility(View.GONE);

mCurrentAnimation = null;

}

});

set.start();

mCurrentAnimation = set;

}

});

}

除了注释之外,还需要对上面的代码做一些说明:

1.读了本文开头部分之后,相信你一定就明白了startBounds.offset()和finalBounds.offset();这两句话是在消除偏差,保证这两个矩形区域边界的计算都是相对于坐标轴的起始点(0,0)。

2.关于选择横向放大还是纵向放大的判断,先来看一张图:

就拿纵向放大来说吧,当startBounds的宽/高  > finalBounds的宽/高时,也就是上图右边那种情况,这时代码是下面这样的:

//纵向放大

startScale = (float) startBounds.width() / finalBounds.width();

float startHeight = startScale * finalBounds.height();

float deltaHeight = (startHeight - startBounds.height()) / 2;

startBounds.top -= deltaHeight;

startBounds.bottom += deltaHeight;

经过上面几行代码的处理之后,实际上startBounds高度已经变高,相当将startBounds上下分别拉伸了相同距离,从而达到了纵向放大的效果。最终startBounds经过动画放大到红色区域,缩小的过程原理也是一样的,就不再多言了。

因为非常简单,所有代码已经贴在上面,所以就不贴demo了。

android图片缩放模式,Android使用缩放动画放大你的图片相关推荐

  1. android studio夜间模式,android studio怎样实现夜间模式

    满意答案 noxlqub 2016.05.01 采纳率:51%    等级:7 已帮助:210人 关于阅读类的app,有个夜间模式真是太重要了. 那么有两种方式可以实现夜间模式 1:修改theme,重 ...

  2. android 省电模式,android省电模式设置

    关于android省电模式设置技巧大全 一.屏幕显示 降低屏幕的亮度,可以有效的节省电能,尤其是摩托罗拉产的Android手机目前为止均为TFT屏幕,同时自动锁屏幕的时间设置的短一些也会带来一些电量的 ...

  3. android自动夜间模式,Android 夜间模式初探

    当下各种阅读类APP(如各种浏览器,某日头条等)都会有夜间模式,也顺应了大家的睡前必须玩一下手机的作息习惯.关于夜间模式的实现,有很多种方法.这篇日志学习一下最简单的实现方式,通过setTheme(i ...

  4. Android自定义ImageView(二)——实现双击放大与缩小图片

    效果图: 首先设置图片依据控件的大小来显示在ImageVeiw中 也就是当图片的宽与高小于控件的宽与高的时候,默认不进行对图片进行放大的操作,但是会将图片居中显示,当然使用的时候可以使用自定义的属性i ...

  5. android自动夜间模式,Android实现日夜间模式的深入理解

    在本篇文章中给出了三种实现日间/夜间模式切换的方案,三种方案综合起来可能导致文章的篇幅过长,请耐心阅读. 1.使用 setTheme的方法让 Activity重新设置主题: 2.设置 Android ...

  6. android系统recovery模式,Android系统Recovery模式中文详细说明

    Recovery具体功能: 1.刷系统:新下载好的rom,,直接放sd卡上刷(进nand),,无需windows! 2.像电脑的ghost,,允许用户随意将系统和里面的个人资料备份成一个文件,,并允许 ...

  7. android q 桌面模式,Android Q带来全新桌面模式

    IT之家3月14日消息 谷歌在美国当地时间3月13日(北京时间14日凌晨)正式推送了Android Q的首个Beta版本,"亲儿子"Pixel系列全系手机可以尝鲜体验这最新的系统. ...

  8. android menu夜间模式,Android常用技巧夜间模式开发浅析

    前言 Android的夜间模式主要主用于阅读方面,在QQ,微信读书,新闻阅读类一般会有相应的功能,本文主要介绍整体APP的夜间模式以及webview中夜间模式的实现. 效果图展示 功能实现简介 APP ...

  9. android l art模式,Android ART模式简介

    Android4.4最大的变化就是引入ART模式来代替Dalvik虚拟机.ART是Android Runtime的缩写,它提供了以AOT(Ahead-Of-Time)的方式运行Android应用程序的 ...

最新文章

  1. MySQL性能优化速记
  2. 用VS.NET 2005重构你的代码
  3. 数据中心空调故障案例集(第二季)
  4. 【数据库复习】第一章绪论
  5. 【Spring注解系列13】Spring自动装配总结:@Autowired、@Resource、@Qualifier、@Inject
  6. m_Orchestrate learning system---二十、如何写代码不容易犯错
  7. java版b2b2c社交电商spring cloud分布式微服务:服务消费(Ribbon)
  8. SpringBoot使用@Cacheable实现最简单的Redis缓存
  9. 信息学奥赛一本通(1063:最大跨度值)
  10. JavaScript高级程序设计学习(二)之基本概念
  11. 开发者自述:我是如何从 0 到 1 走进 Kaggle 的
  12. 电脑dns_女生也能学会的修电脑技巧
  13. 【转帖】Mysql多维数据仓库指南 第一篇 第1章
  14. java数据结构与算法总结(二十五)--初识BitSet之API
  15. 申请美国密歇根州立大学需要具备哪些条件?
  16. 计算机老师教师节祝福语,送给老师教师节祝福语
  17. 如何在网页标题前添加图标
  18. 汽车转向前后轮轨迹matlab程序,车前进后退方向的口诀,动画图解车前后轮转弯轨迹...
  19. idea 项目添加图片或更换图片 引用后没反应(清缓存)
  20. Flash----读写外部Flash

热门文章

  1. 体验游戏,就在即刻!Google Play Instant
  2. 用谷歌浏览器播放audioSource的坑
  3. 心灵鸡汤若干,不喜勿入
  4. 测试面试题之白盒测试的实用技术(问题及答案)
  5. 自学的程序员一点竞争力都没有么?投简历都没有回应?
  6. endnote文献顺序编号不对_EndNote插入文献序号排序混乱如何解决
  7. 安信可的GPRS+GSM模组A9连接机智云的教程
  8. cs和msf配合进行内网的操作
  9. php下载excel文件模板文件,PHP下载excel文件变得腐败
  10. 简介 - Energy是Go基于Chromium构建Windows、Linux、MacOSX的跨平台桌面应用框架