当前位置: 代码迷 >> Android >> Android奇效开发(可伸缩View带互相挤压效果)进阶篇
  详细解决方案

Android奇效开发(可伸缩View带互相挤压效果)进阶篇

热度:98   发布时间:2016-04-28 07:38:56.0
Android特效开发(可伸缩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水平拉伸或者垂直拉伸。你可以设置动画的时间,你可以设置它的插值器,改变动

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

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的过程

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布局也应该是垂直,

stretchanimation = new StretchAnimation(maxSize, minSize, StretchAnimation.TYPE.vertical, 500);// 设置它的插值器 弹球效果stretchanimation.setInterpolator(new BounceInterpolator());// 动画播放的总时间stretchanimation.setDuration(800);// 动画播放完后的回调stretchanimation.setOnAnimationListener(this);// 播放动画 参数是你要播放的Viewstretchanimation.startAnimation(currentView)

下面是在模拟器上运行的效果图, 有点卡。我设置的动画时间是800毫秒,建议你在真机上玩玩看



不同插值器运行效果不一样,上面是垂直动画效果

下面我们只需简单的三步就可以实现水平效果

1.  measureSize(screentWidth);你可以设置屏幕宽度,例如上面我这个大小设置的是屏幕的高度,所以四个按钮就占屏幕的高度。

2.  StretchAnimation实例化时修改 StretchAnimation.TYPE.horizontal 水平效果

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

修改后的水平动画效果:


    本篇相对于上一篇来说算是加强版 。水平伸缩动画和垂直伸缩动画可轻松转换,相对于上一篇增加对动画的控制

功能。可以控制动画时间,而动画时间不会因分辨率的增加而改变;通过改变动画的速率可实现不同的动画效果,弹

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

对上述代码稍作修改就可以实现如下效果,这种效果用到插值器 AccelerateDecelerateInterpolator




  相关解决方案