android TabLayout实现京东详情效果
Google在2015的IO大会上,给我们带来了更加详细的Material Design设计规范,同时,也给我们带来了全新的Android Design Support Library,在这个support库里面,Google给我们提供了更加规范的MD设计风格的控件。最重要的是,Android Design Support Library的兼容性更广,直接可以向下兼容到Android 2.2。
这两天需要做一个仿京东详情的页面,上面的Tab切换,以前都是自己写Viewpager+fragment,或者Indicator的深度定制,一直想尝试一下TabLayout,于是就有了下面的坑。
然后下面是我简单的实现效果(个人觉得很坑,还不如自己自定义的导航器)
添加引用库
- dependencies {
- compile fileTree(dir: 'libs', include: ['*.jar'])
- compile 'com.android.support:appcompat-v7:24.2.0'
- compile 'com.android.support:design:24.2.0'
- compile 'com.android.support:recyclerview-v7:24.2.0'
- compile 'com.android.support:cardview-v7:24.2.0'
- }
Toolbar与TabLayout
我们来看一下实现的布局:
- <pre code_snippet_id="1857898" snippet_file_name="blog_20160830_2_1741274" name="code" class="html"><?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@color/c12"
- android:gravity="center_vertical"
- android:minHeight="45dp"
- android:orientation="horizontal"
- android:paddingLeft="15dp"
- android:paddingRight="15dp">
- <ImageView
- android:id="@+id/back"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:background="@drawable/back_icon" />
- <LinearLayout
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:orientation="horizontal">
- <android.support.design.widget.TabLayout
- android:id="@+id/tabLayout"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- app:tabTextAppearance="@style/TabLayoutTextStyle"
- app:tabGravity="center"
- app:tabMode="fixed"
- app:tabTextColor="@color/c7"
- app:tabSelectedTextColor="@color/c8"/>
- </LinearLayout>
- <ImageView
- android:id="@+id/toolbar_more"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="10dp"
- android:background="@drawable/more_icon" />
- </LinearLayout>
- <View style="@style/horizontal_line" />
- <android.support.v4.view.ViewPager
- android:id="@+id/viewPager"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1" />
- <View style="@style/horizontal_line" />
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="48dp"
- android:background="@color/c12"
- android:orientation="horizontal">
- <LinearLayout
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1">
- <TextView
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:gravity="center"
- android:text="收藏"
- android:textSize="10sp" />
- <View style="@style/vertical_line" />
- <TextView
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:gravity="center"
- android:text="购物车"
- android:textSize="10sp" />
- </LinearLayout>
- <LinearLayout
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1.5"
- android:background="@color/c8"
- android:gravity="center">
- <TextView
- style="@style/style_c12_s16"
- android:gravity="center"
- android:text="加入购物车" />
- </LinearLayout>
- </LinearLayout>
- </LinearLayout></pre><br><br>
这布局文件最关键的一点就是android.support.design.widget.TabLayout 标签中的app:tabMode=”scrollable”,他设置tab的模式为“可滑动的”。
其他的用法和Indicator的用法差不多,都需要设置适配器,然后通过数据实现页面的适配。直接上代码
Adapter
- <span style="font-size:10px;">public class ProductDetailPagerAdapter extends FragmentPagerAdapter {
- private List<Fragment> mFragments=null;
- private List<String> mTitles=null;
- public ProductDetailPagerAdapter(FragmentManager fm, List<Fragment> mFragments,List<String> mTitles) {
- super(fm);
- this.mFragments =mFragments;
- this.mTitles=mTitles;
- }
- public ProductDetailPagerAdapter(FragmentManager fm, Fragment... fragments) {
- super(fm);
- this.mFragments = Arrays.asList(fragments);
- }
- @Override
- public Fragment getItem(int position) {
- return mFragments.get(position);
- }
- @Override
- public int getCount() {
- return mFragments.size();
- }
- @Override
- public CharSequence getPageTitle(int position) {
- return mTitles.get(position);
- }
- }</span>
主页面的相关逻辑,这里的Fragment就是简单的Fragment。
- <pre code_snippet_id="1857898" snippet_file_name="blog_20160830_4_1152629" name="code" class="html">public class ProductDetailsActivity extends BaseActivity {
- @BindView(R.id.viewPager)
- ViewPager viewPager;
- @BindView(R.id.toolbar_more)
- ImageView toolbarMore;
- @BindView(R.id.tabLayout)
- TabLayout tabLayout;
- private List<Fragment> mFragments;
- private String[] titles = new String[]{"商品", "详情"};
- private ProductDetailPagerAdapter productPagerAdapter = null;
- private MorePopupWindow popupWindow = null;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_product_details);
- ButterKnife.bind(this);
- init();
- }
- private void init() {
- initViewPager();
- }
- private void initViewPager() {
- mFragments = new ArrayList<>();
- mFragments.add(new ProductFragment());
- mFragments.add(new ProductDetailFragment());
- productPagerAdapter = new ProductDetailPagerAdapter(getSupportFragmentManager(), mFragments, Arrays.asList(titles));
- viewPager.setOffscreenPageLimit(2);
- viewPager.setAdapter(productPagerAdapter);
- viewPager.setCurrentItem(1);
- tabLayout.setupWithViewPager(viewPager);
- }
- @OnClick(R.id.back)
- public void backClick() {
- finish();
- }
- @OnClick(R.id.toolbar_more)
- public void moreClick() {
- }
- private AdapterView.OnItemClickListener onItemClickListener = new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position,
- long id) {
- popupWindow.dismiss();
- }
- };
- public static void open(Context context) {
- Intent intent = new Intent(context, ProductDetailsActivity.class);
- context.startActivity(intent);
- }
- }</pre><br><br>
tabmode有两个属性值:
MODE_FIXED:Fixed tabs display all tabs concurrently and are best used with content that benefits from quick pivots between tabs.
MODE_SCROLLABLE:Scrollable tabs display a subset of tabs at any given moment, and can contain longer tab labels and a larger number of tabs.
MODE_SCROLLABLE适合很多tabs的情况,是可以滚动的,如果你要实现京东的那种挤在一起的效果就需要MODE_FIXED了。
MODE_SCROLLABLE:Scrollable tabs display a subset of tabs at any given moment, and can contain longer tab labels and a larger number of tabs.
MODE_SCROLLABLE适合很多tabs的情况,是可以滚动的,如果你要实现京东的那种挤在一起的效果就需要MODE_FIXED了。
为了更好的满足开发需要,TabLayout实现了自定义TabLayout的样式,然后通过引入
- app:tabTextAppearance=""
自定义icon添加到tab
当前的TabLayout没有方法让我们去添加icon,我们可以使用SpannableString结合ImageSpan来实现
- private int[] imageResId = {
- R.drawable.ic_one,
- R.drawable.ic_two,
- R.drawable.ic_three
- };
- // ...
- @Override
- public CharSequence getPageTitle(int position) {
- // Generate title based on item position
- // return tabTitles[position];
- Drawable image = context.getResources().getDrawable(imageResId[position]);
- image.setBounds(0, 0, image.getIntrinsicWidth(), image.getIntrinsicHeight());
- SpannableString sb = new SpannableString(" ");
- ImageSpan imageSpan = new ImageSpan(image, ImageSpan.ALIGN_BOTTOM);
- sb.setSpan(imageSpan, 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- return sb;
- }
运行,发现没有显示,这是因为TabLayout创建的tab默认设置textAllCaps属性为true,这阻止了ImageSpan被渲染出来,可以通过下面的样式文件定义来改变:
- <style name="MyCustomTabLayout" parent="Widget.Design.TabLayout">
- <item name="tabTextAppearance">@style/MyCustomTextAppearance</item>
- </style>
- <style name="MyCustomTextAppearance" parent="TextAppearance.Design.Tab">
- <item name="textAllCaps">false</item>
- </style>
然后在getPageTitle方法中设置上有标题的tab
- @Override
- public CharSequence getPageTitle(int position) {
- // Generate title based on item position
- Drawable image = context.getResources().getDrawable(imageResId[position]);
- image.setBounds(0, 0, image.getIntrinsicWidth(), image.getIntrinsicHeight());
- // Replace blank spaces with image icon
- SpannableString sb = new SpannableString(" " + tabTitles[position]);
- ImageSpan imageSpan = new ImageSpan(image, ImageSpan.ALIGN_BOTTOM);
- sb.setSpan(imageSpan, 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- return sb;
- }
TabLayout还支持自定义View,通过getTabView来设置,这里就不讲怎么实现了,有兴趣的可以自行研究。