android 隨手記 ——android view特效開發(可伸縮view帶互相擠壓效果)

本文由manymore13原創,轉載請標明出處http://blog.csdn.net/manymore13/article/details/12907969


上一篇  Android特效開發(可伸縮View帶互相擠壓效果 )初級篇  

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

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

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

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

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


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

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

      BounceInterpolator  彈球效果 

      AccelerateInterpolator 加速

     LinearInterpolator 勻速

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

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

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

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

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

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

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

  1. public class StretchAnimation {  
  2.   
  3.     private final static String TAG = "SizeChange";  
  4.     private Interpolator mInterpolator; // 好多書上翻譯爲插值器  
  5.     private View mView;    // 你要伸縮的view  
  6.     private int mCurrSize; //當前大小  
  7.     private int mRawSize;  
  8.     private int mMinSize; // 最小大小 固定值  
  9.     private int mMaxSize; // 最大大小 固定值  
  10.     private boolean isFinished = true;// 動畫結束標識  
  11.     private TYPE mType = TYPE.vertical;  
  12.     private final static int FRAMTIME = 20;// 一幀的時間 毫秒  
  13.     public static enum TYPE {  
  14.         horizontal, // 改變view水平方向的大小  
  15.         vertical    // 改變view豎直方向的大小  
  16.     }  
  17.   
  18.     private int mDuration;  // 動畫運行的時間  
  19.     private long mStartTime;// 動畫開始時間  
  20.     private float mDurationReciprocal;   
  21.     private int mDSize; // 需要改變view大小的增量  
  22.   
  23.     public StretchAnimation(int maxSize, int minSize, TYPE type, int duration) {  
  24.         if (minSize >= maxSize) {  
  25.             throw new RuntimeException("View的最大改變值不能小於最小改變值");  
  26.         }  
  27.         mMinSize = minSize;  
  28.         mMaxSize = maxSize;  
  29.         mType = type;  
  30.         mDuration = duration;  
  31.     }  
  32.   
  33.     public void setInterpolator(Interpolator interpolator) {  
  34.         mInterpolator = interpolator;  
  35.     }  
  36.   
  37.     public TYPE getmType() {  
  38.         return mType;  
  39.     }  
  40.   
  41.     public boolean isFinished() {  
  42.         return isFinished;  
  43.     }  
  44.   
  45.     public void setDuration(int duration) {  
  46.         mDuration = duration;  
  47.     }  
  48.   
  49.     private void changeViewSize() {  
  50.   
  51.         if (mView != null && mView.getVisibility() != View.GONE) {  
  52.             LayoutParams params = mView.getLayoutParams();  
  53.             if (mType == TYPE.vertical) {  
  54.                 params.height = mCurrSize;  
  55.             } else if (mType == TYPE.horizontal) {  
  56.                 params.width = mCurrSize;  
  57.             }  
  58.             Log.i(TAG, "CurrSize = " + mCurrSize + " Max=" + mMaxSize + " min="  
  59.                     + mMinSize);  
  60.             mView.setLayoutParams(params);  
  61.         }  
  62.     }  
  63.   
  64.     private Handler mHandler = new Handler() {  
  65.   
  66.         @Override  
  67.         public void handleMessage(Message msg) {  
  68.   
  69.             if (msg.what == 1) {  
  70.                 if (!computeViewSize()) {  
  71.   
  72.                     mHandler.sendEmptyMessageDelayed(1, FRAMTIME);  
  73.                 } else {  
  74.                     if (animationlistener != null) {  
  75.                         animationlistener.animationEnd(mView);  
  76.                     }  
  77.                 }  
  78.             }  
  79.             super.handleMessage(msg);  
  80.         }  
  81.   
  82.     };  
  83.   
  84.     /** 
  85.      * @return 返回true 表示動畫完成 
  86.      */  
  87.     private boolean computeViewSize() {  
  88.   
  89.         if (isFinished) {  
  90.             return isFinished;  
  91.         }  
  92.         int timePassed = (int) (AnimationUtils.currentAnimationTimeMillis() - mStartTime);  
  93.   
  94.         if (timePassed <= mDuration) {  
  95.             float x = timePassed * mDurationReciprocal;  
  96.             if (mInterpolator != null) {  
  97.                 x = mInterpolator.getInterpolation(x);  
  98.             }  
  99.             mCurrSize = mRawSize + Math.round(x * mDSize);  
  100.         } else {  
  101.   
  102.             isFinished = true;  
  103.             mCurrSize = mRawSize + mDSize;  
  104.   
  105.         }  
  106.         changeViewSize();  
  107.         return isFinished;  
  108.     }  
  109.   
  110.     public void startAnimation(View view) {  
  111.   
  112.         if (view != null) {  
  113.             mView = view;  
  114.         } else {  
  115.             Log.e(TAG, "view 不能爲空");  
  116.             return;  
  117.         }  
  118.         LayoutParams params = mView.getLayoutParams();  
  119.   
  120.         if (isFinished) {  
  121.             mDurationReciprocal = 1.0f / (float) mDuration;  
  122.             if (mType == TYPE.vertical) {  
  123.                 mRawSize = mCurrSize = mView.getHeight();  
  124.             } else if (mType == TYPE.horizontal) {  
  125.                 mRawSize = mCurrSize = mView.getWidth();  
  126.             }  
  127.             Log.i(TAG, "mRawSize=" + mRawSize);  
  128.             if (mCurrSize > mMaxSize || mCurrSize < mMinSize) {  
  129.                 throw new RuntimeException(  
  130.                         "View 的大小不達標 currentViewSize > mMaxSize || currentViewSize < mMinSize");  
  131.             }  
  132.             isFinished = false;  
  133.             mStartTime = AnimationUtils.currentAnimationTimeMillis(); // 動畫開始時間  
  134.             if (mCurrSize < mMaxSize) {  
  135.                 mDSize = mMaxSize - mCurrSize;  
  136.             } else {  
  137.                 mDSize = mMinSize - mMaxSize;  
  138.             }  
  139.             Log.i(TAG, "mDSize=" + mDSize);  
  140.             mHandler.sendEmptyMessage(1);  
  141.         }  
  142.     }  
  143.   
  144.     private AnimationListener animationlistener;  
  145.   
  146.     interface AnimationListener {  
  147.         public void animationEnd(View v);  
  148.     }  
  149.   
  150.     public void setOnAnimationListener(AnimationListener listener) {  
  151.         animationlistener = listener;  
  152.     }  

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

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

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

  1. public class StretchActivity extends Activity implements   
  2.                    View.OnClickListener,  
  3.                    StretchAnimation.AnimationListener {  
  4.   
  5.     private final static String TAG = "StretchActivity";  
  6.       
  7.     // 屏幕寬度  
  8.     private int screentWidth = 0;  
  9.       
  10.     private int screentHeight = 0;  
  11.       
  12.     // View可伸展最長的寬度  
  13.     private int maxSize;  
  14.       
  15.     // View可伸展最小寬度  
  16.     private int minSize;  
  17.       
  18.     // 當前點擊的View  
  19.     private View currentView;  
  20.       
  21.     // 顯示最長的那個View  
  22.     private View preView;  
  23.       
  24.     // 主佈局ViewGroup  
  25.     private LinearLayout mainContain;  
  26.       
  27.     private StretchAnimation stretchanimation;  
  28.       
  29.     private TextView tvLog;  
  30.   
  31.     @Override  
  32.     protected void onCreate(Bundle savedInstanceState)   
  33.     {  
  34.   
  35.         super.onCreate(savedInstanceState);  
  36.           
  37.         setContentView(R.layout.activity_main);  
  38.   
  39.         mainContain = (LinearLayout) this.findViewById(R.id.main_contain);  
  40.           
  41.         initCommonData();  
  42.           
  43.         initViewData(2);  
  44.           
  45.     }  
  46.   
  47.     /** 
  48.      * @param index 初始化時哪一個是最大的 從零開始 
  49.      */  
  50.     private void initViewData(int index) {  
  51.   
  52.         tvLog = (TextView)this.findViewById(R.id.tv_log);  
  53.         View child;  
  54.         int sizeValue = 0;  
  55.         LayoutParams params = null;  
  56.         int childCount = mainContain.getChildCount();  
  57.         if(index <0 || index >= childCount)  
  58.         {  
  59.             throw new RuntimeException("index 超出範圍");  
  60.         }  
  61.           
  62.         for (int i = 0; i < childCount; i++) {  
  63.               
  64.             child = mainContain.getChildAt(i);  
  65.             child.setOnClickListener(this);  
  66.             params = child.getLayoutParams();  
  67.               
  68.             if (i == index) {  
  69.                 preView = child;  
  70.                 sizeValue = maxSize;  
  71.             } else {  
  72.                 sizeValue = minSize;  
  73.             }  
  74.             if(stretchanimation.getmType() == com.manymore13.Stretch.StretchAnimation.TYPE.horizontal){  
  75.                 params.width = sizeValue;  
  76.             }else if(stretchanimation.getmType() == com.manymore13.Stretch.StretchAnimation.TYPE.vertical){  
  77.                 params.height = sizeValue;  
  78.             }  
  79.   
  80.             child.setLayoutParams(params);  
  81.         }  
  82.           
  83.     }  
  84.       
  85.     private void initCommonData()  
  86.     {  
  87.         DisplayMetrics metric = new DisplayMetrics();  
  88.         getWindowManager().getDefaultDisplay().getMetrics(metric);  
  89.         screentWidth = metric.widthPixels; // 屏幕寬度(像素)  
  90.         screentHeight= metric.heightPixels;  
  91.         //  
  92.         measureSize(screentHeight);  
  93.         stretchanimation = new StretchAnimation(maxSize, minSize, StretchAnimation.TYPE.vertical, 500);  
  94.         stretchanimation.setInterpolator(new BounceInterpolator());  
  95.         stretchanimation.setDuration(800);  
  96.         stretchanimation.setOnAnimationListener(this);  
  97.     }  
  98.   
  99.   
  100.     /** 
  101.      * 測量View 的 max min 長度  這裏你可以根據你的要求設置max 
  102.      * @param screenSize 
  103.      * @param index 從零開始 
  104.      */  
  105.     private void measureSize(int layoutSize) {  
  106.         int halfWidth = layoutSize / 2;  
  107.         maxSize = halfWidth - 50;  
  108.         minSize = (layoutSize - maxSize) / (mainContain.getChildCount() - 1);  
  109.           
  110.         Log.i(TAG, "maxWidth="+maxSize+" minWidth = "+minSize);  
  111.           
  112.     }  
  113.   
  114.     @Override  
  115.     public void onClick(View v) {  
  116.           
  117.         int id = v.getId();  
  118.         View tempView = null;  
  119.         switch (id) {  
  120.           
  121.         case R.id.btnOne:  
  122.             tempView = mainContain.getChildAt(0);  
  123.             break;  
  124.         case R.id.btnTwo:  
  125.             tempView = mainContain.getChildAt(1);  
  126.             break;  
  127.         case R.id.btnThree:  
  128.             tempView = mainContain.getChildAt(2);  
  129.             break;  
  130.         case R.id.btnFour:  
  131.             tempView = mainContain.getChildAt(3);  
  132.             break;  
  133.         }  
  134.         if(tempView == preView){  
  135.             Log.d(TAG, "");  
  136.             String addInfo = ((Button) currentView).getText().toString()+"動畫不能執行";  
  137.             printAddViewDebugInfo(addInfo);  
  138.             return;  
  139.         }else{  
  140.             currentView = tempView;  
  141.         }  
  142.         Log.i(TAG, ((Button) currentView).getText().toString() + " click");  
  143.         clickEvent(currentView);  
  144.         onOffClickable(false);  
  145.         String addInfo = ((Button) currentView).getText().toString()+"start animation";  
  146.         printAddViewDebugInfo(addInfo);  
  147.         stretchanimation.startAnimation(currentView);  
  148.   
  149.   
  150.     }  
  151.       
  152.     private void clickEvent(View view) {  
  153.         View child;  
  154.         int childCount = mainContain.getChildCount();  
  155.         LinearLayout.LayoutParams params;  
  156.         for (int i = 0; i < childCount; i++) {  
  157.             child = mainContain.getChildAt(i);  
  158.             if (preView == child) {  
  159.                 params = (android.widget.LinearLayout.LayoutParams) child  
  160.                         .getLayoutParams();  
  161.                   
  162.                 if(preView != view){  
  163.                     params.weight = 1.0f;  
  164.                 }  
  165.                 child.setLayoutParams(params);  
  166.                   
  167.             } else {  
  168.                 params = (android.widget.LinearLayout.LayoutParams) child  
  169.                         .getLayoutParams();  
  170.                 params.weight = 0.0f;  
  171.                 if(stretchanimation.getmType() == StretchAnimation.TYPE.horizontal){  
  172.                     params.width = minSize;  
  173.                 }else if(stretchanimation.getmType() == StretchAnimation.TYPE.vertical){  
  174.                     params.height = minSize;  
  175.                 }  
  176.                   
  177.                 child.setLayoutParams(params);  
  178.             }  
  179.         }  
  180.         preView = view;  
  181.           
  182.     }  
  183.       
  184.     // 調試信息  
  185.     private void printDebugMsg() {  
  186.         View child;  
  187.         int childCount = mainContain.getChildCount();  
  188.         StringBuilder sb = new StringBuilder();  
  189.         sb.append("preView = "+((Button)preView).getText().toString()+" ");  
  190.         sb.append("click = "+((Button)currentView).getText().toString()+" ");  
  191.         for (int i = 0; i < childCount; i++) {  
  192.             child = mainContain.getChildAt(i);  
  193.             LinearLayout.LayoutParams params = (android.widget.LinearLayout.LayoutParams) child  
  194.                     .getLayoutParams();  
  195.             sb.append(params.weight+" ");  
  196.         }  
  197.         Log.d(TAG, sb.toString());  
  198.     }  
  199.       
  200.     // LinearLayout下所有childView 可點擊開關  
  201.     // 當動畫在播放時應該設置爲不可點擊,結束時設置爲可點擊  
  202.     private void onOffClickable(boolean isClickable)  
  203.     {  
  204.         View child;  
  205.         int childCount = mainContain.getChildCount();  
  206.         for (int i = 0; i < childCount; i++) {  
  207.             child = mainContain.getChildAt(i);  
  208.             child.setClickable(isClickable);  
  209.         }  
  210.     }  
  211.   
  212.     @Override  
  213.     public void animationEnd(View v) {  
  214.           
  215.         Log.i(TAG, ("-----"+((Button)v).getText().toString())+" annation end");  
  216.         String addStr = ((Button)v).getText().toString()+" annation end";  
  217.         printAddViewDebugInfo(addStr);  
  218.         onOffClickable(true);  
  219.     }  
  220.       
  221.     private void printAddViewDebugInfo(String addinfo)  
  222.     {  
  223.         String temp = tvLog.getText().toString();  
  224.         tvLog.setText(temp+"\n"+addinfo);  
  225.     }  

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

初始化stretchanimation 

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

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

  1. stretchanimation = new StretchAnimation(maxSize, minSize, StretchAnimation.TYPE.vertical, 500);  
  2. // 設置它的插值器 彈球效果  
  3. stretchanimation.setInterpolator(new BounceInterpolator());  
  4. // 動畫播放的總時間  
  5. stretchanimation.setDuration(800);  
  6. // 動畫播放完後的回調  
  7. stretchanimation.setOnAnimationListener(this);  
  8. // 播放動畫 參數是你要播放的View  
  9. stretchanimation.startAnimation(currentView)  

下面是在模擬器上運行的效果圖, 有點卡。我設置的動畫時間是800毫秒,建議你在真機上玩玩看



不同插值器運行效果不一樣,上面是垂直動畫效果

下面我們只需簡單的三步就可以實現水平效果

1.  measureSize(screentWidth);你可以設置屏幕寬度,例如上面我這個大小設置的是屏幕的高度,所以四個按鈕就佔屏幕的高度。

2.  StretchAnimation實例化時修改 StretchAnimation.TYPE.horizontal 水平效果

3.  修改XML佈局Linearlayout屬性 android:orientation="horizontal" 水平

修改後的水平動畫效果:


    本篇相對於上一篇來說算是加強版 。水平伸縮動畫和垂直伸縮動畫可輕鬆轉換,相對於上一篇增加對動畫的控制

功能。可以控制動畫時間,而動畫時間不會因分辨率的增加而改變;通過改變動畫的速率可實現不同的動畫效果,彈

球效果,加速,勻速效果等等。

對上述代碼稍作修改就可以實現如下效果,這種效果用到插值器 AccelerateDecelerateInterpolator

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章