android 随手记 ——android view特效开发(可伸缩view带互相挤压效果)

来源:互联网 发布:2018中国经济知乎 编辑:程序博客网 时间:2024/04/28 03:16
本文由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水平拉伸或者垂直拉伸。你可以设置动画的时间,你可以设置它的插值器,改变动

画的效果。下面该类的实现过程。

[java] view plaincopyprint?
  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的过程

[java] view plaincopyprint?
  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布局也应该是垂直,

[java] view plaincopyprint?
  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

原创粉丝点击