今天介绍现在比较流行的一种app布局方式:内外层嵌套Tab,子Tab可以实现滑动切换。
实现原理:FragmentTabHost+Fragment实现第一层Tab。
在Fragment里利用ViewPaper,实现第二层Tab,也就是子Tab。
从原理可以看出,这两层Tab是完全解耦的,没有任何事件和数据联系,那么先介绍第一层怎么实现,由于google对FragmentTabHost+Fragment封装的比较好,实现过程无非是FragmentTabHost+Fragment的使用过程。
可以用一句话来说明过程:就是给FragmentTabHost设置一定数量的按钮背景。
- public static Context context;
- /**
- * 中间内容的fragment
- */
- private Fragment frg_content;
- /**
- * fragmentTabHost
- */
- private FragmentTabHost frg_tabHost;
- /**
- * 定义数组来存放按钮图片
- */
- private int intImageViewArray[] = {
- R.drawable.selector_bt_bookstore_featured, R.drawable.selector_bt_bookstore_top,
- R.drawable.selector_bt_bookstore_category, R.drawable.selector_bt_bookstore_search
- };
- /**
- * 定义数组来存放Fragment界面
- */
- private Class fragmentArray[] = {
- BookstoreFeaturedFragment.class, BookstoreTopFragment.class,
- BookstoreCategoryFragment.class, BookstoreSearchFragment.class
- };
- /**
- * Tab选项卡的文字
- */
- private String txt_Array[] = {
- "精选", "排行", "分类", "搜索"
- };
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- overridePendingTransition(R.anim.slide_right_in, R.anim.slide_mid_left_out);
- setContentView(R.layout.ac_bookstore_main);
- // 得到fragment的个数
- int count = intImageViewArray.length;
- // 实例化TabHost对象,得到TabHost
- frg_tabHost = (FragmentTabHost) findViewById(android.R.id.tabhost);
- frg_tabHost.setup(this, getSupportFragmentManager(), R.id.realtabcontent);
- for (int i = 0; i < count; i++) {
- // 为每一个Tab按钮设置图标、文字和内容
- ImageView imageView = new ImageView(this);
- // imageView.setImageResource(intImageViewArray[i]);
- imageView.setImageDrawable(skinContext.getResources().getDrawable(intImageViewArray[i]));
- imageView.setScaleType(ScaleType.CENTER_CROP);
- TabSpec tabSpec = frg_tabHost.newTabSpec(txt_Array[i]).setIndicator(imageView);
- // 将Tab按钮添加进Tab选项卡中
- frg_tabHost.addTab(tabSpec, fragmentArray[i], null);
- }
- frg_tabHost.setCurrentTab(0);
- getSlidingMenu().setSlidingEnabled(false);
- }
这样就实现了点击哪个按钮,就显示哪个fragment。
这样就存在一个问题,就是tab布局问题,显示在上面还是下面:
tab显示在底部的布局:
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical" >
- <FrameLayout
- android:id="@+id/realtabcontent"
- android:layout_width="fill_parent"
- android:layout_height="0dip"
- android:layout_weight="1" />
- <android.support.v4.app.FragmentTabHost
- android:id="@android:id/tabhost"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content" >
- <FrameLayout
- android:id="@android:id/tabcontent"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_weight="0" />
- </android.support.v4.app.FragmentTabHost>
- </LinearLayout>
tab显示在顶部的布局:
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical" >
- <android.support.v4.app.FragmentTabHost
- android:id="@android:id/tabhost"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content" >
- <FrameLayout
- android:id="@android:id/tabcontent"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_weight="0" />
- </android.support.v4.app.FragmentTabHost>
- <FrameLayout
- android:id="@+id/realtabcontent"
- android:layout_width="fill_parent"
- android:layout_height="0dip"
- android:layout_weight="1" />
- </LinearLayout>
对比可以看出:realtabcontent是fragment显示的位置,一般是竖向填充:layout_weight="1"
tabhost是tab按钮放置位置,一般是横向填充,高度根据背景自适应。
tabcontent是干什么用的呢?看下代码:
- public void switchContent(Fragment fragment) {
- getSupportFragmentManager().beginTransaction().replace(android.R.id.tabcontent, fragment)
- .commit();
- }
这行代码的作用是 显示frament页,所以我认为tabcontent指的是当前frament页面,可以用于页面的刷新。
这样第一层tab就实现了。
我们在实现第二层tab,其实即使ViewPaper的使用方法:
ViewPager的使用比较简单,完全可以当做一个listview使用:
- viewpager_featured.setAdapter(viewPagerAdapter);// 设置ViewPager的适配器
- viewpager_featured.setCurrentItem(0);
还可以设置缓存页面个数:viewpager_featured.setOffscreenPageLimit(2);用来防止页面刷新,这个数值越大会占用越多内存。所以放置数据刷新的方法是设置绑定数据的状态,根据数据状态觉得是否要刷新。
主要讲解下ViewPager和tab结合的时候的使用,特别是有动画效果的时候的使用。
这里介绍一种tab实现方式:1.tab 个数可删减。2、焦点tab显示在可见位置
这里tab按钮用textview来实现,先动态的初始化tab个数,以及根据显示字数多少设置宽度:
- /**
- * 设置侧滑数据
- */
- private void setSlideMenu() {
- // 包含TextView的LinearLayout
- int two_width = 60;
- int three_width = 90;
- int four_width = 104;
- if ((Integer.parseInt(DeviceInfoUtils.getWidth(activity)) * Integer.parseInt(DeviceInfoUtils
- .getHeight(activity))) >= 1080 * 1720) {
- two_width = 120;
- three_width = 180;
- four_width = 200;
- } else if ((Integer.parseInt(DeviceInfoUtils.getWidth(activity)) * Integer.parseInt(DeviceInfoUtils
- .getHeight(activity))) > 600 * 1280) {
- two_width = 90;
- three_width = 135;
- four_width = 156;
- } else if ((Integer.parseInt(DeviceInfoUtils.getWidth(activity)) * Integer.parseInt(DeviceInfoUtils
- .getHeight(activity))) == 640 * 960) {
- two_width = 80;
- three_width = 125;
- four_width = 135;
- } else {
- two_width = 60;
- three_width = 90;
- four_width = 100;
- }
- // 参数设置
- LinearLayout.LayoutParams menuLinerLayoutParames = new LinearLayout.LayoutParams(four_width,
- LinearLayout.LayoutParams.MATCH_PARENT);
- menuLinerLayoutParames.gravity = Gravity.CENTER;
- menuLinerLayoutParames.leftMargin = 5;
- menuLinerLayoutParames.rightMargin = 5;
- LinearLayout.LayoutParams menuLinerLayoutParames1 = new LinearLayout.LayoutParams(two_width,
- LinearLayout.LayoutParams.MATCH_PARENT);
- menuLinerLayoutParames1.gravity = Gravity.CENTER;
- menuLinerLayoutParames1.leftMargin = 5;
- menuLinerLayoutParames1.rightMargin = 5;
- LinearLayout.LayoutParams menuLinerLayoutParames3 = new LinearLayout.LayoutParams(three_width,
- LinearLayout.LayoutParams.MATCH_PARENT);
- menuLinerLayoutParames3.gravity = Gravity.CENTER;
- menuLinerLayoutParames3.leftMargin = 5;
- menuLinerLayoutParames3.rightMargin = 5;
- // 添加TextView控件
- for (int i = 0; i < listChannelMenu.size(); i++) {
- TextView tvMenu = new TextView(activity);
- tvMenu.setLayoutParams(new LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
- LinearLayout.LayoutParams.MATCH_PARENT));
- tvMenu.setText(listChannelMenu.get(i).getChannelTypeName());
- tvMenu.setGravity(Gravity.CENTER);
- tvMenu.setTypeface(Typeface.SERIF);
- tvMenu.setTextSize(15);
- // tvMenu.setBackgroundResource(R.drawable.selector_bt_channel);
- tvMenu.setBackgroundDrawable((BookstoreActivity.context).getResources().getDrawable(
- R.drawable.selector_bt_channel));
- tvMenu.setOnClickListener(this);
- if (tvMenu.getText().toString().trim().length() == 2) {
- linearLayout_menu.addView(tvMenu, menuLinerLayoutParames1);
- } else if (tvMenu.getText().toString().trim().length() == 3) {
- linearLayout_menu.addView(tvMenu, menuLinerLayoutParames3);
- } else {
- linearLayout_menu.addView(tvMenu, menuLinerLayoutParames);
- }
- // 当点击上面的导航菜单时下方的控件的内容
- tvMenu.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- if (v.isClickable()) {
- TextView textMenu = (TextView) v;
- for (int i = 0; i < listChannelMenu.size(); i++) {
- if (textMenu.getText().toString().trim()
- .equals(listChannelMenu.get (i).getChannelTypeName())) {
- // 选中菜单栏的菜单
- setSelectedState(i);
- // 点击菜单时改变内容
- viewpager_featured.setCurrentItem(i);
- }
- }
- }
- }
- });
- }
- viewPagerAdapter.addItem(listChannelMenu, true);
- }
根据屏幕大小动态计算了,textview按钮的属性。并且设置了点击监听,根据tab的数量给adapter绑定数据。
设置适配器:
- setSlideMenu();
- TextView mTextView = (TextView) linearLayout_menu.getChildAt(0);
- mTextView.setSelected(true);
- viewpager_featured.setAdapter(viewPagerAdapter);// 设置ViewPager的适配器
- viewpager_featured.setCurrentItem(0);
这样点击tab,ViewPaper可以自动切换了,那滑动ViewPapert,tab调整显示位置实现:
- viewpager_featured.setOnPageChangeListener(new OnPageChangeListener() {
- @SuppressLint("NewApi")
- @Override
- public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
- // if (blnFlag) {
- getMenuXPosition();
- }
- @Override
- public void onPageSelected(int position) {
- if (listMenuPosition == null || listMenuPosition.size() <= 0) {
- getMenuXPosition();
- }
- int moveLeft = (int) listMenuPosition.get(position) - (int) listMenuPosition.get(1);
- hScroll_menu.smoothScrollTo(moveLeft, 0);
- setSelectedState(position);
- }
- @Override
- public void onPageScrollStateChanged(int state) {
- }
- });
- /**
- * 存放菜单栏的位置
- */
- private List<Integer> listMenuPosition;
- private void getMenuXPosition() {
- for (int i = 0; i < linearLayout_menu.getChildCount(); i++) {
- TextView textView = (TextView) linearLayout_menu.getChildAt(i);
- listMenuPosition.add(textView.getLeft());
- }
- }
是通过记录每个textview的左侧位置,每次滑动的时候都都让scrollview滑动一定位置,保证焦点textview 的tab可见。最后给出布局文件:
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="@dimen/bookstore_featured_menu_height"
- android:orientation="horizontal" >
- <HorizontalScrollView
- android:id="@+id/scrollview_menu"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:scrollbars="none" >
- <LinearLayout
- android:id="@+id/linearLayout_menu"
- android:layout_width="0dip"
- android:layout_height="match_parent"
- android:background="@drawable/bookstore_featured_navigation_bg"
- android:gravity="center_vertical"
- android:orientation="horizontal" >
- </LinearLayout>
- </HorizontalScrollView>
- </LinearLayout>
- <RelativeLayout
- android:id="@+id/relativelayout_viewpager"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
- <android.support.v4.view.ViewPager
- android:id="@+id/viewpager_featured_pager"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
- </android.support.v4.view.ViewPager>
- <ProgressBar
- android:id="@+id/progressBar_loading"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerInParent="true" />
- </RelativeLayout>
和ViewPaper配合实现tab的方式有很多,关键根据需求不同来选择实现方式,textview、button、radiobutton、iamgeview都可以用来实现tab。