[原创]实现android知乎、一览等的开场动画图片放大效果
代码下载地址:
https://github.com/Carbs0126/AutoZoomInImageView
知乎等app的开场动画为:一张图片被显示到屏幕的正中央,并充满整个屏幕,过一小段时间后,开始慢慢方法,且图片的正中央始终处于屏幕的正中央,也就是“镜头缓慢放大”的效果
难点1.android手机屏幕碎片化。由于是全屏显示,因此一张图片需要放到不同大小的ImageView中,且图片中央需要放到ImageView中央;
难点2.放大时需要保证图片中央与ImageView中央处于一点;
难点3.实现缓慢放大效果(这个可以利用ValueAnimator实现)
实现原理是:采用调整ImageView的matrix的方式来实现此效果。
本篇文章抽象出一个新的view,以便于使用及修改,同时完善了上篇文章由于行文仓促而留下的多个未实现的需求。
实现效果如下:

快速应用到工程:
首先,添加依赖:
compile 'cn.carbs.android:AutoZoomInImageView:1.0.0'
其次,xml布局文件中声明
<cn.carbs.android.library.AutoZoomInImageView
android:id="@+id/iv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/horse" />
最后,在Activity中使用代码:(注意,要在view完全显示出来之后使用,因此这里我用了post(runnable)的方式)
iv.post(new Runnable() {//iv即AutoZoomInImageView
@Override
public void run() {
//简单方式启动放大动画
// iv.init()
// .startZoomInByScaleDeltaAndDuration(0.3f, 1000, 1000);//放大增量是0.3,放大时间是1000毫秒,放大开始时间是1000毫秒以后
//使用较为具体的方式启动放大动画
iv.init()
.setScaleDelta(0.2f)//放大的系数是原来的(1 + 0.2)倍
.setDurationMillis(1500)//动画的执行时间为1500毫秒
.setOnZoomListener(new AutoZoomInImageView.OnZoomListener(){
@Override
public void onStart(View view) {
//放大动画开始时的回调
}
@Override
public void onUpdate(View view, float progress) {
//放大动画进行过程中的回调 progress取值范围是[0,1]
}
@Override
public void onEnd(View view) {
//放大动画结束时的回调
}
})
.start(1000);//延迟1000毫秒启动
}
});
主要的代码如下:
1.AutoZoomInImageView的代码为:
package cn.carbs.android.library; import android.animation.Animator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView; @SuppressLint("NewApi")
public class AutoZoomInImageView extends ImageView{ private Drawable mDrawable;
private int mDrawableW;
private int mDrawableH; private int mImageViewW;
private int mImageViewH; private Matrix mMatrix;
private float[] mValues = new float[9]; private float mScaleDelta = 0.2f;
private long mDurationMillis = 700; public AutoZoomInImageView(Context context) {
super(context);
this.setScaleType(ScaleType.MATRIX);
} public AutoZoomInImageView(Context context, AttributeSet attrs) {
super(context, attrs);
this.setScaleType(ScaleType.MATRIX);
} public AutoZoomInImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.setScaleType(ScaleType.MATRIX);
} public AutoZoomInImageView init(){
initInternalValues();
initPicturePosition();
return this;
} public void init(Drawable drawable){
initInternalValues(drawable);
initPicturePosition();
} private void initInternalValues(){
mDrawable = getDrawable(); if(mDrawable == null){
throw new IllegalArgumentException("please set the source of AutoZoomInImageView");
} mDrawableW = mDrawable.getIntrinsicWidth();
mDrawableH = mDrawable.getIntrinsicHeight(); mImageViewW = getMeasuredWidth();
mImageViewH = getMeasuredHeight(); mMatrix = getImageMatrix();
mMatrix.getValues(mValues);
} private void initInternalValues(Drawable drawable){
mDrawable = drawable; if(mDrawable == null){
throw new IllegalArgumentException("please set the source of AutoZoomInImageView");
} mDrawableW = mDrawable.getIntrinsicWidth();
mDrawableH = mDrawable.getIntrinsicHeight(); mImageViewW = getMeasuredWidth();
mImageViewH = getMeasuredHeight(); mMatrix = getImageMatrix();
mMatrix.getValues(mValues);
} private void initPicturePosition(){
updateMatrixValuesOrigin(mMatrix, mValues, mDrawableW, mDrawableH, mImageViewW, mImageViewH);
setImageMatrix(mMatrix);
} private void startZoomInByScaleDelta(final float scaleDelta, long duration){ final float oriScaleX = mValues[0];
final float oriScaleY = mValues[4]; ValueAnimator va = ValueAnimator.ofFloat(0, scaleDelta);
va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (Float)animation.getAnimatedValue();
if(mOnZoomListener != null) mOnZoomListener.onUpdate(AutoZoomInImageView.this, value / scaleDelta);
updateMatrixValuesSpan(mValues, mDrawableW, mDrawableH, mImageViewW, mImageViewH,
oriScaleX, oriScaleY, value);
mMatrix.setValues(mValues);
setImageMatrix(mMatrix);
}
});
va.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
if(mOnZoomListener != null) mOnZoomListener.onStart(AutoZoomInImageView.this);
} @Override
public void onAnimationEnd(Animator animation) {
if(mOnZoomListener != null) mOnZoomListener.onEnd(AutoZoomInImageView.this);
}
@Override
public void onAnimationCancel(Animator animation) {}
@Override
public void onAnimationRepeat(Animator animation) {}
});
va.setDuration(duration);
va.start();
} /**
* 开始放大动画
* start zooming in
* @param scaleDelta 放大的增大倍数,如果是0.2,那么最后大小放大至1.2倍。
* the scale that the image will add to original scale
* @param durationMillis 放大效果的持续时间,单位毫秒。
* the duration of zoomin animation, in millisecond.
* @param delayMillis 开始放大效果的延迟时间,单位毫秒。delayed毫秒后开始放大动画效果。
* the delayed time of starting zoomin animation, in millisecond.
*/
public void startZoomInByScaleDeltaAndDuration(final float scaleDelta, final long durationMillis, long delayMillis){
if(scaleDelta < 0){
throw new IllegalArgumentException("scaleDelta should be larger than 0, now scaleDelta is " + scaleDelta);
}
if(durationMillis < 0){
throw new IllegalArgumentException("durationMillis should not be less than 0, now durationMillis is " + durationMillis);
}
if(delayMillis < 0){
throw new IllegalArgumentException("delayMillis should not be less than 0, now delayMillis is " + delayMillis);
} postDelayed(new Runnable() {
@Override
public void run() {
startZoomInByScaleDelta(scaleDelta, durationMillis);
}
}, delayMillis);
} /**
* 放大的增大倍数,如果是0.2,那么最后大小放大至1.2倍。
* the scale that the image will add to original scale
* @param scaleDelta
* @return
*/
public AutoZoomInImageView setScaleDelta(float scaleDelta){
mScaleDelta = scaleDelta;
return this;
} /**
* 放大效果的持续时间,单位毫秒。
* the duration of zoomin animation, in millisecond.
* @param durationMillis
* @return
*/
public AutoZoomInImageView setDurationMillis(long durationMillis){
mDurationMillis = durationMillis;
return this;
} /**
* 动画结束的回调
* callback when zoomin animation finished
* @param onZoomListener
* @return
*/
public AutoZoomInImageView setOnZoomListener(OnZoomListener onZoomListener){
mOnZoomListener = onZoomListener;
return this;
} /**
* 开始放大效果
* start animation of zoomin
* @param delayMillis 开始放大效果的延迟时间,单位毫秒。delayed毫秒后开始放大动画效果
* the delayed time of starting zoomin animation, in millisecond.
*/
public void start(long delayMillis){
postDelayed(new Runnable() {
@Override
public void run() {
startZoomInByScaleDelta(mScaleDelta, mDurationMillis);
}
}, delayMillis);
} private void updateMatrixValuesOrigin(Matrix outMatrix, float[] outValues, float drawW, float drawH, float imageW, float imageH){ if(outMatrix == null || outValues == null){
throw new IllegalArgumentException("please set the source of AutoZoomInImageView's matrix and values");
} outMatrix.reset(); if((imageH * drawW > drawH * imageW)){
float scale1 = (imageH)/(drawH);
float offset1 = (drawW * scale1 - imageW)/2; outMatrix.postScale(scale1, scale1);
outMatrix.postTranslate(-offset1, 0); }else{
float scale2 = (imageW)/(drawW);
float offset2 = (drawH * scale2 - imageH)/2; outMatrix.postScale(scale2, scale2);
outMatrix.postTranslate(0, -offset2);
}
outMatrix.getValues(outValues);
} private void updateMatrixValuesSpan(float[] outValues,
float drawW, float drawH,
float imageW, float imageH,
float oriScaleX, float oriScaleY,
float scaleDelta){
//根据四个参数:图片的宽高、控件的宽高,动态的计算出输出的矩阵(float数组)的值
outValues[0] = oriScaleX * (1 + scaleDelta);
outValues[4] = oriScaleY * (1 + scaleDelta);
float offsetwidth = (drawW * outValues[0] - imageW)/2;
outValues[2] = - offsetwidth;
float offsetHeight = (drawH * outValues[0] - imageH)/2;
outValues[5] = - offsetHeight;
} private OnZoomListener mOnZoomListener;
public interface OnZoomListener{
/**
* 动画更新时执行的回调
* @param view 返回此AutoZoomInImageView
* @param progress 返回动画进行过程,范围是[0,1]
*/
void onUpdate(View view, float progress);
void onEnd(View view);
void onStart(View view);
} //function for log
public String printMyMatrix(Matrix m){
float[] valueFloat = new float[9];
m.getValues(valueFloat); String s = "";
for(int i = 0; i < 9; i++){
s = s + " [ " + valueFloat[i] + " ] ";
}
return s;
} //function for log
public String printMyValue(float[] valueFloat){
String s = "";
for(int i = 0; i < 9; i++){
s = s + " [ " + valueFloat[i] + " ] ";
}
return s;
} }
2.代码中调用AutoZoomInImageView启用动画的方法:
iv.post(new Runnable() {//iv即AutoZoomInImageView
@Override
public void run() {
//简单方式启动放大动画
// iv.init()
// .startZoomInByScaleDeltaAndDuration(0.3f, 1000, 1000);//放大增量是0.3,放大时间是1000毫秒,放大开始时间是1000毫秒以后
//使用较为具体的方式启动放大动画
iv.init()
.setScaleDelta(0.2f)//放大的系数是原来的(1 + 0.3)倍
.setDurationMillis(1500)//动画的执行时间为1000毫秒
.setOnZoomListener(new AutoZoomInImageView.OnZoomListener(){
@Override
public void onStart(View view) {
//放大动画开始时的回调
}
@Override
public void onUpdate(View view, float progress) {
//放大动画进行过程中的回调 progress取值范围是[0,1]
}
@Override
public void onEnd(View view) {
//放大动画结束时的回调
}
})
.start(1000);//延迟1000毫秒启动
}
});
3.xml文件中声明此view:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"> <cn.carbs.android.library.AutoZoomInImageView
android:id="@+id/iv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/horse" /> </LinearLayout>
代码下载地址:
https://github.com/Carbs0126/AutoZoomInImageView
[原创]实现android知乎、一览等的开场动画图片放大效果的更多相关文章
- 实现仿知乎的开场动画,图片zoomin的效果,实现原理,没加动效
知乎等应用的开场动画是:全屏显示一副图像,并以图像的中间为原点,实现放大(也就是zoomin)的动画,让等待的过程不再单调乏味. 最近不是很忙,因此想了下如何实现这种效果,方案是:采用调整imagev ...
- [原创]AndroBugs_Framework Android漏洞扫描器介绍
[原创]AndroBugs_Framework Android漏洞扫描器介绍 1 AndroBugs_Framework Android 漏洞扫描器简介 一款高效的Android漏洞扫描器,可以帮助开 ...
- 【优才原创】Android的拖放机制
优才网 [优才原创]Android的拖放机制 2016-04-18 优才学院 优才网 一.拖放机制概述 ² 拖放操作是手指触摸屏幕上的某一对象.然后拖动该对象.最后在屏幕的某个位置释放该对象并运行某种 ...
- Android UI体验之全屏沉浸式透明状态栏效果
前言: Android 4.4之后谷歌提供了沉浸式全屏体验, 在沉浸式全屏模式下, 状态栏. 虚拟按键动态隐藏, 应用可以使用完整的屏幕空间, 按照 Google 的说法, 给用户一种 身临其境 的体 ...
- 浅谈Android样式开发之View Animation (视图动画)
引言 一个用户体验良好的App肯定少不了动画效果.Android为我们提供了2种动画框架,分别是视图动画(View Animation)和属性动画(Property Animation).视图动画比较 ...
- Android Activity和Fragment的转场动画
Android Activity和Fragment的转场动画 Activity转场动画 Activity的转场动画是通过overridePendingTransition(int enterAnim, ...
- 41.Android之图片放大缩小学习
生活中经常会用到图片放大和缩小,今天简单学习下. 思路:1.添加一个操作图片放大和缩小类; 2. 布局文件中引用这个自定义控件; 3. 主Activity一些修改. 代码如下: 增加图片操作类: ...
- [转]ANDROID L——Material Design详解(动画篇)
转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! 转自:http://blog.csdn.net/a396901990/article/de ...
- Android之绚丽的图片游览效果--有点像W7效果,透明的倒影,层叠的图片,渐变的颜色透明度
这里转载一个牛人的博客:http://www.cnblogs.com/tankaixiong/archive/2011/02/24/1964340.html 下面,是我参照他的博客实现的一个效果图.这 ...
随机推荐
- 搭建Openstack云平台
实验室需要做一个大数据平台项目,临时接下需要部署实验室云平台的任务,由于之前没有接触过相关技术,仅以此篇作为纪录文,记录一下我的openstack的初步学习以及搭建过程. 1.openstcak及其组 ...
- 【Win10 UWP】QQ SDK(一):SDK基本使用方法
每当开发一个应用需要社交分享的应用时,总是心里咯噔一下:到底什么时候分享能加上QQ和微信?除了WP8.0版本的微信SDK,官方似乎从未正面发布过适应时代发展的QQ SDK,就连后台,也没有一个可以创建 ...
- 收缩SQL Server日志不是那么简单的(翻译)
原文地址:http://rusanu.com/2012/07/27/how-to-shrink-the-sql-server-log/ 说明:本文为了更好的说明收缩的过程,在原文翻译的基础上增加了一些 ...
- JsRender实用教程(tag else使用、循环嵌套访问父级数据)
前言 JsRender是一款基于jQuery的JavaScript模版引擎,它具有如下特点: · 简单直观 · 功能强大 · 可扩展的 · 快如闪电 这些特性看起来很厉害,但几乎每个模版引擎, ...
- ThreadLocal线程范围内的共享变量
模拟ThreadLocal类实现:线程范围内的共享变量,每个线程只能访问他自己的,不能访问别的线程. package com.ljq.test.thread; import java.util.Has ...
- JavaScript-冒泡排序
随机生成10个不重复的100以内的整数放入数组,并排序后进行打印<br /> 如: for(var i=0;i<9;i++) { for(var j=i+1;j<9;j++) ...
- atitit.gui界面纵向居中总结
atitit.gui界面纵向居中总结 1.table法...这个简单.. 表格设置100%高度,<td align="center" valign="middle& ...
- SQL 2012 发布与订阅实现数据同步 图解(解决 错误22022)
概念参见:https://msdn.microsoft.com/zh-cn/library/ms151170.aspx 推送订阅 对于推送订阅,发布服务器将更改传播到订阅服务器,而无需订阅服务器发出请 ...
- python中os和sys模块的详解
平时在工作中经常会用到os模块和sys模块的一些特性,下面是这些特性的一些相关解释,希望对大家有所帮助 os模块 os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径 os. ...
- PHP实现微信公众账号开发
1.首先需要一个可以外网访问的接口url. 我这里是申请的新浪免费云服务器,http://xxxxx.applinzi.com/wx.php,具体自己可以去新浪云中心申请地址为:http://www. ...