首先看看ios上的淘宝购物车的动画效果ios淘宝购物车动画
我们实现的效果
看特效是分为两个界面,一个是主view,一个是弹出层。弹出层是用dialog实现的,只是加入了弹出的动画,这里就不分析了,我们主要看主view的动画是怎么实现的,初看好像只是缩放了一点,但是又带着点其它炫耀的动画,其实也是通过旋转、偏移等动画组合的效果。看看动画具体的实现,
1、进入的动画
/**
* 缩放xy,但是y缩放的比例比较小
*/
ObjectAnimator fViewScaleXAnim=ObjectAnimator.ofFloat(this,"scaleX",1.0f,0.8f);
fViewScaleXAnim.setDuration(350);
ObjectAnimator fViewScaleYAnim=ObjectAnimator.ofFloat(this,"scaleY",1.0f,0.9f);
fViewScaleYAnim.setDuration(350);
/**
* 重点特效
* 通过x的双重旋转
*/
ObjectAnimator fViewRotationXAnim = ObjectAnimator.ofFloat(this, "rotationX", 0f, 10f);
fViewRotationXAnim.setDuration(200);
ObjectAnimator fViewResumeAnim = ObjectAnimator.ofFloat(this, "rotationX", 10f, 0f);
fViewResumeAnim.setDuration(150);
fViewResumeAnim.setStartDelay(200);
/**
* y需要的偏移量
*/
ObjectAnimator fViewTransYAnim=ObjectAnimator.ofFloat(this,"translationY",0,-0.01f* height);
fViewTransYAnim.setDuration(350);
2、退出的动画(退出动画是和进入动画相反的)
ObjectAnimator fViewScaleXAnim=ObjectAnimator.ofFloat(this,"scaleX",0.8f,1.0f);
fViewScaleXAnim.setDuration(350);
ObjectAnimator fViewScaleYAnim=ObjectAnimator.ofFloat(this,"scaleY",0.9f,1.0f);
fViewScaleYAnim.setDuration(350);
ObjectAnimator fViewRotationXAnim = ObjectAnimator.ofFloat(this, "rotationX", 0f, 10f);
fViewRotationXAnim.setDuration(200);
ObjectAnimator fViewResumeAnim = ObjectAnimator.ofFloat(this, "rotationX", 10f, 0f);
fViewResumeAnim.setDuration(150);
fViewResumeAnim.setStartDelay(200);
ObjectAnimator fViewTransYAnim=ObjectAnimator.ofFloat(this,"translationY",-0.01f* height,0);
fViewTransYAnim.setDuration(350);
2、下面开始封装开发我们的ShopAnimatorView(仿淘宝购物车动画View)
ShopAnimatorView.java
package com.flyjun.shopanimator.view;
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.util.AttributeSet;
import android.view.WindowManager;
import android.widget.RelativeLayout;
public class ShopAnimatorView extends RelativeLayout{
private int width;
private int height;
private AnimatorSet showAnim;
private AnimatorSet hiddenAnim;
private onShopAnimatorListener shopAnimatorListener;
public ShopAnimatorView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// TODO Auto-generated constructor stub
this.init();
}
public ShopAnimatorView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
this.init();
}
public ShopAnimatorView(Context context) {
super(context);
// TODO Auto-generated constructor stub
this.init();
}
/**
* 初始化操作
*/
private void init(){
WindowManager wm = (WindowManager) getContext()
.getSystemService(Context.WINDOW_SERVICE);
width = wm.getDefaultDisplay().getWidth();
height = wm.getDefaultDisplay().getHeight();
initShowAnim();
initHiddenAnim();
}
/**
* 进场动画
*/
private void initShowAnim(){
/**
* 缩放xy,但是y缩放的比例比较小
*/
ObjectAnimator fViewScaleXAnim=ObjectAnimator.ofFloat(this,"scaleX",1.0f,0.8f);
fViewScaleXAnim.setDuration(350);
ObjectAnimator fViewScaleYAnim=ObjectAnimator.ofFloat(this,"scaleY",1.0f,0.9f);
fViewScaleYAnim.setDuration(350);
/**
* 重点特效
* 通过x的双重旋转
*/
ObjectAnimator fViewRotationXAnim = ObjectAnimator.ofFloat(this, "rotationX", 0f, 10f);
fViewRotationXAnim.setDuration(200);
ObjectAnimator fViewResumeAnim = ObjectAnimator.ofFloat(this, "rotationX", 10f, 0f);
fViewResumeAnim.setDuration(150);
fViewResumeAnim.setStartDelay(200);
/**
* y需要的偏移量
*/
ObjectAnimator fViewTransYAnim=ObjectAnimator.ofFloat(this,"translationY",0,-0.01f* height);
fViewTransYAnim.setDuration(350);
showAnim=new AnimatorSet();
showAnim.playTogether(fViewScaleXAnim,fViewRotationXAnim,fViewResumeAnim,fViewTransYAnim,fViewScaleYAnim);
/**
* 动画开始时可以 显示弹出层
*/
showAnim.addListener(new AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
// TODO Auto-generated method stub
if(shopAnimatorListener != null){
shopAnimatorListener.onShowView();
}
}
@Override
public void onAnimationRepeat(Animator animation) {
// TODO Auto-generated method stub
}
@Override
public void onAnimationEnd(Animator animation) {
// TODO Auto-generated method stub
}
@Override
public void onAnimationCancel(Animator animation) {
// TODO Auto-generated method stub
}
});
}
/**
* 退出动画
*/
private void initHiddenAnim(){
ObjectAnimator fViewScaleXAnim=ObjectAnimator.ofFloat(this,"scaleX",0.8f,1.0f);
fViewScaleXAnim.setDuration(350);
ObjectAnimator fViewScaleYAnim=ObjectAnimator.ofFloat(this,"scaleY",0.9f,1.0f);
fViewScaleYAnim.setDuration(350);
ObjectAnimator fViewRotationXAnim = ObjectAnimator.ofFloat(this, "rotationX", 0f, 10f);
fViewRotationXAnim.setDuration(200);
ObjectAnimator fViewResumeAnim = ObjectAnimator.ofFloat(this, "rotationX", 10f, 0f);
fViewResumeAnim.setDuration(150);
fViewResumeAnim.setStartDelay(200);
ObjectAnimator fViewTransYAnim=ObjectAnimator.ofFloat(this,"translationY",-0.01f* height,0);
fViewTransYAnim.setDuration(350);
hiddenAnim=new AnimatorSet();
hiddenAnim.playTogether(fViewScaleXAnim,fViewRotationXAnim,fViewResumeAnim,fViewTransYAnim,fViewScaleYAnim);
/**
* 动画开始时可以 关闭弹出层
*/
hiddenAnim.addListener(new AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
// TODO Auto-generated method stub
if(shopAnimatorListener != null){
shopAnimatorListener.onCancelView();
}
}
@Override
public void onAnimationRepeat(Animator animation) {
// TODO Auto-generated method stub
}
@Override
public void onAnimationEnd(Animator animation) {
// TODO Auto-generated method stub
}
@Override
public void onAnimationCancel(Animator animation) {
// TODO Auto-generated method stub
}
});
}
/**
* 开始进入动画
*/
public void startShowAnim(){
showAnim.start();
}
/**
* 开始退出动画
*/
public void startHiddenAnim(){
hiddenAnim.start();
}
/**
* 设置什么显示弹出层和关闭弹出层的监听器
*/
public void setOnShopAnimatorListener(onShopAnimatorListener shopAnimatorListener){
this.shopAnimatorListener=shopAnimatorListener;
}
public interface onShopAnimatorListener{
public void onShowView();
public void onCancelView();
}
}
3、弹出层的封装(实现用的是dialog)
ShopAnimatorDialog.java
package com.flyjun.shopanimator.view;
import com.flyjun.shopanimator.R;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.Gravity;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
public abstract class ShopAnimatorDialog extends Dialog{
private Context context;
private ShopAnimatorView shopAnimatorView;
public ShopAnimatorDialog(Context context,ShopAnimatorView shopAnimatorView) {
super(context);
// TODO Auto-generated constructor stub
this.context=context;
this.shopAnimatorView=shopAnimatorView;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
init();
}
private void init(){
/**
* 弹出的动画
*/
getWindow().setWindowAnimations(R.style.MyDialogAnimation);
setCanceledOnTouchOutside(false);
/**
* 设置在dialog关闭时恢复动画
*/
this.setOnCancelListener(new OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
// TODO Auto-generated method stub
shopAnimatorView.startHiddenAnim();
}
});
setContentView(getContentView());
android.view.WindowManager.LayoutParams ll=getWindow().getAttributes();
ll.width=WindowManager.LayoutParams.MATCH_PARENT;
ll.gravity=Gravity.BOTTOM;
getWindow().setAttributes(ll);
}
/**
* 返回ContentView(视图view)
* @return
*/
public abstract View getContentView();
public void showDialog(){
if(!isShowing()){
show();
}
}
public void cancelDialog(){
if(isShowing()){
cancel();
}
}
}
弹出层动画
<style name="MyDialogAnimation">
<!--进入 -->
<item name="android:windowEnterAnimation">@anim/dialog_enter_anim</item>
<!--退出-->
<item name="android:windowExitAnimation">@anim/dialog_exit_anim</item>
</style>
dialog_enter_anim.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromYDelta="100%"
android:toYDelta="0"
android:duration="300"></translate>
</set>
dialog_exit_anim.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromYDelta="0"
android:toYDelta="100%"
android:duration="300"/>
</set>
4、demo代码,测试的代码就变得简单了
主view的布局
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/background_dark"
>
<com.flyjun.shopanimator.view.ShopAnimatorView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/shopLayout"
android:background="@android:color/white">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello"
android:textSize="20sp"
/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="加入购物车"
android:id="@+id/add"
android:layout_alignParentBottom="true"
android:layout_marginBottom="10dp"/>
</com.flyjun.shopanimator.view.ShopAnimatorView>
</RelativeLayout>
MainActivity.java
package com.flyjun.shopanimator;
import com.flyjun.shopanimator.view.ShopAnimatorView;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.view.WindowManager;
public class MainActivity extends Activity {
private ShopAnimatorView shopAnimatorView;
private TestDialog testDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 隐藏标题栏
requestWindowFeature(Window.FEATURE_NO_TITLE);
// 隐藏状态栏
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_main);
this.shopAnimatorView=(ShopAnimatorView) findViewById(R.id.shopLayout);
this.testDialog=new TestDialog(this, shopAnimatorView);
findViewById(R.id.add).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
shopAnimatorView.startShowAnim();
testDialog.showDialog();
}
});
}
@Override
public void onBackPressed() {
// TODO Auto-generated method stub
// super.onBackPressed();
// shopAnimatorView.startHiddenAnim();
testDialog.cancelDialog();
}
}
测试的弹出层
TestDialog.java
package com.flyjun.shopanimator;
import com.flyjun.shopanimator.view.ShopAnimatorDialog;
import com.flyjun.shopanimator.view.ShopAnimatorView;
import android.content.Context;
import android.os.Bundle;
import android.view.Gravity;
import android.view.View;
import android.view.WindowManager;
public class TestDialog extends ShopAnimatorDialog{
public TestDialog(Context context, ShopAnimatorView shopAnimatorView) {
super(context, shopAnimatorView);
// TODO Auto-generated constructor stub
}
@Override
public View getContentView() {
// TODO Auto-generated method stub
return View.inflate(getContext(), R.layout.test_dialog, null);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
findViewById(R.id.close).setOnClickListener(new android.view.View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
cancelDialog();
}
});
}
}
仿淘宝购物车动画完成,厉害了Flyjun哥!!!
通过ShopAnimatorView的封装和ShopAnimatorDialog弹出层的封装,对于不同的业务和界面都可以轻松的实现了