Android 5.0(L) ToolBar(替代ActionBar) 实战
Posted 萧文翰
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 5.0(L) ToolBar(替代ActionBar) 实战相关的知识,希望对你有一定的参考价值。
《代码大全》一书中提到:当我们编程时,不要简单停留在“在一种编程语言上进行编程”,而是要“深入一种语言”编程。在前面两篇文章中,虽然初步实现了ViewPager+Tab的布局,但实际上还是有一些不足的。让我们把目光锁定在Tab:
可以发现,3个Tab加一起的宽度并没有填充屏幕整个宽度。实际上,Google这样的设计是为了应对多Tab的情形,当Tab总宽度超出屏幕宽度的时候,用户可以简单地通过滑动Tab看到后面未显示出来的Tab。这种情况其实还是比较多见的,比如众多的新闻阅读客户端和Google原生应用等等。
但是如果我们只有三个Tab,甚至只有两个Tab,想让整个Tab的宽度等于屏幕宽度,该如何做呢?第一反应自然是要略微折腾一下SlidingTabLayout.java的源码了。
经过研究发现,实际上如上图顶部Tab的实现通过HorizontalScrollView,然后add了一个又一个的view,这个View就是每个Tab的标题,而整个SlidingTabLayout是继承了HorizontalScrollView。
接下来的思路就很清晰了:首先获取屏幕的总宽度,然后传给SlidingTabLayout对象,在其类内部对每个Tab所占的宽度进行计算和分配,最后将每个Tab标题的视图add到HorizontalScrollView就OK了。
首先获取屏幕的宽度。这一操作我们在MainActivity中进行,下面列出这一段程序清单:
<span style="white-space:pre"> </span>// 获取屏幕宽度
private int caculateScreenX()
return getResources().getDisplayMetrics().widthPixels;
获取到屏幕宽度之后,接下来就是把这个值传递给SlidingTabLayout。回顾一下用法,上一篇文章里面提到,我们在添加Tab导航栏的时候,仅仅是实例化了SlidingTabLayout的对象,然后调用了setViewPager()方法而已。那么,我们不妨在该方法上面添加一个参数,用来传递屏幕宽度。当然,我们需要在SlidingTabLayout中声明一个全局的int变量,用来存放每个Tab的宽度值:
<span style="white-space:pre"> </span>public void setViewPager(ViewPager viewPager, int screenX)
<span style="white-space:pre"> </span>this.screenX = screenX / viewPager.viewPager.getAdapter().getCount();
<span style="white-space:pre"> </span>mTabStrip.removeAllViews();
<span style="white-space:pre"> </span>mViewPager = viewPager;
<span style="white-space:pre"> </span>if (viewPager != null)
<span style="white-space:pre"> </span>viewPager.setOnPageChangeListener(new InternalViewPagerListener());
<span style="white-space:pre"> </span>populateTabStrip();
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>
跟随该方法,找到populateTabStrip()方法,可以看到其中通过一个for循环以此添加了各个Tab视图。而每个Tab视图均通过createDefaultTabView()方法创建,因此该方法最为关键。
找到这个方法之后我们发现,每个Tab的标题View实际上是TextView,那么我们只需将每个TextView的宽度制定为刚刚计算出来的screenX(每个Tab的宽度)即可。具体可参看下面的代码清单:
<span style="white-space:pre"> </span>protected TextView createDefaultTabView(Context context)
TextView textView = new TextView(context);
textView.setGravity(Gravity.CENTER);
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP);
textView.setTypeface(Typeface.DEFAULT_BOLD);
textView.setWidth(screenX);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
// If we're running on Honeycomb or newer, then we can use the
// Theme's
// selectableItemBackground to ensure that the View has a pressed
// state
TypedValue outValue = new TypedValue();
getContext().getTheme().resolveAttribute(
android.R.attr.selectableItemBackground, outValue, true);
textView.setBackgroundResource(outValue.resourceId);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
// If we're running on ICS or newer, enable all-caps to match the
// Action Bar tab style
textView.setAllCaps(true);
int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources()
.getDisplayMetrics().density);
textView.setPadding(padding, padding, padding, padding);
return textView;
最后,我们修改MainActivity,把计算好的屏幕宽度传给SlidingTabLayout,Ctrl+F11看看效果吧!
不出意外的话,看到上图就说明已经成功了!
下面放上整个SlidingTabLayout修改后的类:
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.xwh.toolbardemo.view;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Typeface;
import android.os.Build;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.HorizontalScrollView;
import android.widget.TextView;
/**
* To be used with ViewPager to provide a tab indicator component which give
* constant feedback as to the user's scroll progress.
* <p>
* To use the component, simply add it to your view hierarchy. Then in your
* @link android.app.Activity or @link android.support.v4.app.Fragment call
* @link #setViewPager(ViewPager) providing it the ViewPager this layout is
* being used for.
* <p>
* The colors can be customized in two ways. The first and simplest is to
* provide an array of colors via @link #setSelectedIndicatorColors(int...)
* and @link #setDividerColors(int...). The alternative is via the
* @link TabColorizer interface which provides you complete control over which
* color is used for any individual position.
* <p>
* The views used as tabs can be customized by calling
* @link #setCustomTabView(int, int), providing the layout ID of your custom
* layout.
*/
public class SlidingTabLayout extends HorizontalScrollView
/**
* Allows complete control over the colors drawn in the tab layout. Set with
* @link #setCustomTabColorizer(TabColorizer).
*/
public interface TabColorizer
/**
* @return return the color of the indicator used when @code position
* is selected.
*/
int getIndicatorColor(int position);
/**
* @return return the color of the divider drawn to the right of
* @code position.
*/
int getDividerColor(int position);
private static final int TITLE_OFFSET_DIPS = 24;
private static final int TAB_VIEW_PADDING_DIPS = 16;
private static final int TAB_VIEW_TEXT_SIZE_SP = 12;
private int mTitleOffset;
private int mTabViewLayoutId;
private int mTabViewTextViewId;
private ViewPager mViewPager;
private ViewPager.OnPageChangeListener mViewPagerPageChangeListener;
private final SlidingTabStrip mTabStrip;
private int screenX;
public SlidingTabLayout(Context context)
this(context, null);
public SlidingTabLayout(Context context, AttributeSet attrs)
this(context, attrs, 0);
public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle)
super(context, attrs, defStyle);
// Disable the Scroll Bar
setHorizontalScrollBarEnabled(false);
// Make sure that the Tab Strips fills this View
setFillViewport(true);
mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources()
.getDisplayMetrics().density);
mTabStrip = new SlidingTabStrip(context);
addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
/**
* Set the custom @link TabColorizer to be used.
*
* If you only require simple custmisation then you can use
* @link #setSelectedIndicatorColors(int...) and
* @link #setDividerColors(int...) to achieve similar effects.
*/
public void setCustomTabColorizer(TabColorizer tabColorizer)
mTabStrip.setCustomTabColorizer(tabColorizer);
/**
* Sets the colors to be used for indicating the selected tab. These colors
* are treated as a circular array. Providing one color will mean that all
* tabs are indicated with the same color.
*/
public void setSelectedIndicatorColors(int... colors)
mTabStrip.setSelectedIndicatorColors(colors);
/**
* Sets the colors to be used for tab dividers. These colors are treated as
* a circular array. Providing one color will mean that all tabs are
* indicated with the same color.
*/
public void setDividerColors(int... colors)
mTabStrip.setDividerColors(colors);
/**
* Set the @link ViewPager.OnPageChangeListener. When using
* @link SlidingTabLayout you are required to set any
* @link ViewPager.OnPageChangeListener through this method. This is so
* that the layout can update it's scroll position correctly.
*
* @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener)
*/
public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener)
mViewPagerPageChangeListener = listener;
/**
* Set the custom layout to be inflated for the tab views.
*
* @param layoutResId
* Layout id to be inflated
* @param textViewId
* id of the @link TextView in the inflated view
*/
public void setCustomTabView(int layoutResId, int textViewId)
mTabViewLayoutId = layoutResId;
mTabViewTextViewId = textViewId;
/**
* Sets the associated view pager. Note that the assumption here is that the
* pager content (number of tabs and tab titles) does not change after this
* call has been made.
*/
public void setViewPager(ViewPager viewPager, int screenX)
this.screenX = (screenX / viewPager.getAdapter().getCount());
mTabStrip.removeAllViews();
mViewPager = viewPager;
if (viewPager != null)
viewPager.setOnPageChangeListener(new InternalViewPagerListener());
populateTabStrip();
/**
* Create a default view to be used for tabs. This is called if a custom tab
* view is not set via @link #setCustomTabView(int, int).
*/
@SuppressLint("NewApi")
protected TextView createDefaultTabView(Context context)
TextView textView = new TextView(context);
textView.setGravity(Gravity.CENTER);
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP);
textView.setTypeface(Typeface.DEFAULT_BOLD);
textView.setWidth(screenX);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
// If we're running on Honeycomb or newer, then we can use the
// Theme's
// selectableItemBackground to ensure that the View has a pressed
// state
TypedValue outValue = new TypedValue();
getContext().getTheme().resolveAttribute(
android.R.attr.selectableItemBackground, outValue, true);
textView.setBackgroundResource(outValue.resourceId);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
// If we're running on ICS or newer, enable all-caps to match the
// Action Bar tab style
textView.setAllCaps(true);
int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources()
.getDisplayMetrics().density);
textView.setPadding(padding, padding, padding, padding);
return textView;
private void populateTabStrip()
final PagerAdapter adapter = mViewPager.getAdapter();
final View.OnClickListener tabClickListener = new TabClickListener();
for (int i = 0; i < adapter.getCount(); i++)
View tabView = null;
TextView tabTitleView = null;
if (mTabViewLayoutId != 0)
// If there is a custom tab view layout id set, try and inflate
// it
tabView = LayoutInflater.from(getContext()).inflate(
mTabViewLayoutId, mTabStrip, false);
tabTitleView = (TextView) tabView
.findViewById(mTabViewTextViewId);
if (tabView == null)
tabView = createDefaultTabView(getContext());
if (tabTitleView == null && TextView.class.isInstance(tabView))
tabTitleView = (TextView) tabView;
tabTitleView.setText(adapter.getPageTitle(i));
tabView.setOnClickListener(tabClickListener);
mTabStrip.addView(tabView);
@Override
protected void onAttachedToWindow()
super.onAttachedToWindow();
if (mViewPager != null)
scrollToTab(mViewPager.getCurrentItem(), 0);
private void scrollToTab(int tabIndex, int positionOffset)
final int tabStripChildCount = mTabStrip.getChildCount();
if (tabStripChildCount == 0 || tabIndex < 0
|| tabIndex >= tabStripChildCount)
return;
View selectedChild = mTabStrip.getChildAt(tabIndex);
if (selectedChild != null)
int targetScrollX = selectedChild.getLeft() + positionOffset;
if (tabIndex > 0 || positionOffset > 0)
// If we're not at the first child and are mid-scroll, make sure
// we obey the offset
targetScrollX -= mTitleOffset;
scrollTo(targetScrollX, 0);
private class InternalViewPagerListener implements
ViewPager.OnPageChangeListener
private int mScrollState;
@Override
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels)
int tabStripChildCount = mTabStrip.getChildCount();
if ((tabStripChildCount == 0) || (position < 0)
|| (position >= tabStripChildCount))
return;
mTabStrip.onViewPagerPageChanged(position, positionOffset);
View selectedTitle = mTabStrip.getChildAt(position);
int extraOffset = (selectedTitle != null) ? (int) (positionOffset * selectedTitle
.getWidth()) : 0;
scrollToTab(position, extraOffset);
if (mViewPagerPageChangeListener != null)
mViewPagerPageChangeListener.onPageScrolled(position,
positionOffset, positionOffsetPixels);
@Override
public void onPageScrollStateChanged(int state)
mScrollState = state;
if (mViewPagerPageChangeListener != null)
mViewPagerPageChangeListener.onPageScrollStateChanged(state);
@Override
public void onPageSelected(int position)
if (mScrollState == ViewPager.SCROLL_STATE_IDLE)
mTabStrip.onViewPagerPageChanged(position, 0f);
scrollToTab(position, 0);
if (mViewPagerPageChangeListener != null)
mViewPagerPageChangeListener.onPageSelected(position);
private class TabClickListener implements View.OnClickListener
@Override
public void onClick(View v)
for (int i = 0; i < mTabStrip.getChildCount(); i++)
if (v == mTabStrip.getChildAt(i))
mViewPager.setCurrentItem(i);
return;
这一篇到此结束。下一篇将讨论菜单的添加和关于ToolBar的更多精彩用法。
以上是关于Android 5.0(L) ToolBar(替代ActionBar) 实战的主要内容,如果未能解决你的问题,请参考以下文章