1. 程式人生 > >Android一點 仿淘寶購物車動畫

Android一點 仿淘寶購物車動畫

首先看看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彈出層的封裝,對於不同的業務和介面都可以輕鬆的實現了