Android特效開發(可伸縮View帶互相擠壓效果)進階篇

在上一篇文章末尾我提出瞭三點不足 ,遂本篇主要是為瞭解決上篇的不足之處。

對於上一篇的不足之處 有三點 :

     1. 特效動畫死板,變化速度死板;

     2. 特效動畫不能設置動畫時間,如遇到高分辨率的機型,動畫時間會變長。

     3. view隻能水平伸縮,不能豎直伸縮。

 

對於第一點不足之處變化速度死板,我立馬想到瞭Android中Interpolator類,對於做過Android中動畫的同學

來說,這個類應該並不陌生,該類可以改變動畫的變化速率,它的直接子類中有

      BounceInterpolator  彈球效果 

      AccelerateInterpolator 加速

     LinearInterpolator 勻速

更多子類可請查閱Android開發文檔

它有個getInterpolation (float input) 方法,你可以傳入動畫消逝時間值(input范圍 [0,1] ),0代表開始,1代表

結束,獲取變化速率。等會兒代碼中有用到這個類。

有關插值器可參考: android動畫(一)Interpolator

對於第一二三點不足,我寫瞭輔助類StretchAnimation可以解決。歡迎批評指正。

StretchAnimation隻負責view水平拉伸或者垂直拉伸。你可以設置動畫的時間,你可以設置它的插值器,改變動

畫的效果。下面該類的實現過程。

[java]  

public class StretchAnimation {  

  

    private final static String TAG = "SizeChange";  

    private Interpolator mInterpolator; // 好多書上翻譯為插值器  

    private View mView;    // 你要伸縮的view  

    private int mCurrSize; //當前大小  

    private int mRawSize;  

    private int mMinSize; // 最小大小 固定值  

    private int mMaxSize; // 最大大小 固定值  

    private boolean isFinished = true;// 動畫結束標識  

    private TYPE mType = TYPE.vertical;  

    private final static int FRAMTIME = 20;// 一幀的時間 毫秒  

    public static enum TYPE {  

        horizontal, // 改變view水平方向的大小  

        vertical    // 改變view豎直方向的大小  

    }  

  

    private int mDuration;  // 動畫運行的時間  

    private long mStartTime;// 動畫開始時間  

    private float mDurationReciprocal;   

    private int mDSize; // 需要改變view大小的增量  

  

    public StretchAnimation(int maxSize, int minSize, TYPE type, int duration) {  

        if (minSize >= maxSize) {  

            throw new RuntimeException("View的最大改變值不能小於最小改變值");  

        }  

        mMinSize = minSize;  

        mMaxSize = maxSize;  

        mType = type;  

        mDuration = duration;  

    }  

  

    public void setInterpolator(Interpolator interpolator) {  

        mInterpolator = interpolator;  

    }  

  

    public TYPE getmType() {  

        return mType;  

    }  

  

    public boolean isFinished() {  

        return isFinished;  

    }  

  

    public void setDuration(int duration) {  

        mDuration = duration;  

    }  

  

    private void changeViewSize() {  

  

        if (mView != null && mView.getVisibility() != View.GONE) {  

            LayoutParams params = mView.getLayoutParams();  

            if (mType == TYPE.vertical) {  

                params.height = mCurrSize;  

            } else if (mType == TYPE.horizontal) {  

                params.width = mCurrSize;  

            }  

            Log.i(TAG, "CurrSize = " + mCurrSize + " Max=" + mMaxSize + " min="  

                    + mMinSize);  

            mView.setLayoutParams(params);  

        }  

    }  

  

    private Handler mHandler = new Handler() {  

  

        @Override  

        public void handleMessage(Message msg) {  

  

            if (msg.what == 1) {  

                if (!computeViewSize()) {  

  

                    mHandler.sendEmptyMessageDelayed(1, FRAMTIME);  

                } else {  

                    if (animationlistener != null) {  

                        animationlistener.animationEnd(mView);  

                    }  

                }  

            }  

            super.handleMessage(msg);  

        }  

  

    };  

  

    /** 

     * @return 返回true 表示動畫完成 

     */  

    private boolean computeViewSize() {  

  

        if (isFinished) {  

            return isFinished;  

        }  

        int timePassed = (int) (AnimationUtils.currentAnimationTimeMillis() – mStartTime);  

  

        if (timePassed <= mDuration) {  

            float x = timePassed * mDurationReciprocal;  

            if (mInterpolator != null) {  

                x = mInterpolator.getInterpolation(x);  

            }  

            mCurrSize = mRawSize + Math.round(x * mDSize);  

        } else {  

  

            isFinished = true;  

            mCurrSize = mRawSize + mDSize;  

  

        }  

        changeViewSize();  

        return isFinished;  

    }  

  

    public void startAnimation(View view) {  

  

        if (view != null) {  

            mView = view;  

        } else {  

            Log.e(TAG, "view 不能為空");  

            return;  

        }  

        LayoutParams params = mView.getLayoutParams();  

  

        if (isFinished) {  

            mDurationReciprocal = 1.0f / (float) mDuration;  

            if (mType == TYPE.vertical) {  

                mRawSize = mCurrSize = mView.getHeight();  

            } else if (mType == TYPE.horizontal) {  

                mRawSize = mCurrSize = mView.getWidth();  

            }  

            Log.i(TAG, "mRawSize=" + mRawSize);  

            if (mCurrSize > mMaxSize || mCurrSize < mMinSize) {  

                throw new RuntimeException(  

                        "View 的大小不達標 currentViewSize > mMaxSize || currentViewSize < mMinSize");  

            }  

            isFinished = false;  

            mStartTime = AnimationUtils.currentAnimationTimeMillis(); // 動畫開始時間  

            if (mCurrSize < mMaxSize) {  

                mDSize = mMaxSize – mCurrSize;  

            } else {  

                mDSize = mMinSize – mMaxSize;  

            }  

            Log.i(TAG, "mDSize=" + mDSize);  

            mHandler.sendEmptyMessage(1);  

        }  

    }  

  

    private AnimationListener animationlistener;  

  

    interface AnimationListener {  

        public void animationEnd(View v);  

    }  

  

    public void setOnAnimationListener(AnimationListener listener) {  

        animationlistener = listener;  

    }  

 

初始化該類後再調用startAnimation 就可以播放動畫。

原理補充:每次開始播放動畫時你要知道需要改變的值是多少,我上面是用mDSize表示,然後根據時間的消逝值除以你設置的動畫要播放的時間值得到結果X,你再通過Interpolation.getInterpolation(x)就可以得到變化速率,變化速率乘以mDSize,就可以得到此時時的View大小改變量瞭。改變量曉得瞭,你就可以算出view的此時的大小瞭。

下面是我在activity中使用StretchAnimation的過程

[java]  

public class StretchActivity extends Activity implements   

                   View.OnClickListener,  

                   StretchAnimation.AnimationListener {  

  

    private final static String TAG = "StretchActivity";  

      

    // 屏幕寬度  

    private int screentWidth = 0;  

      

    private int screentHeight = 0;  

      

    // View可伸展最長的寬度  

    private int maxSize;  

      

    // View可伸展最小寬度  

    private int minSize;  

      

    // 當前點擊的View  

    private View currentView;  

      

    // 顯示最長的那個View  

    private View preView;  

      

    // 主佈局ViewGroup  

    private LinearLayout mainContain;  

      

    private StretchAnimation stretchanimation;  

      

    private TextView tvLog;  

  

    @Override  

    protected void onCreate(Bundle savedInstanceState)   

    {  

  

        super.onCreate(savedInstanceState);  

          

        setContentView(R.layout.activity_main);  

  

        mainContain = (LinearLayout) this.findViewById(R.id.main_contain);  

          

        initCommonData();  

          

        initViewData(2);  

          

    }  

  

    /** 

     * @param index 初始化時哪一個是最大的 從零開始 

     */  

    private void initViewData(int index) {  

  

        tvLog = (TextView)this.findViewById(R.id.tv_log);  

        View child;  

        int sizeValue = 0;  

        LayoutParams params = null;  

        int childCount = mainContain.getChildCount();  

        if(index <0 || index >= childCount)  

        {  

            throw new RuntimeException("index 超出范圍");  

        }  

          

        for (int i = 0; i < childCount; i++) {  

              

            child = mainContain.getChildAt(i);  

            child.setOnClickListener(this);  

            params = child.getLayoutParams();  

              

            if (i == index) {  

                preView = child;  

                sizeValue = maxSize;  

            } else {  

                sizeValue = minSize;  

            }  

            if(stretchanimation.getmType() == com.manymore13.Stretch.StretchAnimation.TYPE.horizontal){  

                params.width = sizeValue;  

            }else if(stretchanimation.getmType() == com.manymore13.Stretch.StretchAnimation.TYPE.vertical){  

                params.height = sizeValue;  

            }  

  

            child.setLayoutParams(params);  

        }  

          

    }  

      

    private void initCommonData()  

    {  

        DisplayMetrics metric = new DisplayMetrics();  

        getWindowManager().getDefaultDisplay().getMetrics(metric);  

        screentWidth = metric.widthPixels; // 屏幕寬度(像素)  

        screentHeight= metric.heightPixels;  

        //  

        measureSize(screentHeight);  

        stretchanimation = new StretchAnimation(maxSize, minSize, StretchAnimation.TYPE.vertical, 500);  

        stretchanimation.setInterpolator(new BounceInterpolator());  

        stretchanimation.setDuration(800);  

        stretchanimation.setOnAnimationListener(this);  

    }  

  

  

    /** 

     * 測量View 的 max min 長度  這裡你可以根據你的要求設置max 

     * @param screenSize 

     * @param index 從零開始 

     */  

    private void measureSize(int layoutSize) {  

        int halfWidth = layoutSize / 2;  

        maxSize = halfWidth – 50;  

        minSize = (layoutSize – maxSize) / (mainContain.getChildCount() – 1);  

          

        Log.i(TAG, "maxWidth="+maxSize+" minWidth = "+minSize);  

          

    }  

  

    @Override  

    public void onClick(View v) {  

          

        int id = v.getId();  

        View tempView = null;  

        switch (id) {  

          

        case R.id.btnOne:  

            tempView = mainContain.getChildAt(0);  

            break;  

        case R.id.btnTwo:  

            tempView = mainContain.getChildAt(1);  

            break;  

        case R.id.btnThree:  

            tempView = mainContain.getChildAt(2);  

            break;  

        case R.id.btnFour:  

            tempView = mainContain.getChildAt(3);  

            break;  

        }  

        if(tempView == preView){  

            Log.d(TAG, "");  

            String addInfo = ((Button) currentView).getText().toString()+"動畫不能執行";  

            printAddViewDebugInfo(addInfo);  

            return;  

        }else{  

            currentView = tempView;  

        }  

        Log.i(TAG, ((Button) currentView).getText().toString() + " click");  

        clickEvent(currentView);  

        onOffClickable(false);  

        String addInfo = ((Button) currentView).getText().toString()+"start animation";  

        printAddViewDebugInfo(addInfo);  

        stretchanimation.startAnimation(currentView);  

  

  

    }  

      

    private void clickEvent(View view) {  

        View child;  

        int childCount = mainContain.getChildCount();  

        LinearLayout.LayoutParams params;  

        for (int i = 0; i < childCount; i++) {  

            child = mainContain.getChildAt(i);  

            if (preView == child) {  

                params = (android.widget.LinearLayout.LayoutParams) child  

                        .getLayoutParams();  

                  

                if(preView != view){  

                    params.weight = 1.0f;  

                }  

                child.setLayoutParams(params);  

                  

            } else {  

                params = (android.widget.LinearLayout.LayoutParams) child  

                        .getLayoutParams();  

                params.weight = 0.0f;  

                if(stretchanimation.getmType() == StretchAnimation.TYPE.horizontal){  

                    params.width = minSize;  

                }else if(stretchanimation.getmType() == StretchAnimation.TYPE.vertical){  

                    params.height = minSize;  

                }  

                  

                child.setLayoutParams(params);  

            }  

        }  

        preView = view;  

          

    }  

      

    // 調試信息  

    private void printDebugMsg() {  

        View child;  

        int childCount = mainContain.getChildCount();  

        StringBuilder sb = new StringBuilder();  

        sb.append("preView = "+((Button)preView).getText().toString()+" ");  

        sb.append("click = "+((Button)currentView).getText().toString()+" ");  

        for (int i = 0; i < childCount; i++) {  

            child = mainContain.getChildAt(i);  

            LinearLayout.LayoutParams params = (android.widget.LinearLayout.LayoutParams) child  

                    .getLayoutParams();  

            sb.append(params.weight+" ");  

        }  

        Log.d(TAG, sb.toString());  

    }  

      

    // LinearLayout下所有childView 可點擊開關  

    // 當動畫在播放時應該設置為不可點擊,結束時設置為可點擊  

    private void onOffClickable(boolean isClickable)  

    {  

        View child;  

        int childCount = mainContain.getChildCount();  

        for (int i = 0; i < childCount; i++) {  

            child = mainContain.getChildAt(i);  

            child.setClickable(isClickable);  

        }  

    }  

  

    @Override  

    public void animationEnd(View v) {  

          

        Log.i(TAG, ("—–"+((Button)v).getText().toString())+" annation end");  

        String addStr = ((Button)v).getText().toString()+" annation end";  

        printAddViewDebugInfo(addStr);  

        onOffClickable(true);  

    }  

      

    private void printAddViewDebugInfo(String addinfo)  

    {  

        String temp = tvLog.getText().toString();  

        tvLog.setText(temp+"\n"+addinfo);  

    }  

在上面代碼中可以看到stretchanimation 的初始化與調用

初始化stretchanimation 

// 我這裡設置的View是垂直伸縮動畫,maxSIze是伸縮的最大值,minSize是伸縮的最小值,500是500毫秒的動畫時間

// 註意:你這裡設置StretchAnimation.TYPE.vertical垂直伸縮動畫,你XML中相應View佈局也應該是垂直,

[java]  

stretchanimation = new StretchAnimation(maxSize, minSize, StretchAnimation.TYPE.vertical, 500);  

// 設置它的插值器 彈球效果  

stretchanimation.setInterpolator(new BounceInterpolator());  

// 動畫播放的總時間  

stretchanimation.setDuration(800);  

// 動畫播放完後的回調  

stretchanimation.setOnAnimationListener(this);  

// 播放動畫 參數是你要播放的View  

stretchanimation.startAnimation(currentView)  

 

發佈留言