今天来写一篇关于点击小图查看大图的缩放动画的文章,效果图如下所示:
先来讲一下实现的思路:看到这个效果图,想都不用想就知道用属性动画或者补间动画通过缩放、位移、改变透明度来实现。首先点击小图会跳转到另一个Activity B来显示大图。这个Activity是透明的Activity(因为点击大图执行退出动画时,需要看到上一个界面,效果才更佳)。需要传小图的位置、大小、图片地址三个信息给Activity B,执行图片放大效果前,需要在Activity B中绘制一个和小图大小位置一样的控件。再对图片进行放大和位移。点击大图退出时,对图片进行缩小和位移。同时对Activity B背景透明度从完全不透明到完全透明改变。
下面看一下具体代码的实现:
//小图界面部分代码
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.imageView:
int []locations=new int[2];
//获取控件的绝对坐标包括状态栏高度
imageView.getLocationOnScreen(locations);
//小于android4.4 不全屏
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.KITKAT) {
locations[1] = locations[1] - Utils.getStatusBarHeight(this);
}
String uri=Utils.testImageUrl;
PictureActivity.startActivity(this,locations,imageView.getWidth(),imageView.getHeight(),uri);
break;
case R.id.imageView2:
int []locations2=new int[2];
//获取控件的绝对坐标包括状态栏高度
imageViewsqure.getLocationOnScreen(locations2);
//小于android4.4 不全屏
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.KITKAT) {
locations2[1] = locations2[1] - Utils.getStatusBarHeight(this);
}
String uri2=Utils.testImageUrl2;
PictureActivity.startActivity(this,locations2,imageViewsqure.getWidth(),imageViewsqure.getHeight(),uri2);
break;
}
}
下面是大图界面的代码:
public class PictureActivity extends Activity {
private ImageView imageView;
private boolean isAnimator = true;//是不是使用属性动画,推荐
private ImageView smallImage;
@RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//去除activity进入动画
overridePendingTransition(0, 0);
//使用下面两句实现全屏,在全屏界面过渡到非全屏界面会有界面抖动问题,所以没有用这
//种方法
// requestWindowFeature(Window.FEATURE_NO_TITLE);
// getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
//大于android4.4及以上全屏显示
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getWindow().setStatusBarColor(Color.TRANSPARENT);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getWindow()
.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}
setContentView(R.layout.activity_picture);
imageView = (ImageView) findViewById(R.id.imageView);
if (isAnimator) {
PictureSacleAnimUtils.setUpImageView(this, imageView);
PictureSacleAnimUtils.enterAnimator(this, imageView);
} else {
//在动画前先绘制一个一模一样的ImageView,先把大图控件隐藏,这里用补间动画 ,
//动画完成后隐藏小图控件,显示大图控件。因为补间动画点击事件还是在原来的位置
smallImage = PictureSacleAnimUtils.copyImageView(this);
imageView.setVisibility(View.GONE);
PictureSacleAnimUtils.enterAnimation(this, smallImage, imageView);
}
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (isAnimator) {
PictureSacleAnimUtils.exitAnimator(PictureActivity.this, imageView);
} else {
PictureSacleAnimUtils.exitAnimation(PictureActivity.this, smallImage, imageView);
}
}
});
}
public static void startActivity(Context context, int[] locations, int width, int height, String url) {
Intent intent = new Intent(context, PictureActivity.class);
intent.putExtra(PictureSacleAnimUtils.LOCATIONS, locations);
intent.putExtra(PictureSacleAnimUtils.WIDTH, width);
intent.putExtra(PictureSacleAnimUtils.HEIGHT, height);
if (!TextUtils.isEmpty(url)) {
intent.putExtra(PictureSacleAnimUtils.PICTUREURI, url);
}
context.startActivity(intent);
}
@Override
protected void onPause() {
//去除activity离开动画
overridePendingTransition(0, 0);
super.onPause();
}
@Override
protected void onDestroy() {
super.onDestroy();
}
}
这里可以 isAnimator属性来选择是否使用属性动画还是补间动画。而且这个Activity要应用为透明Activity。需要在配置清单文件中配置PictureActivity的主题:
<style name="TranslucentTheme">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowContentOverlay">@null</item>
</style>
接下来我们来看看实现动画的帮助类:
/**
* Created by 刘信 on 2018/1/30.
*/
public class PictureSacleAnimUtils {
//位置
public static final String LOCATIONS = "locations";
public static final String WIDTH = "width";
public static final String HEIGHT = "height";
public static final String PICTUREURI = "picture_uri";
private static final int DURATION=300;//动画时间
/**
* 复制一个一模一样的ImageView
* 补间动画需要这个
* @param activity
* @return copy的ImageView
*/
public static ImageView copyImageView(Activity activity) {
Intent intent = activity.getIntent();
int[] locations = intent.getIntArrayExtra(LOCATIONS);
int width = intent.getIntExtra(WIDTH, 0);
int height = intent.getIntExtra(HEIGHT, 0);
String url = intent.getStringExtra(PICTUREURI);
View view = activity.getWindow().getDecorView().findViewById(android.R.id.content);
ImageView imageCopy = new ImageView(activity);
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(width, height);
layoutParams.leftMargin = locations[0];
layoutParams.topMargin = locations[1];
imageCopy.setLayoutParams(layoutParams);
Glide.with(activity.getApplicationContext())
.load(url)
.override(width, height)
.into(imageCopy);
((ViewGroup) view).addView(imageCopy);
return imageCopy;
}
/**
*
* 配置重新改变ImageView的大小和位置,使ImageView位置和大小和上个页面完全一致
* @param activity
*/
public static void setUpImageView(Activity activity,ImageView imageView){
Intent intent = activity.getIntent();
int[] locations = intent.getIntArrayExtra(LOCATIONS);
int width = intent.getIntExtra(WIDTH, 0);
int height = intent.getIntExtra(HEIGHT, 0);
String url = intent.getStringExtra(PICTUREURI);
//这里imageView的父控件是FrameLayout
FrameLayout.LayoutParams layoutParams=new FrameLayout.LayoutParams(width,height);
layoutParams.leftMargin=locations[0];
layoutParams.topMargin=locations[1];
imageView.setLayoutParams(layoutParams);
Glide.with(activity.getApplicationContext())
.load(url)
.override(width, height)
.into(imageView);
}
/**
* 进入动画 补间动画实现。
*
* @param activity
* @param smallImageview 要缩放的小图
* @param imageview 要展示点击的大图
*/
public static void enterAnimation(final Activity activity, final ImageView smallImageview, final ImageView imageview) {
Intent intent = activity.getIntent();
int[] locations = intent.getIntArrayExtra(LOCATIONS);
int width = intent.getIntExtra(WIDTH, 0);
int height = intent.getIntExtra(HEIGHT, 0);
final String url = intent.getStringExtra(PICTUREURI);
// 缩放后宽度是屏幕宽度,缩放后的宽度比
float scaleRatX = Utils.getScreenWidth(activity) / (float) width;
//缩放后的高度
float scaleHeight=Utils.getScreenWidth(activity)*height/(float)width;
//缩放后的高度比
float scaleRatY = scaleHeight / (float) height;
//位移,位移后x方向的距离比原来位置向左偏移了locations[0]
//y方向的偏移量,自己画图计算你就明白了
float translateY=locations[1]-((Utils.getScreenHeight(activity)-scaleHeight)/2.0f);
ScaleAnimation sacleAnimation = new ScaleAnimation(1, scaleRatX, 1, scaleRatY, ScaleAnimation.RELATIVE_TO_SELF, 0, ScaleAnimation.RELATIVE_TO_SELF, 0);
TranslateAnimation translateAnimation = new TranslateAnimation(0, -locations[0], 0, -translateY);
View view= activity.getWindow().getDecorView().findViewById(android.R.id.content);
view.setBackgroundColor(Color.BLACK);
/* AlphaAnimation alphaAnimation=new AlphaAnimation(1f,0f);
alphaAnimation.setFillAfter(true);
alphaAnimation.setInterpolator(new DecelerateInterpolator());
alphaAnimation.setDuration(DURATION);
view.startAnimation(alphaAnimation);*/
AnimationSet animationSet = new AnimationSet(true);
animationSet.setDuration(DURATION);
animationSet.setFillAfter(true);
animationSet.addAnimation(sacleAnimation);
animationSet.addAnimation(translateAnimation);
smallImageview.startAnimation(animationSet);
animationSet.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
imageview.setVisibility(View.VISIBLE);
Glide.with(activity.getApplicationContext())
.load(url)
.into(imageview);
smallImageview.setVisibility(View.GONE);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
}
/**
* android 3.0及以上适配
* 进入动画 属性动画实现
* 属性动画是真正的改变了图片位置和大小,通过改变对象的属性实现
*
* @param activity
* @param imageview 要缩放的图片
*
*/
@TargetApi(11)
public static void enterAnimator(final Activity activity, final ImageView imageview) {
Intent intent = activity.getIntent();
int[] locations = intent.getIntArrayExtra(LOCATIONS);
int width = intent.getIntExtra(WIDTH, 0);
int height = intent.getIntExtra(HEIGHT, 0);
final String url = intent.getStringExtra(PICTUREURI);
float scaleRatX = Utils.getScreenWidth(activity) / (float) width;
float scaleHeight=Utils.getScreenWidth(activity)*height/(float)width;
float scaleRatY = scaleHeight / (float) height;
float translateY=locations[1]-((Utils.getScreenHeight(activity)-scaleHeight)/2.0f);
imageview.setPivotX(0);//设置缩放中心点
imageview.setPivotY(0);
FloatEvaluator floatEvaluator=new FloatEvaluator();
ObjectAnimator animatorScaleX=ObjectAnimator.ofObject(imageview,"scaleX",floatEvaluator,1,scaleRatX);
ObjectAnimator animatorScaleY=ObjectAnimator.ofObject(imageview,"scaleY",floatEvaluator,1,scaleRatY);
ObjectAnimator animatorTranslateX=ObjectAnimator.ofObject(imageview,"translationX",floatEvaluator,0,-locations[0]);
ObjectAnimator animatorTranslateY=ObjectAnimator.ofObject(imageview,"translationY",floatEvaluator,0,-translateY);
View view= activity.getWindow().getDecorView().findViewById(android.R.id.content);
view.setBackgroundColor(Color.BLACK);
// ObjectAnimator animatorAlpha=ObjectAnimator.ofObject(view,"alpha",floatEvaluator,0f,1f);
// animatorAlpha.setInterpolator(new DecelerateInterpolator());
AnimatorSet animationSet = new AnimatorSet();
animationSet.setDuration(DURATION);
animationSet.playTogether(animatorScaleX,animatorScaleY,animatorTranslateX,animatorTranslateY);
animationSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
Glide.with(activity.getApplicationContext())
.load(url)
.into(imageview);
}
});
animationSet.start();
}
/**
* android 3.0及以上适配
* 进入动画 属性动画实现
* 属性动画是真正的改变了图片位置和大小,通过改变对象的属性实现
*
* @param activity
* @param imageview 要缩放的图片
*/
@TargetApi(11)
public static void exitAnimator(final Activity activity, final ImageView imageview) {
Intent intent = activity.getIntent();
int[] locations = intent.getIntArrayExtra(LOCATIONS);
int width = intent.getIntExtra(WIDTH, 0);
int height = intent.getIntExtra(HEIGHT, 0);
final String url = intent.getStringExtra(PICTUREURI);
float scaleRatX = Utils.getScreenWidth(activity) / (float) width;
float scaleHeight=Utils.getScreenWidth(activity)*height/(float)width;
float scaleRatY = scaleHeight / (float) height;
float translateY=locations[1]-((Utils.getScreenHeight(activity)-scaleHeight)/2.0f);
imageview.setPivotX(0);
imageview.setPivotY(0);
FloatEvaluator floatEvaluator=new FloatEvaluator();
ObjectAnimator animatorScaleX=ObjectAnimator.ofObject(imageview,"scaleX",floatEvaluator,scaleRatX,1);
ObjectAnimator animatorScaleY=ObjectAnimator.ofObject(imageview,"scaleY",floatEvaluator,scaleRatY,1);
ObjectAnimator animatorTranslateX=ObjectAnimator.ofObject(imageview,"translationX",floatEvaluator,-locations[0],0);
ObjectAnimator animatorTranslateY=ObjectAnimator.ofObject(imageview,"translationY",floatEvaluator,-translateY,0);
final ViewGroup view= (ViewGroup) activity.getWindow().getDecorView().findViewById(android.R.id.content);
ObjectAnimator animatorAlpha=ObjectAnimator.ofObject(view,"alpha",floatEvaluator,1f,0f);
AnimatorSet animationSet = new AnimatorSet();
animationSet.setDuration(DURATION);
animatorAlpha.setStartDelay(DURATION/4);
animationSet.playTogether(animatorScaleX,animatorScaleY,animatorTranslateX,animatorTranslateY,animatorAlpha);
animationSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
view.removeAllViews();
activity.finish();
}
});
animationSet.start();
}
/**
* 退出动画 补间动画实现
* @param activity
* @param smallImageview 要缩放的小图
* @param imageView 要展示点击的大图
*/
public static void exitAnimation(final Activity activity, final ImageView smallImageview, final ImageView imageView) {
Intent intent = activity.getIntent();
int[] locations = intent.getIntArrayExtra(LOCATIONS);
int width = intent.getIntExtra(WIDTH, 0);
int height = intent.getIntExtra(HEIGHT, 0);
final String url = intent.getStringExtra(PICTUREURI);
float scaleRatX = Utils.getScreenWidth(activity) / (float) width;
float scaleHeight=Utils.getScreenWidth(activity)*height/(float)width;
float scaleRatY = scaleHeight / (float) height;
float translateY=locations[1]-((Utils.getScreenHeight(activity)-scaleHeight)/2.0f);
ScaleAnimation sacleAnimation = new ScaleAnimation(scaleRatX, 1, scaleRatY, 1, ScaleAnimation.RELATIVE_TO_SELF, 0, ScaleAnimation.RELATIVE_TO_SELF, 0);
TranslateAnimation translateAnimation = new TranslateAnimation(-locations[0], 0, -translateY, 0);
final ViewGroup view= (ViewGroup) activity.getWindow().getDecorView().findViewById(android.R.id.content);
AlphaAnimation alphaAnimation=new AlphaAnimation(1f,0f);
alphaAnimation.setFillAfter(true);
alphaAnimation.setDuration(DURATION);
alphaAnimation.setStartOffset(DURATION/4);
alphaAnimation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
view.removeAllViews();
activity.finish();
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
view.startAnimation(alphaAnimation);
AnimationSet animationSet = new AnimationSet(true);
animationSet.setDuration(DURATION);
animationSet.setFillAfter(true);
animationSet.addAnimation(sacleAnimation);
animationSet.addAnimation(translateAnimation);
smallImageview.startAnimation(animationSet);
animationSet.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
smallImageview.setVisibility(View.VISIBLE);
imageView.setVisibility(View.GONE);
}
@Override
public void onAnimationEnd(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
}
}
上面动画帮助类需要用到的工具类方法如下:
/**
* 获得屏幕宽度
*
* @param context
* @return
*/
public static int getScreenWidth(Context context)
{
WindowManager wm = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(outMetrics);
return outMetrics.widthPixels;
}
/**
* 获得屏幕高度
* 小于android4.4去掉状态栏高度
* @param context
* @return
*/
public static int getScreenHeight(Context context)
{
WindowManager wm = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(outMetrics);
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.KITKAT){
return outMetrics.heightPixels;
}else {
return outMetrics.heightPixels-getStatusBarHeight(context);
}
}
public static int getStatusBarHeight(Context context) {
int result = 0;
try {
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = context.getResources().getDimensionPixelSize(resourceId);
}
}catch (Exception e){
e.printStackTrace();
}
return result;
}
其实这种效果实现起来还算简单,就是对于android 系统适配问题有点麻烦。还有一种方法来实现点击小图查看大图的效果,就是使用Transition来实现。这样只适配于android 5.0及以上系统。额。那就先这样了。