天天看點

仿網易新聞欄目滑動TAB效果

    網易新聞這種滑動TAB效果,在android軟體中還是比較常見的(是不是原創我不清楚,僅當學習研究之用~~~)。

仿網易新聞欄目滑動TAB效果

   比較常見的做法是:在FrameLayout裡包裝TAB bar,再在FrameLayout加一個虛拟的tab,切換tab時用虛拟tab在之前選中和目前選中的tab距離之間做一個移動動畫。

   用此種方法的十之八九,但在複雜的布局中FrameLayout不是你想加就能加滴,加了很容易崩潰滴^_^,此法的劣勢大家明了吧?不明那就慢慢明吧。

   今天我采用一種更加高效、靈活的方法來實作這種大家喜歡的花樣--自畫控件+Drawable動畫。

   大體原理跟FrameLayout加虛拟tab類似,以下是基本的實作方法:在切換tab時在之前選中的tab和目前選中的tab,做一個drawable移動動畫。這個 drawable 就是目前選中tab的畫面。

   下面是我重載RadioGoup做的tab bar并實作了滑動效果的類

public class NewsRadioGroup extends RadioGroup
		implements
			OnCheckedChangeListener {
	private final Transformation mTransformation = new Transformation();
	private Animation mAnimation;
	private OnCheckedChangeListener mOnCheckedChangeListener;
	private int mLastCheckedId = -1;
	private Drawable mDummy;
	private Drawable mDrawableNormal,mDrawableChecked;
	private boolean mAminDoing=false;

	public NewsRadioGroup(Context context) {
		super(context);
		init();
	}

	public NewsRadioGroup(Context context, AttributeSet attrs) {
		super(context, attrs);
		init();
	}

	private void init() {
		super.setOnCheckedChangeListener(this);
		mLastCheckedId = super.getCheckedRadioButtonId();
		mDummy = getResources().getDrawable(R.drawable.rb_checked);
		mDrawableNormal = getResources().getDrawable(android.R.color.transparent);
	}

	public void onCheckedChanged(RadioGroup group, int checkedId) {
		if (mLastCheckedId != -1) {
			doAmin(checkedId);
		}else{
			mLastCheckedId = checkedId;
		}
		if (mOnCheckedChangeListener != null) {
			mOnCheckedChangeListener.onCheckedChanged(group, checkedId);
		}
	}
	
	private void doAmin( int checkedId){
		RadioButton rbCurChecked = (RadioButton) super.findViewById(checkedId), rbLastChecked = (RadioButton) super.findViewById(mLastCheckedId);
		if(rbCurChecked==null||rbLastChecked==null){
			mLastCheckedId=checkedId;
			return;
		}
		int X1=rbLastChecked.getLeft(),X2=rbCurChecked.getLeft();
		if (X1 <= 0 && X2 <= 0) {
			mLastCheckedId=checkedId;
			return;
		}

		if (mDrawableChecked == null) {
			mDrawableChecked = rbCurChecked.getBackground();
			mDummy.setBounds(0, 0, rbCurChecked.getWidth(), rbCurChecked.getHeight());
		}
		rbCurChecked.setBackgroundDrawable(mDrawableNormal);

		if(mAminDoing && mAnimation!=null){
			mAnimation.reset();
		}
		mAnimation = new TranslateAnimation(X1,X2, rbCurChecked.getTop(), rbCurChecked.getTop());
		mAnimation.setDuration(300);
		mAnimation.initialize(0, 0, 0, 0);
		mAminDoing=true;
		mAnimation.startNow();
		invalidate();
	}

	public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
		mOnCheckedChangeListener = listener;
	}

	protected void onDraw(Canvas canvas) {
		if (mAnimation == null || !mAminDoing) {
			return;
		}
		if (!mAnimation.hasEnded()) {
			int sc = canvas.save();
			mAnimation.getTransformation(
					AnimationUtils.currentAnimationTimeMillis(),
					mTransformation);
			canvas.concat(mTransformation.getMatrix());
			mDummy.draw(canvas);
			canvas.restoreToCount(sc);
			invalidate();
		} else {
			mAminDoing=false;
			setReallyCheck();
		}
	}

	private void setReallyCheck() {
		if (mLastCheckedId != -1) {
			super.findViewById(mLastCheckedId).setBackgroundDrawable(mDrawableNormal);
		}
		
		mLastCheckedId = super.getCheckedRadioButtonId();
		if (mLastCheckedId != -1) {
			super.findViewById(mLastCheckedId).setBackgroundDrawable(mDrawableChecked);
		}
	}

}// end class NesRadioGroup
           

  之前以為隻要重寫RadioGroup的check方法就可以攔截選中,沒想到RadioGroup把實際的選中執行實作在setCheckxx裡面,而這個函數是私有方法,最後繞了不小的圈子。

代碼在上,具體的效果請見demo~~