看代码学AndroidUI - Tab
Posted on 2016-03-12 in Android by yucongchen
最近慢慢学习一点安卓,先看了些基础的,还处于很初级的阶段,平常都是面对弱类型的语言,python,js,现在看java突然有点不适应。
这里推荐郭神的《第一行代码》,不过书中关于UI,界面方面的说的比较少。
俗话说:Don't BB, show me the code. 于是去看看官方文档关于UI的代码例子http://developer.android.com/intl/zh-cn/samples/ui.html
官方提供的源代码可以下载之后导入Android Studio,或者当启动Android Studio的时候,右边什么创建新项目下面有一个Import an Android code sample
,在这里选了,会直接帮你去github上download代码并导入工程,这个必须赞。
这里使用的是SlidingTabsBasic这个例子。
开始
现运行一下代码,看看界面是什么样子,如下图所示:
可以大致看到主要分了上下两个模块,上面主要是个TextView,下面就是我们要看的tab。对照一下activity_main.xml,上半部分:
<ViewAnimator
android:id="@+id/sample_output"
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="1">
<ScrollView
style="@style/Widget.SampleMessageTile"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
style="@style/Widget.SampleMessage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/horizontal_page_margin"
android:paddingRight="@dimen/horizontal_page_margin"
android:paddingTop="@dimen/vertical_page_margin"
android:paddingBottom="@dimen/vertical_page_margin"
android:text="@string/intro_message" />
</ScrollView>
<fragment
android:name="com.example.android.common.logger.LogFragment"
android:id="@+id/log_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</ViewAnimator>
:::
这里,TextView里面有个属性android:text
所引用的string就是我们屏幕上看到"A basic sample ...."的东西,还有个fragment,这个是放log的,也就是点右上角那个『SHOW LOG』显示的,由于主要是学习tab的用法,所以这里先带过。
然后下半部分:
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@android:color/darker_gray" />
<FrameLayout
android:id="@+id/sample_content_fragment"
android:layout_weight="2"
android:layout_width="match_parent"
android:layout_height="0px" />
这里有个1dp的View,就是上下部分之间的灰色的线,还有个FrameLayout,tab就是放这里了。说是放这里,其实也不是,因为和html那种直白的语法不同,这里是动态填充进去的。于是我们打开MainActivity.java文件,看看onCreate方法。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
SlidingTabsBasicFragment fragment = new SlidingTabsBasicFragment();
transaction.replace(R.id.sample_content_fragment, fragment);
transaction.commit();
}
}
这里实例化了一个SlidingTabsBasicFragment
对象,并把这个Fragment加入到主Activity的FrameLayout中。
继续看SlidingTabsBasicFragment
对象
SlidingTabsBasicFragment对象的onCreateView方法:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_sample, container, false);
}
这里在创建View的时候使用inflate方法填充了一个fragment_sample。inflate就是填充充气的意思,恩,就类似给一个空的娃娃充气,呃,跑题了。
既然填充了fragment_sample,那我们自然要看看这里面是什么鬼,
fragment_sample:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.example.android.common.view.SlidingTabLayout
android:id="@+id/sliding_tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="1"
android:background="@android:color/white"/>
</LinearLayout>
这是一个LinearLayout,里面有两个东西,一个是自定义控件SlidingTabLayout
,一个是ViewPage。这哥俩分别是界面上的Tab Head和Tab Content。
看完回到SlidingTabsBasicFragment,里面还有一个onViewCreated
方法。
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
// BEGIN_INCLUDE (setup_viewpager)
// Get the ViewPager and set it's PagerAdapter so that it can display items
mViewPager = (ViewPager) view.findViewById(R.id.viewpager);
mViewPager.setAdapter(new SamplePagerAdapter());
// END_INCLUDE (setup_viewpager)
// BEGIN_INCLUDE (setup_slidingtablayout)
// Give the SlidingTabLayout the ViewPager, this must be done AFTER the ViewPager has had
// it's PagerAdapter set.
mSlidingTabLayout = (SlidingTabLayout) view.findViewById(R.id.sliding_tabs);
mSlidingTabLayout.setViewPager(mViewPager);
// END_INCLUDE (setup_slidingtablayout)
}
主要是设置了一个SamplePageAdapter
,这个Adapter就在这个文件里,是个内部类。
SamplePageAdapter:
class SamplePagerAdapter extends PagerAdapter {
/**
* @return the number of pages to display
*/
@Override
public int getCount() {
return 10;
}
/**
* @return true if the value returned from {@link #instantiateItem(ViewGroup, int)} is the
* same object as the {@link View} added to the {@link ViewPager}.
*/
@Override
public boolean isViewFromObject(View view, Object o) {
return o == view;
}
// BEGIN_INCLUDE (pageradapter_getpagetitle)
/**
* Return the title of the item at {@code position}. This is important as what this method
* returns is what is displayed in the {@link SlidingTabLayout}.
* <p>
* Here we construct one using the position value, but for real application the title should
* refer to the item's contents.
*/
@Override
public CharSequence getPageTitle(int position) {
return "Item " + (position + 1);
}
// END_INCLUDE (pageradapter_getpagetitle)
/**
* Instantiate the {@link View} which should be displayed at {@code position}. Here we
* inflate a layout from the apps resources and then change the text view to signify the position.
*/
@Override
public Object instantiateItem(ViewGroup container, int position) {
// Inflate a new layout from our resources
View view = getActivity().getLayoutInflater().inflate(R.layout.pager_item,
container, false);
// Add the newly created View to the ViewPager
container.addView(view);
// Retrieve a TextView from the inflated View, and update it's text
TextView title = (TextView) view.findViewById(R.id.item_title);
title.setText(String.valueOf(position + 1));
Log.i(LOG_TAG, "instantiateItem() [position: " + position + "]");
// Return the View
return view;
}
/**
* Destroy the item from the {@link ViewPager}. In our case this is simply removing the
* {@link View}.
*/
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
Log.i(LOG_TAG, "destroyItem() [position: " + position + "]");
}
}
这里代码都比较好懂,其中getCount
返回10,表示有10个ViewPage,之后Tab Head上面的item个数也是根据这个来的,把这里返回值改成2,就可以看到只有2个tab了。
getPageTitle
这个方法,返回的就是Item 1, Item 2…… 这样的东西,在SlidingTabLayoout
那个自定义控件中会被调用,然后来设置Tab Head的文字。
instantiateItem
当ViewPage实例化的时候,设置ViewPage的layout为pager_item,并且设置里面的文字。
pager_item.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<TextView
android:id="@+id/item_subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Page:"/>
<TextView
android:id="@+id/item_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="80sp" />
</LinearLayout>
一个竖向且排列在中间的LinearLayout,里面有两个TextView,就是instantiateItem方法里面去设置的ViewPage中的文字。
设置Tab Head平分铺满
大概整个过程我们以及弄清楚了,除了那个自定义控件。这时候把getCount
方法的返回值改成2,发现Tab Head并没有铺满屏幕,还是一个个往左对齐,这样很丑对不对。那我们来改造一下。
还记得之前说的getPageTitle
方法会在SlidingTabLayoout
那个自定义控件中会被调用,然后来设置Tab Head文字吗?我们来看下调用的地方:
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);
}
}
大概梳理一下,从adapter那拿到有多少个ViewPage,就是getCount
方法返回的个数,然后循环添加Tab Head,这里有一行小小的注释,意思是当设置了自定义的tab view layout id时,就尝试用这个自定义的来『充气』。
当没有自定义的时候就通过createDefaultTabView
来建一个默认的,看下这个方法,默认的TabView就是一个TextView,所以,我们只要自定义一个TextView就可以实现平分铺满Tab Head了。
在layout下添加一下custom_tab.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:textSize="12sp"
android:gravity="center"
android:textStyle="bold"
android:padding="16dp" />
内容很简单,就是一个layout_weight="1"
的TextView。
之后在SlidingTabsBasicFragment的onCreate方法中设置自定义的TabView, mSlidingTabLayout.setCustomTabView(R.layout.custom_tab, 0);
完整代码:
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
mViewPager = (ViewPager) view.findViewById(R.id.viewpager);
mViewPager.setAdapter(new SamplePagerAdapter());
mSlidingTabLayout = (SlidingTabLayout) view.findViewById(R.id.sliding_tabs);
// Set Custom TabView
mSlidingTabLayout.setCustomTabView(R.layout.custom_tab, 0);
mSlidingTabLayout.setViewPager(mViewPager);
}
之后再构建运行一下,如下图:
ok,好看多了。
整个代码已经放到github上,SlidingTabsBasic Sample
参考
http://blog.csdn.net/mr_wanggang/article/details/42340265