Android UI 之 Tab类型界面总结

Android 程序中实现Tab类型界面很常见,本人在做项目的时候也经常用到,所以想在这里总结一下,实现tab类型界面的几种方式,供大家参考。如有不对之处,欢迎大家指正!

    一、TabActivity + TabWidget + TabHost.

    实现TAB类型界面,首先想到的就是这种方式。但是在API level 13之后官方就不建议使用它了。不过还是在这里简单说一下它的使用吧。


    使用它的关键就是布局文件了。需要在布局中添 加<TabHost>、<TabWidget>、<FrameLayout>这三个控件,id分别是系统提供 的:@android:id/tabhost 、@android:id/tabs 、@android:id/tabcontent 。

 

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:orientation="vertical" >  
  6.   
  7.     <TabHost  
  8.         android:id="@android:id/tabhost"  
  9.         android:layout_width="match_parent"  
  10.         android:layout_height="match_parent" >  
  11.   
  12.         <RelativeLayout  
  13.             android:layout_width="match_parent"  
  14.             android:layout_height="match_parent"  
  15.             android:orientation="vertical" >  
  16.   
  17.             <!-- 可以指定tabwidget的位置    android:layout_alignParentBottom="true" -->  
  18.   
  19.             <TabWidget  
  20.                 android:id="@android:id/tabs"  
  21.                 android:layout_width="match_parent"  
  22.                 android:layout_height="wrap_content"  
  23.                 android:layout_alignParentBottom="false" >  
  24.             </TabWidget>  
  25.   
  26.             <FrameLayout  
  27.                 android:id="@android:id/tabcontent"  
  28.                 android:layout_width="match_parent"  
  29.                 android:layout_height="match_parent"  
  30.                 android:layout_below="@android:id/tabs" >  
  31.   
  32.                 <LinearLayout  
  33.                     android:id="@+id/tab1"  
  34.                     android:layout_width="match_parent"  
  35.                     android:layout_height="match_parent"  
  36.                     android:background="#DEB887"  
  37.                     android:orientation="vertical" >  
  38.                 </LinearLayout>  
  39.   
  40.                 <LinearLayout  
  41.                     android:id="@+id/tab2"  
  42.                     android:layout_width="match_parent"  
  43.                     android:layout_height="match_parent"  
  44.                     android:background="#BCEE68"  
  45.                     android:orientation="vertical" >  
  46.                 </LinearLayout>  
  47.   
  48.                 <LinearLayout  
  49.                     android:id="@+id/tab3"  
  50.                     android:layout_width="match_parent"  
  51.                     android:layout_height="match_parent"  
  52.                     android:background="#7D9EC0"  
  53.                     android:orientation="vertical" >  
  54.                 </LinearLayout>  
  55.             </FrameLayout>  
  56.         </RelativeLayout>  
  57.     </TabHost>  
  58.   
  59. </LinearLayout>  

    一个linearlayout对应一个tab页面的布局。

 

  1. tabHost = getTabHost();  
  2.   
  3.         tabHost.addTab(tabHost  
  4.                 .newTabSpec("111")  
  5.                 .setIndicator("", getResources().getDrawable(R.drawable.wuyong))  
  6.                 .setContent(R.id.tab1));  
  7.   
  8.         tabHost.addTab(tabHost  
  9.                 .newTabSpec("222")  
  10.                 .setIndicator("",  
  11.                         getResources().getDrawable(R.drawable.gongsunsheng))  
  12.                 .setContent(R.id.tab2));  
  13.   
  14.         tabHost.addTab(tabHost.newTabSpec("333")  
  15.                 .setIndicator("", getResources().getDrawable(R.drawable.likui))  
  16.                 .setContent(R.id.tab3));  
  17.   
  18.         tabHost.setBackgroundColor(Color.argb(150, 22, 70, 150));  
  19.         tabHost.setCurrentTab(0);  
  20.         tabHost.setOnTabChangedListener(new OnTabChangeListener() {  
  21.             @Override  
  22.             public void onTabChanged(String tabId) {  
  23.                 Toast.makeText(FourthActivity.this, tabId, Toast.LENGTH_SHORT)  
  24.                         .show();  
  25.             }  
  26.         });  


二、ViewPager + PageAdapter

    目前最常见的tab界面就是使用viewpager来实现了。

    先来说一下viewpager的一般使用步骤:

    1. 在布局文件中添加viewpager控件

    2. 在代码中设置viewpager适配器,该类继承与pagerAdapter或它的子类。必须实现以下四个方法:

    (1)getCount()

    (2)instantiateItem()

    (3)destroyItem()

    (4)isViewFromObject()

    3. 初始化viewpager控件,设置监听器

    4. 设置监听事件(setOnPageChangeListener)

    下面看一下这种方式的效果图:


主要的功能代码如下:

 

  1. private void init() {  
  2.         viewPager = (ViewPager) findViewById(R.id.first_vp);  
  3.         LayoutInflater inflater = LayoutInflater.from(this);  
  4.         View view1 = inflater.inflate(R.layout.first_layout1, null);  
  5.         View view2 = inflater.inflate(R.layout.first_layout2, null);  
  6.         View view3 = inflater.inflate(R.layout.first_layout3, null);  
  7.         list.add(view1);  
  8.         list.add(view2);  
  9.         list.add(view3);  
  10.   
  11.         viewPager.setAdapter(pagerAdapter);  
  12.         viewPager.setOnPageChangeListener(new OnPageChangeListener() {  
  13.             @Override  
  14.             public void onPageSelected(int arg0) {  
  15.                 setDots(arg0);  
  16.             }  
  17.   
  18.             @Override  
  19.             public void onPageScrolled(int arg0, float arg1, int arg2) {  
  20.             }  
  21.   
  22.             @Override  
  23.             public void onPageScrollStateChanged(int arg0) {  
  24.             }  
  25.         });  
  26.     }  
  1. private PagerAdapter pagerAdapter = new PagerAdapter() {  
  1.         <span style="white-space:pre">    </span>//官方建议这么写  
  2.         @Override  
  3.         public boolean isViewFromObject(View arg0, Object arg1) {  
  4.             return arg0 == arg1;  
  5.         }  
  6.    <span style="white-space:pre">     </span>//返回一共有多少个界面  
  7.         @Override  
  8.         public int getCount() {  
  9.             return list.size();  
  10.         }  
  11. <span style="white-space:pre">        </span>//实例化一个item  
  12.         @Override  
  13.         public Object instantiateItem(ViewGroup container, int position) {  
  14.             container.addView(list.get(position));  
  15.             return list.get(position);  
  16.         }  
  17.  <span style="white-space:pre">       </span>//销毁一个item  
  18.         @Override  
  19.         public void destroyItem(ViewGroup container, int position, Object object) {  
  20.             container.removeView(list.get(position));  
  21.         }  
  22.   
  23.     };  

    适配器中必须要实现以上的四个方法。

    如果只有这几个页面,交互性肯定是不好的,所以需要添加“指示器”,用来标识当前的页面是哪一个!我在这里用点来实现。就像效果图显示的那样。

 

  1. /** 
  2.      * 初始化底部的点 
  3.      */  
  4.     private void initDots() {  
  5.         pointLayout = (LinearLayout) findViewById(R.id.point_layout);  
  6.         dots = new ImageView[list.size()];  
  7.         for (int i = 0; i < list.size(); i++) {  
  8.             dots[i] = (ImageView) pointLayout.getChildAt(i);  
  9.         }  
  10.         currentIndex = 0;  
  11.         dots[currentIndex].setBackgroundResource(R.drawable.dian_down);  
  12.     }  
  13.   
  14.     /** 
  15.      * 当滚动的时候更换点的背景图 
  16.      */  
  17.     private void setDots(int position) {  
  18.         if (position < 0 || position > list.size() - 1  
  19.                 || currentIndex == position) {  
  20.             return;  
  21.         }  
  22.         dots[position].setBackgroundResource(R.drawable.dian_down);  
  23.         dots[currentIndex].setBackgroundResource(R.drawable.dian);  
  24.         currentIndex = position;  
  25.     }  

    重点就是页面切换之后,点也要切换。这时候就用到了OnPageChangeListener中的onPageSelected(int arg0)这个方法了。

 

  1. @Override  
  2.             public void onPageSelected(int arg0) {  
  3.                 setDots(arg0);  
  4.             }  

三、Fragment + FragmentManager

 

    fragment相信大家在项目中肯定都用过。这个方法主要就是利用fragmentManager对fragment的事务管理功能。

  1. // 三个选项卡  
  2.     private LinearLayout tab1Layout, tab2Layout, tab3Layout;  
  3.     // 默认选中第一个tab  
  4.     private int index = 1;  
  5.     // fragment管理类  
  6.     private FragmentManager fragmentManager;  
  7.     // 三个fragment  
  8.     private Fragment tab1Fragment, tab2Fragment, tab3Fragment;  
  9.   
  10.     @Override  
  11.     protected void onCreate(Bundle savedInstanceState) {  
  12.         super.onCreate(savedInstanceState);  
  13.         setContentView(R.layout.activity_second);  
  14.         fragmentManager = getSupportFragmentManager();  
  15.         init();  
  16.     }  
  17.   
  18.     /** 
  19.      * 初始化控件 
  20.      */  
  21.     private void init() {  
  22.         tab1Layout = (LinearLayout) findViewById(R.id.tab1_layout);  
  23.         tab2Layout = (LinearLayout) findViewById(R.id.tab2_layout);  
  24.         tab3Layout = (LinearLayout) findViewById(R.id.tab3_layout);  
  25.   
  26.         tab1Layout.setOnClickListener(this);  
  27.         tab2Layout.setOnClickListener(this);  
  28.         tab3Layout.setOnClickListener(this);  
  29.         //  
  30.         setDefaultFragment();  
  31.     }  
  32.   
  33.     /** 
  34.      * 设置默认显示的fragment 
  35.      */  
  36.     private void setDefaultFragment() {  
  37.         FragmentTransaction transaction = fragmentManager.beginTransaction();  
  38.         tab1Fragment = new Tab1Fragment();  
  39.         transaction.replace(R.id.content_layout, tab1Fragment);  
  40.         transaction.commit();  
  41.     }  
  42.   
  43.     /** 
  44.      *切换fragment 
  45.      * @param newFragment 
  46.      */  
  47.     private void replaceFragment(Fragment newFragment) {  
  48.         FragmentTransaction transaction = fragmentManager.beginTransaction();  
  49.         if (!newFragment.isAdded()) {  
  50.             transaction.replace(R.id.content_layout, newFragment);  
  51.             transaction.commit();  
  52.         } else {  
  53.             transaction.show(newFragment);  
  54.         }  
  55.     }  
  56.   
  57.     /** 
  58.      * 改变现象卡的选中状态 
  59.      */  
  60.     private void clearStatus() {  
  61.         if (index == 1) {  
  62.             tab1Layout.setBackgroundColor(getResources().getColor(R.color.tab));  
  63.         } else if (index == 2) {  
  64.             tab2Layout.setBackgroundColor(getResources().getColor(R.color.tab));  
  65.         } else if (index == 3) {  
  66.             tab3Layout.setBackgroundColor(getResources().getColor(R.color.tab));  
  67.         }  
  68.     }  
  69.   
  70.     @Override  
  71.     public void onClick(View v) {  
  72.         clearStatus();  
  73.         switch (v.getId()) {  
  74.         case R.id.tab1_layout:  
  75.             if (tab1Fragment == null) {  
  76.                 tab1Fragment = new Tab1Fragment();  
  77.             }  
  78.             replaceFragment(tab1Fragment);  
  79.             tab1Layout.setBackgroundColor(getResources().getColor(  
  80.                     R.color.tab_down));  
  81.             index = 1;  
  82.             break;  
  83.         case R.id.tab2_layout:  
  84.             if (tab2Fragment == null) {  
  85.                 tab2Fragment = new Tab2Fragment();  
  86.             }  
  87.             replaceFragment(tab2Fragment);  
  88.             tab2Layout.setBackgroundColor(getResources().getColor(  
  89.                     R.color.tab_down));  
  90.             index = 2;  
  91.             break;  
  92.         case R.id.tab3_layout:  
  93.             if (tab3Fragment == null) {  
  94.                 tab3Fragment = new Tab3Fragment();  
  95.             }  
  96.             replaceFragment(tab3Fragment);  
  97.             tab3Layout.setBackgroundColor(getResources().getColor(  
  98.                     R.color.tab_down));  
  99.             index = 3;  
  100.             break;  
  101.         }  
  102.     }  

    每一个fragment对应一个布局,点击不同的按钮来切换页面。效果如下图:

 

四、ViewPager + Fragment + FragmentPagerAdapter

    如果想使用fragment的时候又想可以左右滑动,就可以使用这种方式。主要的部分就在viewpager的适配器。它的适配器继承FragmentPagerAdapter.

  1. package com.tab.view.demo3;  
  2.   
  3. import java.util.ArrayList;  
  4.   
  5. import android.support.v4.app.Fragment;  
  6. import android.support.v4.app.FragmentManager;  
  7. import android.support.v4.app.FragmentPagerAdapter;  
  8. public class FragmentAdapter extends FragmentPagerAdapter {  
  9.     private ArrayList<Fragment> list;  
  10.     public FragmentAdapter(FragmentManager fm, ArrayList<Fragment> list) {  
  11.         super(fm);  
  12.         this.list = list;  
  13.     }  
  14.     @Override  
  15.     public Fragment getItem(int arg0) {  
  16.         return list.get(arg0);  
  17.     }  
  18.     @Override  
  19.     public int getCount() {  
  20.         return list.size();  
  21.     }  
  22. }  

    需要传入FragmentManager对象和一个存放fragment的list对象。

  1. /** 
  2.      * 初始化viewpager 
  3.      */  
  4.     private void initViewPager() {  
  5.         viewPager = (ViewPager) findViewById(R.id.third_vp);  
  6.         fragmentsList = new ArrayList<>();  
  7.         Fragment fragment = new Tab1Fragment();  
  8.         fragmentsList.add(fragment);  
  9.         fragment = new Tab2Fragment();  
  10.         fragmentsList.add(fragment);  
  11.         fragment = new Tab3Fragment();  
  12.         fragmentsList.add(fragment);  
  13.   
  14.         viewPager.setAdapter(new FragmentAdapter(getSupportFragmentManager(),  
  15.                 fragmentsList));  
  16.         viewPager.setCurrentItem(0);  
  17.         viewPager.setOnPageChangeListener(this);  
  18.     }  


    对button添加点击事件。

  1. @Override  
  2.     public void onClick(View v) {  
  3.         switch (v.getId()) {  
  4.         case R.id.tab1_tv:  
  5.             viewPager.setCurrentItem(0);  
  6.             break;  
  7.         case R.id.tab2_tv:  
  8.             viewPager.setCurrentItem(1);  
  9.             break;  
  10.         case R.id.tab3_tv:  
  11.             viewPager.setCurrentItem(2);  
  12.             break;  
  13.         }  
  14.     }  

    我 在布局文件中添加了一个imageview作为指示器。如果想第一种tab类型界面的实现方式那样在onPageSelected()方法中进行设置,效 果是只能当页面完全切换过来之后才能把指示器移动过去。要想实现滑动页面的时候同时移动指示器,就需要在onPageScrolled()方法中进行设 置。

  1. @Override  
  2.     public void onPageScrolled(int position, float positionOffset,  
  3.             int positionOffsetPixels) {  
  4.         offset = (screen1_3 - cursorImg.getLayoutParams().width) / 2;  
  5.         Log.d("111", position + "--" + positionOffset + "--"  
  6.                 + positionOffsetPixels);  
  7.         final float scale = getResources().getDisplayMetrics().density;  
  8.         if (position == 0) {// 0<->1  
  9.             lp.leftMargin = (int) (positionOffsetPixels / 3) + offset;  
  10.         } else if (position == 1) {// 1<->2  
  11.             lp.leftMargin = (int) (positionOffsetPixels / 3) + screen1_3 +offset;  
  12.         }  
  13.         cursorImg.setLayoutParams(lp);  
  14.         currentIndex = position;  
  15.     }  


    onPageScrolled中的三个参数比较重要。第一个参数是position。它的含义是表示当前显示的界面中的第一个界面。意思就是的当滑动的时 候,有可能出现两个界面,position指的是左边的界面。第二个参数是positionOffset指的是偏移量的比例,取值范围是[0, 1)。第三个参数是positionOffsetPixels是指偏移的像素值。后两个参数都相对页面(一个page)来说的。

    我之前有看到过设置指示器的时候用的前两个参数的,我也试了一下,OK的。不过感觉比较复杂,看了一下官方api,用第三个参数更简单。关键就是理解第一个参数position。用这种方法我只在代码里有两个判断就可以完成了。

    效果图如下:

 

五、Viewpager + PagerTitleStrip / PagerTabStrip


    这种方式没有上一种效果好看,而且标题变动。看一下效果图:

 

    布局文件:

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:orientation="vertical" >  
  6.   
  7.     <android.support.v4.view.ViewPager  
  8.         android:id="@+id/fifth_vp"  
  9.         android:layout_width="wrap_content"  
  10.         android:layout_height="wrap_content"  
  11.         android:layout_gravity="center" >  
  12.   
  13.         <android.support.v4.view.PagerTabStrip  
  14.             android:id="@+id/fifth_strip"  
  15.             android:layout_width="wrap_content"  
  16.             android:layout_height="wrap_content"  
  17.             android:layout_gravity="top"  
  18.             android:background="#7EC0EE"  
  19.             android:padding="10dip" />  
  20.     </android.support.v4.view.ViewPager>  
  21.   
  22. </LinearLayout>  

    先来说一下PagerTitleStrip和PagerTabStrip的区别:PagerTitleStrip没有指示器,只有标题,且标题没有响应事 件;而PagerTabStrip是带有指示器的,当然也有标题,具有相应事件。二者的实现只在布局文件中有区别,只需要把 android.support.v4.view.PagerTabStrip改成 android.support.v4.viewPagerTitleStrip即可。
    代码中需要注意的就是,在适配器中重写getPageTitle(int)方法来显示标题。

  1. PagerAdapter pagerAdapter = new PagerAdapter() {          
  2.                 //此处省略其他的方法  
  1. // 重写此方法即可显示标题  
  2. @Override  
  3. public CharSequence getPageTitle(int position) {  
  4.     return titleList.get(position);  
  5. }  
  6. ;  



总结:

    我目前所遇到的tab类型界面的实现方式就是这么多了,若果大家有其他的实现方式,请留言。如果我写的有误,请留言指正,共同进步!谢谢!

    Demo下载地址:http://download.csdn.net/detail/crazy1235/8358671

posted @ 2015-09-22 10:30  Nicat  阅读(547)  评论(0编辑  收藏  举报