본문 바로가기

android

SlidingTabLayout 사용시 screenX 분배하기

반응형

원본 http://blog.csdn.net/xwhnew/article/details/40679791


《代码大全》一书中提到:当我们编程时,不要简单停留在“在一种编程语言上进行编程”,而是要“深入一种语言”编程。在前面两篇文章中,虽然初步实现了ViewPager+Tab的布局,但实际上还是有一些不足的。让我们把目光锁定在Tab:


可以发现,3个Tab加一起的宽度并没有填充屏幕整个宽度。实际上,Google这样的设计是为了应对多Tab的情形,当Tab总宽度超出屏幕宽度的时候,用户可以简单地通过滑动Tab看到后面未显示出来的Tab。这种情况其实还是比较多见的,比如众多的新闻阅读客户端和Google原生应用等等。

但是如果我们只有三个Tab,甚至只有两个Tab,想让整个Tab的宽度等于屏幕宽度,该如何做呢?第一反应自然是要略微折腾一下SlidingTabLayout.java的源码了。

经过研究发现,实际上如上图顶部Tab的实现通过HorizontalScrollView,然后add了一个又一个的view,这个View就是每个Tab的标题,而整个SlidingTabLayout是继承了HorizontalScrollView。

接下来的思路就很清晰了:首先获取屏幕的总宽度,然后传给SlidingTabLayout对象,在其类内部对每个Tab所占的宽度进行计算和分配,最后将每个Tab标题的视图add到HorizontalScrollView就OK了。

首先获取屏幕的宽度。这一操作我们在MainActivity中进行,下面列出这一段程序清单:

  1. <span style="white-space:pre">    </span>// 获取屏幕宽度  
  2.     private int caculateScreenX() {  
  3.         return getResources().getDisplayMetrics().widthPixels;  
  4.     }  

获取到屏幕宽度之后,接下来就是把这个值传递给SlidingTabLayout。回顾一下用法,上一篇文章里面提到,我们在添加Tab导航栏的时候,仅仅是实例化了SlidingTabLayout的对象,然后调用了setViewPager()方法而已。那么,我们不妨在该方法上面添加一个参数,用来传递屏幕宽度。当然,我们需要在SlidingTabLayout中声明一个全局的int变量,用来存放每个Tab的宽度值:

  1. <span style="white-space:pre">    </span>public void setViewPager(ViewPager viewPager, int screenX) {  
  2. <span style="white-space:pre">        </span>this.screenX = screenX / viewPager.viewPager.getAdapter().getCount();  
  3. <span style="white-space:pre">        </span>mTabStrip.removeAllViews();  
  4. <span style="white-space:pre">        </span>mViewPager = viewPager;  
  5. <span style="white-space:pre">        </span>if (viewPager != null) {  
  6. <span style="white-space:pre">            </span>viewPager.setOnPageChangeListener(new InternalViewPagerListener());  
  7. <span style="white-space:pre">            </span>populateTabStrip();  
  8. <span style="white-space:pre">        </span>}  
  9. <span style="white-space:pre">    </span>}  

跟随该方法,找到populateTabStrip()方法,可以看到其中通过一个for循环以此添加了各个Tab视图。而每个Tab视图均通过createDefaultTabView()方法创建,因此该方法最为关键。

找到这个方法之后我们发现,每个Tab的标题View实际上是TextView,那么我们只需将每个TextView的宽度制定为刚刚计算出来的screenX(每个Tab的宽度)即可。具体可参看下面的代码清单:

  1. <span style="white-space:pre">    </span>protected TextView createDefaultTabView(Context context) {  
  2.         TextView textView = new TextView(context);  
  3.         textView.setGravity(Gravity.CENTER);  
  4.         textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP);  
  5.         textView.setTypeface(Typeface.DEFAULT_BOLD);  
  6.         textView.setWidth(screenX);  
  7.   
  8.         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {  
  9.             // If we're running on Honeycomb or newer, then we can use the  
  10.             // Theme's  
  11.             // selectableItemBackground to ensure that the View has a pressed  
  12.             // state  
  13.             TypedValue outValue = new TypedValue();  
  14.             getContext().getTheme().resolveAttribute(  
  15.                     android.R.attr.selectableItemBackground, outValue, true);  
  16.             textView.setBackgroundResource(outValue.resourceId);  
  17.         }  
  18.   
  19.         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {  
  20.             // If we're running on ICS or newer, enable all-caps to match the  
  21.             // Action Bar tab style  
  22.             textView.setAllCaps(true);  
  23.         }  
  24.   
  25.         int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources()  
  26.                 .getDisplayMetrics().density);  
  27.         textView.setPadding(padding, padding, padding, padding);  
  28.   
  29.         return textView;  
  30.     }  


最后,我们修改MainActivity,把计算好的屏幕宽度传给SlidingTabLayout,Ctrl+F11看看效果吧!


不出意外的话,看到上图就说明已经成功了!

下面放上整个SlidingTabLayout修改后的类:

  1. /* 
  2.  * Copyright (C) 2013 The Android Open Source Project 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */  
  16.   
  17. package com.xwh.toolbardemo.view;  
  18.   
  19. import android.annotation.SuppressLint;  
  20. import android.content.Context;  
  21. import android.graphics.Typeface;  
  22. import android.os.Build;  
  23. import android.support.v4.view.PagerAdapter;  
  24. import android.support.v4.view.ViewPager;  
  25. import android.util.AttributeSet;  
  26. import android.util.Log;  
  27. import android.util.TypedValue;  
  28. import android.view.Gravity;  
  29. import android.view.LayoutInflater;  
  30. import android.view.View;  
  31. import android.widget.HorizontalScrollView;  
  32. import android.widget.TextView;  
  33.   
  34. /** 
  35.  * To be used with ViewPager to provide a tab indicator component which give 
  36.  * constant feedback as to the user's scroll progress. 
  37.  * <p> 
  38.  * To use the component, simply add it to your view hierarchy. Then in your 
  39.  * {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call 
  40.  * {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is 
  41.  * being used for. 
  42.  * <p> 
  43.  * The colors can be customized in two ways. The first and simplest is to 
  44.  * provide an array of colors via {@link #setSelectedIndicatorColors(int...)} 
  45.  * and {@link #setDividerColors(int...)}. The alternative is via the 
  46.  * {@link TabColorizer} interface which provides you complete control over which 
  47.  * color is used for any individual position. 
  48.  * <p> 
  49.  * The views used as tabs can be customized by calling 
  50.  * {@link #setCustomTabView(int, int)}, providing the layout ID of your custom 
  51.  * layout. 
  52.  */  
  53. public class SlidingTabLayout extends HorizontalScrollView {  
  54.   
  55.     /** 
  56.      * Allows complete control over the colors drawn in the tab layout. Set with 
  57.      * {@link #setCustomTabColorizer(TabColorizer)}. 
  58.      */  
  59.     public interface TabColorizer {  
  60.   
  61.         /** 
  62.          * @return return the color of the indicator used when {@code position} 
  63.          *         is selected. 
  64.          */  
  65.         int getIndicatorColor(int position);  
  66.   
  67.         /** 
  68.          * @return return the color of the divider drawn to the right of 
  69.          *         {@code position}. 
  70.          */  
  71.         int getDividerColor(int position);  
  72.   
  73.     }  
  74.   
  75.     private static final int TITLE_OFFSET_DIPS = 24;  
  76.     private static final int TAB_VIEW_PADDING_DIPS = 16;  
  77.     private static final int TAB_VIEW_TEXT_SIZE_SP = 12;  
  78.   
  79.     private int mTitleOffset;  
  80.   
  81.     private int mTabViewLayoutId;  
  82.     private int mTabViewTextViewId;  
  83.   
  84.     private ViewPager mViewPager;  
  85.     private ViewPager.OnPageChangeListener mViewPagerPageChangeListener;  
  86.   
  87.     private final SlidingTabStrip mTabStrip;  
  88.   
  89.     private int screenX;  
  90.   
  91.     public SlidingTabLayout(Context context) {  
  92.         this(context, null);  
  93.     }  
  94.   
  95.     public SlidingTabLayout(Context context, AttributeSet attrs) {  
  96.         this(context, attrs, 0);  
  97.     }  
  98.   
  99.     public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) {  
  100.         super(context, attrs, defStyle);  
  101.   
  102.         // Disable the Scroll Bar  
  103.         setHorizontalScrollBarEnabled(false);  
  104.         // Make sure that the Tab Strips fills this View  
  105.         setFillViewport(true);  
  106.   
  107.         mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources()  
  108.                 .getDisplayMetrics().density);  
  109.   
  110.         mTabStrip = new SlidingTabStrip(context);  
  111.         addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);  
  112.     }  
  113.   
  114.     /** 
  115.      * Set the custom {@link TabColorizer} to be used. 
  116.      *  
  117.      * If you only require simple custmisation then you can use 
  118.      * {@link #setSelectedIndicatorColors(int...)} and 
  119.      * {@link #setDividerColors(int...)} to achieve similar effects. 
  120.      */  
  121.     public void setCustomTabColorizer(TabColorizer tabColorizer) {  
  122.         mTabStrip.setCustomTabColorizer(tabColorizer);  
  123.     }  
  124.   
  125.     /** 
  126.      * Sets the colors to be used for indicating the selected tab. These colors 
  127.      * are treated as a circular array. Providing one color will mean that all 
  128.      * tabs are indicated with the same color. 
  129.      */  
  130.     public void setSelectedIndicatorColors(int... colors) {  
  131.         mTabStrip.setSelectedIndicatorColors(colors);  
  132.     }  
  133.   
  134.     /** 
  135.      * Sets the colors to be used for tab dividers. These colors are treated as 
  136.      * a circular array. Providing one color will mean that all tabs are 
  137.      * indicated with the same color. 
  138.      */  
  139.     public void setDividerColors(int... colors) {  
  140.         mTabStrip.setDividerColors(colors);  
  141.     }  
  142.   
  143.     /** 
  144.      * Set the {@link ViewPager.OnPageChangeListener}. When using 
  145.      * {@link SlidingTabLayout} you are required to set any 
  146.      * {@link ViewPager.OnPageChangeListener} through this method. This is so 
  147.      * that the layout can update it's scroll position correctly. 
  148.      *  
  149.      * @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener) 
  150.      */  
  151.     public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {  
  152.         mViewPagerPageChangeListener = listener;  
  153.     }  
  154.   
  155.     /** 
  156.      * Set the custom layout to be inflated for the tab views. 
  157.      *  
  158.      * @param layoutResId 
  159.      *            Layout id to be inflated 
  160.      * @param textViewId 
  161.      *            id of the {@link TextView} in the inflated view 
  162.      */  
  163.     public void setCustomTabView(int layoutResId, int textViewId) {  
  164.         mTabViewLayoutId = layoutResId;  
  165.         mTabViewTextViewId = textViewId;  
  166.     }  
  167.   
  168.     /** 
  169.      * Sets the associated view pager. Note that the assumption here is that the 
  170.      * pager content (number of tabs and tab titles) does not change after this 
  171.      * call has been made. 
  172.      */  
  173.     public void setViewPager(ViewPager viewPager, int screenX) {  
  174.         this.screenX = (screenX / viewPager.getAdapter().getCount());  
  175.         mTabStrip.removeAllViews();  
  176.   
  177.         mViewPager = viewPager;  
  178.         if (viewPager != null) {  
  179.             viewPager.setOnPageChangeListener(new InternalViewPagerListener());  
  180.             populateTabStrip();  
  181.         }  
  182.     }  
  183.   
  184.     /** 
  185.      * Create a default view to be used for tabs. This is called if a custom tab 
  186.      * view is not set via {@link #setCustomTabView(int, int)}. 
  187.      */  
  188.     @SuppressLint("NewApi")  
  189.     protected TextView createDefaultTabView(Context context) {  
  190.         TextView textView = new TextView(context);  
  191.         textView.setGravity(Gravity.CENTER);  
  192.         textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP);  
  193.         textView.setTypeface(Typeface.DEFAULT_BOLD);  
  194.         textView.setWidth(screenX);  
  195.   
  196.         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {  
  197.             // If we're running on Honeycomb or newer, then we can use the  
  198.             // Theme's  
  199.             // selectableItemBackground to ensure that the View has a pressed  
  200.             // state  
  201.             TypedValue outValue = new TypedValue();  
  202.             getContext().getTheme().resolveAttribute(  
  203.                     android.R.attr.selectableItemBackground, outValue, true);  
  204.             textView.setBackgroundResource(outValue.resourceId);  
  205.         }  
  206.   
  207.         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {  
  208.             // If we're running on ICS or newer, enable all-caps to match the  
  209.             // Action Bar tab style  
  210.             textView.setAllCaps(true);  
  211.         }  
  212.   
  213.         int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources()  
  214.                 .getDisplayMetrics().density);  
  215.         textView.setPadding(padding, padding, padding, padding);  
  216.   
  217.         return textView;  
  218.     }  
  219.   
  220.     private void populateTabStrip() {  
  221.         final PagerAdapter adapter = mViewPager.getAdapter();  
  222.         final View.OnClickListener tabClickListener = new TabClickListener();  
  223.   
  224.         for (int i = 0; i < adapter.getCount(); i++) {  
  225.             View tabView = null;  
  226.             TextView tabTitleView = null;  
  227.   
  228.             if (mTabViewLayoutId != 0) {  
  229.                 // If there is a custom tab view layout id set, try and inflate  
  230.                 // it  
  231.                 tabView = LayoutInflater.from(getContext()).inflate(  
  232.                         mTabViewLayoutId, mTabStrip, false);  
  233.                 tabTitleView = (TextView) tabView  
  234.                         .findViewById(mTabViewTextViewId);  
  235.             }  
  236.   
  237.             if (tabView == null) {  
  238.                 tabView = createDefaultTabView(getContext());  
  239.             }  
  240.   
  241.             if (tabTitleView == null && TextView.class.isInstance(tabView)) {  
  242.                 tabTitleView = (TextView) tabView;  
  243.             }  
  244.   
  245.             tabTitleView.setText(adapter.getPageTitle(i));  
  246.             tabView.setOnClickListener(tabClickListener);  
  247.   
  248.             mTabStrip.addView(tabView);  
  249.         }  
  250.     }  
  251.   
  252.     @Override  
  253.     protected void onAttachedToWindow() {  
  254.         super.onAttachedToWindow();  
  255.   
  256.         if (mViewPager != null) {  
  257.             scrollToTab(mViewPager.getCurrentItem(), 0);  
  258.         }  
  259.     }  
  260.   
  261.     private void scrollToTab(int tabIndex, int positionOffset) {  
  262.         final int tabStripChildCount = mTabStrip.getChildCount();  
  263.         if (tabStripChildCount == 0 || tabIndex < 0  
  264.                 || tabIndex >= tabStripChildCount) {  
  265.             return;  
  266.         }  
  267.   
  268.         View selectedChild = mTabStrip.getChildAt(tabIndex);  
  269.         if (selectedChild != null) {  
  270.             int targetScrollX = selectedChild.getLeft() + positionOffset;  
  271.   
  272.             if (tabIndex > 0 || positionOffset > 0) {  
  273.                 // If we're not at the first child and are mid-scroll, make sure  
  274.                 // we obey the offset  
  275.                 targetScrollX -= mTitleOffset;  
  276.             }  
  277.   
  278.             scrollTo(targetScrollX, 0);  
  279.         }  
  280.     }  
  281.   
  282.     private class InternalViewPagerListener implements  
  283.             ViewPager.OnPageChangeListener {  
  284.         private int mScrollState;  
  285.   
  286.         @Override  
  287.         public void onPageScrolled(int position, float positionOffset,  
  288.                 int positionOffsetPixels) {  
  289.             int tabStripChildCount = mTabStrip.getChildCount();  
  290.             if ((tabStripChildCount == 0) || (position < 0)  
  291.                     || (position >= tabStripChildCount)) {  
  292.                 return;  
  293.             }  
  294.   
  295.             mTabStrip.onViewPagerPageChanged(position, positionOffset);  
  296.   
  297.             View selectedTitle = mTabStrip.getChildAt(position);  
  298.             int extraOffset = (selectedTitle != null) ? (int) (positionOffset * selectedTitle  
  299.                     .getWidth()) : 0;  
  300.             scrollToTab(position, extraOffset);  
  301.   
  302.             if (mViewPagerPageChangeListener != null) {  
  303.                 mViewPagerPageChangeListener.onPageScrolled(position,  
  304.                         positionOffset, positionOffsetPixels);  
  305.             }  
  306.         }  
  307.   
  308.         @Override  
  309.         public void onPageScrollStateChanged(int state) {  
  310.             mScrollState = state;  
  311.   
  312.             if (mViewPagerPageChangeListener != null) {  
  313.                 mViewPagerPageChangeListener.onPageScrollStateChanged(state);  
  314.             }  
  315.         }  
  316.   
  317.         @Override  
  318.         public void onPageSelected(int position) {  
  319.             if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {  
  320.                 mTabStrip.onViewPagerPageChanged(position, 0f);  
  321.                 scrollToTab(position, 0);  
  322.             }  
  323.   
  324.             if (mViewPagerPageChangeListener != null) {  
  325.                 mViewPagerPageChangeListener.onPageSelected(position);  
  326.             }  
  327.         }  
  328.   
  329.     }  
  330.   
  331.     private class TabClickListener implements View.OnClickListener {  
  332.         @Override  
  333.         public void onClick(View v) {  
  334.             for (int i = 0; i < mTabStrip.getChildCount(); i++) {  
  335.                 if (v == mTabStrip.getChildAt(i)) {  
  336.                     mViewPager.setCurrentItem(i);  
  337.                     return;  
  338.                 }  
  339.             }  
  340.         }  
  341.     }  
  342.   
  343. }  


반응형