今天介绍现在比较流行的一种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。
FragmentTabHost+Fragment+ViewPager实现内外层嵌套Tab,布布扣,bubuko.com
FragmentTabHost+Fragment+ViewPager实现内外层嵌套Tab
原文:http://blog.csdn.net/xiangxue336/article/details/22804971