網易新聞這種滑動TAB效果,在android軟體中還是比較常見的(是不是原創我不清楚,僅當學習研究之用~~~)。
比較常見的做法是:在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~~