看代码学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这个例子。

开始

现运行一下代码,看看界面是什么样子,如下图所示:

看代码学AndroidUI

可以大致看到主要分了上下两个模块,上面主要是个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);
}

之后再构建运行一下,如下图:

看代码学AndroidUI

ok,好看多了。

整个代码已经放到github上,SlidingTabsBasic Sample

参考
http://blog.csdn.net/mr_wanggang/article/details/42340265