最近公司需要改进tab之间切换的速度,所以我用hierarchyviewer工具查看了一下,发现每个tab的onmeasure+onlayout+ondraw的时间加起来为200ms左右,所以导致了tab之间切换的非常不流畅。
怎么优化呢?先看看android.widget.ViewFlipper类,继承自android.widget.ViewAnimator,是一个为tab之间切换实现简单动画的一个viewgroup,但是我们公司的软件tab之间的切换根本就没有动画,也就是说我们只是将ViewFlipper类作为一个承载view的容器,用户点击每个tab,从而显示相应的view,显然这没有使用ViewFlipper的意义,但是为什么切换的速度这么慢呢?我们去看看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即可