最近公司需要改进tab之间切换的速度,所以我用hierarchyviewer工具查看了一下,发现每个tab的onmeasure+onlayout+ondraw的时间加起来为200ms左右,所以导致了tab之间切换的非常不流畅。
怎么优化呢?先看看android.widget.ViewFlipper类,继承自android.widget.ViewAnimator,是一个为tab之间切换实现简单动画的一个viewgroup,但是我们公司的软件tab之间的切换根本就没有动画,也就是说我们只是将ViewFlipper类作为一个承载view的容器,用户点击每个tab,从而显示相应的view,显然这没有使用ViewFlipper的意义,但是为什么切换的速度这么慢呢?我们去看看ViewFlipper的源码:
没有找到 setDisplayedChild(int)函数,那就在父类里面,找到父类:
/**
* Sets which child view will be displayed.
*
* @param whichChild the index of the child view to display
*/
@android.view.RemotableViewMethod
public void setDisplayedChild(int whichChild) {
mWhichChild = whichChild;
if (whichChild >= getChildCount()) {
mWhichChild = 0;
} else if (whichChild < 0) {
mWhichChild = getChildCount() - 1;
}
boolean hasFocus = getFocusedChild() != null;
// This will clear old focus if we had it
showOnly(mWhichChild);
if (hasFocus) {
// Try to retake focus if we had it
requestFocus(FOCUS_FORWARD);
}
}
从代码里面可以简单的看出来,showOnly()函数是重点,转到showOnly函数
void showOnly(int childIndex, boolean animate) {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (i == childIndex) {
if (animate && mInAnimation != null) {
child.startAnimation(mInAnimation);
}
child.setVisibility(View.VISIBLE);
mFirstTime = false;
} else {
if (animate && mOutAnimation != null && child.getVisibility() == View.VISIBLE) {
child.startAnimation(mOutAnimation);
} else if (child.getAnimation() == mInAnimation)
child.clearAnimation();
child.setVisibility(View.GONE);
}
}
}
一个循环,把所有的子view都执行移出动画和移入动画,并且同时设置visibility,所以我个人觉得是不是这里循环有点耗时?
于是我就换成了tabhost,嗯!就是这个感觉,时间上感觉少了不少,切换tab的速度也快了很多,tabhost的实现主要是
public void setCurrentTab(int index) {
if (index < 0 || index >= mTabSpecs.size()) {
return;
}
if (index == mCurrentTab) {
return;
}
// notify old tab content
if (mCurrentTab != -1) {
mTabSpecs.get(mCurrentTab).mContentStrategy.tabClosed();
}
mCurrentTab = index;
final TabHost.TabSpec spec = mTabSpecs.get(index);
// Call the tab widget's focusCurrentTab(), instead of just
// selecting the tab.
mTabWidget.focusCurrentTab(mCurrentTab);
// tab content
mCurrentView = spec.mContentStrategy.getContentView();
if (mCurrentView.getParent() == null) {
mTabContent
.addView(
mCurrentView,
new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
}
if (!mTabWidget.hasFocus()) {
// if the tab widget didn't take focus (likely because we're in touch mode)
// give the current tab content view a shot
mCurrentView.requestFocus();
}
//mTabContent.requestFocus(View.FOCUS_FORWARD);
invokeOnTabChangeListener();
}
tabwidget的focusCurrentTab函数:
public void setCurrentTab(int index) {
if (index < 0 || index >= getTabCount() || index == mSelectedTab) {
return;
}
if (mSelectedTab != -1) {
getChildTabViewAt(mSelectedTab).setSelected(false);
}
mSelectedTab = index;
getChildTabViewAt(mSelectedTab).setSelected(true);
mStripMoved = true;
if (isShown()) {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
}
}
setSelected会调用到view的
invalidate函数重新绘制
流程就是这样,用起来的效果也是tabhost比较快,个人觉得如果不需要tab之间切换的移出和移入动画,
用viewFlipper就大材小用了,用tabhost即可