最近在项目中遇到了TabLayout,android有自带的TabLayout控件,对于一般需求不大,不需要太大的自定义要求的来说android自带的也就够用了。但是我项目上遇到的需要自定义TabLayout的Indicator的样式,我的需求是需要圆角的,结果研究了大半天也没有发现xml中哪儿可以直接改,然后也百度了许久也并没有这方面的说法,被逼无赖之后就去看了TabLayout的源码,在源码中找了很久终于找到了TabLayout类中有一个draw()方法,也就是说这就是绘制Indiator的方法。先看一波效果图:
找到draw()方法之后单是不能去修改源码啊,所以最后就把TabLayout里的源码全部copy到了自己的项目中,这样就想怎么改就怎么改了 下面的代码有点多 我先贴一下TabLayout的源码目录 ,然后先讲重点的地方,最后再把所有的源码贴在后面,要用的小盆友直接去后面粘代码到自己的项目就ok。
这就是TabLayout的全部源码了,是感觉有点多哈。是本来就真的多得抠脚!
下面我给大家贴修改Indicator样式的代码 他的位置在TabLayout类里面得 其实改的地方不多的
@Override public void draw(Canvas canvas) { super.draw(canvas); // 设置Indicator的长度 // Thick colored underline below the current selection if (mIndicatorLeft >= 0 && mIndicatorRight > mIndicatorLeft) { canvas.drawRect(mIndicatorLeft + 120, getHeight() - mSelectedIndicatorHeight, mIndicatorRight - 120, getHeight(), mSelectedIndicatorPaint); // 设置Indicator的样式,我这边是设置圆角 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { canvas.drawRoundRect(mIndicatorLeft,getHeight() - mSelectedIndicatorHeight, mIndicatorRight, getHeight(),30,30,mSelectedIndicatorPaint); } } } }
第一个修改Indicator长度的时候在比如TabLayout的title只有两个或者三个的时候,但是又想控制Indicator的长度,那就是用上面的按个方法了,长度是自己可以控制的,你们可以看大里面那个120,这是自己调的数值,这个120的意思就是Indicator原来的长度左边减去120,右边再减去120,然后留下来的长度也就是我想要的,这个具体跟句自己的需求去定义,每个人的要求不一样,这个都是可以自己动手去调的,调到自己合适的就ok了。
第二个就是修改Indicator的样式,我这边就是用的一个圆角的样式。
在xml代码:
<com.hconline.antapp.library.wiget.TabLayout android:id="@+id/tablayout" android:layout_width="match_parent" android:layout_height="match_parent" app:tabIndicatorColor="@color/colorPrimary" app:tabIndicatorHeight="5dp" app:tabSelectedTextColor="@color/colorPrimary" app:tabTextColor="@color/share_record_name"> </com.hconline.antapp.library.wiget.TabLayout>
上面的属性我一次给大家解释下吧!
app:tabIndicatorColor="@color/colorPrimary"
Indicator的颜色设置
app:tabIndicatorHeight="5dp"Indicator的高度设置
app:tabSelectedTextColor="@color/colorPrimary"title别选中的颜色设置
app:tabTextColor="@color/share_record_name"title默认颜色设置
上面的这些属性都是根据大家自己项目的需求去设置的。
最后我给大家一次贴出来TabLayout的代码 方便大家去使用,有点长,请谅解:
Class AnimationUtils
import android.support.v4.view.animation.FastOutLinearInInterpolator; import android.support.v4.view.animation.FastOutSlowInInterpolator; import android.support.v4.view.animation.LinearOutSlowInInterpolator; import android.view.animation.Animation; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import android.view.animation.LinearInterpolator; class AnimationUtils { static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator(); static final Interpolator FAST_OUT_SLOW_IN_INTERPOLATOR = new FastOutSlowInInterpolator(); static final Interpolator FAST_OUT_LINEAR_IN_INTERPOLATOR = new FastOutLinearInInterpolator(); static final Interpolator LINEAR_OUT_SLOW_IN_INTERPOLATOR = new LinearOutSlowInInterpolator(); static final Interpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator(); /** * Linear interpolation between { @code startValue} and { @code endValue} by { @code fraction}. */ static float lerp(float startValue, float endValue, float fraction) { return startValue + (fraction * (endValue - startValue)); } static int lerp(int startValue, int endValue, float fraction) { return startValue + Math.round(fraction * (endValue - startValue)); } static class AnimationListenerAdapter implements Animation.AnimationListener { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { } @Override public void onAnimationRepeat(Animation animation) { } } }
Calss MathUtils
class MathUtils { static int constrain(int amount, int low, int high) { return amount < low ? low : (amount > high ? high : amount); } static float constrain(float amount, float low, float high) { return amount < low ? low : (amount > high ? high : amount); } }
Class TabItem
import android.content.Context; import android.graphics.drawable.Drawable; import android.support.design.R; import android.support.v7.widget.TintTypedArray; import android.util.AttributeSet; import android.view.View; /** * TabItem is a special 'view' which allows you to declare tab items for a { @link TabLayout} * within a layout. This view is not actually added to TabLayout, it is just a dummy which allows * setting of a tab items's text, icon and custom layout. See TabLayout for more information on how * to use it. * * @attr ref android.support.design.R.styleable#TabItem_android_icon * @attr ref android.support.design.R.styleable#TabItem_android_text * @attr ref android.support.design.R.styleable#TabItem_android_layout * * @see TabLayout */ public final class TabItem extends View { final CharSequence mText; final Drawable mIcon; final int mCustomLayout; public TabItem(Context context) { this(context, null); } public TabItem(Context context, AttributeSet attrs) { super(context, attrs); final TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs, R.styleable.TabItem); mText = a.getText(R.styleable.TabItem_android_text); mIcon = a.getDrawable(R.styleable.TabItem_android_icon); mCustomLayout = a.getResourceId(R.styleable.TabItem_android_layout, 0); a.recycle(); } }
Class TabLayout
package com.hconline.antapp.library.wiget; /* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import android.annotation.TargetApi; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.TypedArray; import android.database.DataSetObserver; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Build; import android.support.annotation.ColorInt; import android.support.annotation.DrawableRes; import android.support.annotation.IntDef; import android.support.annotation.LayoutRes; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.RestrictTo; import android.support.annotation.StringRes; import android.support.v4.util.Pools; import android.support.v4.view.GravityCompat; import android.support.v4.view.PagerAdapter; import android.support.v4.view.PointerIconCompat; import android.support.v4.view.ViewCompat; import android.support.v4.view.ViewPager; import android.support.v4.widget.TextViewCompat; import android.support.v7.app.ActionBar; import android.support.v7.content.res.AppCompatResources; import android.text.Layout; import android.text.TextUtils; import android.util.AttributeSet; import android.util.TypedValue; import android.view.Gravity; import android.view.LayoutInflater; import android.view.SoundEffectConstants; import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.HorizontalScrollView; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import com.hconline.antapp.R; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Iterator; import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP; import static android.support.v4.view.ViewPager.SCROLL_STATE_DRAGGING; import static android.support.v4.view.ViewPager.SCROLL_STATE_IDLE; import static android.support.v4.view.ViewPager.SCROLL_STATE_SETTLING; /** * TabLayout provides a horizontal layout to display tabs. * <p> * <p>Population of the tabs to display is * done through { @link Tab} instances. You create tabs via { @link #newTab()}. From there you can * change the tab's label or icon via { @link Tab#setText(int)} and { @link Tab#setIcon(int)} * respectively. To display the tab, you need to add it to the layout via one of the * { @link #addTab(Tab)} methods. For example: * <pre> * TabLayout tabLayout = ...; * tabLayout.addTab(tabLayout.newTab().setText("Tab 1")); * tabLayout.addTab(tabLayout.newTab().setText("Tab 2")); * tabLayout.addTab(tabLayout.newTab().setText("Tab 3")); * </pre> * You should set a listener via { @link #setOnTabSelectedListener(OnTabSelectedListener)} to be * notified when any tab's selection state has been changed. * <p> * <p>You can also add items to TabLayout in your layout through the use of { @link TabItem}. * An example usage is like so:</p> * <p> * <pre> * <TabLayout * android:layout_height="wrap_content" * android:layout_width="match_parent"> * * <TabItem * android:text="@string/tab_text"/> * * <TabItem * android:icon="@drawable/ic_android"/> * * </TabLayout> * </pre> * <p> * <h3>ViewPager integration</h3> * <p> * If you're using a { @link android.support.v4.view.ViewPager} together * with this layout, you can call { @link #setupWithViewPager(ViewPager)} to link the two together. * This layout will be automatically populated from the { @link PagerAdapter}'s page titles.</p> * <p> * <p> * This view also supports being used as part of a ViewPager's decor, and can be added * directly to the ViewPager in a layout resource file like so:</p> * <p> * <pre> * <android.support.v4.view.ViewPager * android:layout_width="match_parent" * android:layout_height="match_parent"> * * <TabLayout * android:layout_width="match_parent" * android:layout_height="wrap_content" * android:layout_gravity="top" /> * * </android.support.v4.view.ViewPager> * </pre> * * @attr ref R.styleable#TabLayout_tabPadding * @attr ref R.styleable#TabLayout_tabPaddingStart * @attr ref R.styleable#TabLayout_tabPaddingTop * @attr ref R.styleable#TabLayout_tabPaddingEnd * @attr ref R.styleable#TabLayout_tabPaddingBottom * @attr ref R.styleable#TabLayout_tabContentStart * @attr ref R.styleable#TabLayout_tabBackground * @attr ref R.styleable#TabLayout_tabMinWidth * @attr ref R.styleable#TabLayout_tabMaxWidth * @attr ref R.styleable#TabLayout_tabTextAppearance * @see <a href="http://www.google.com/design/spec/components/tabs.html">Tabs</a> */ @ViewPager.DecorView public class TabLayout extends HorizontalScrollView { private static final int DEFAULT_HEIGHT_WITH_TEXT_ICON = 72; // dps static final int DEFAULT_GAP_TEXT_ICON = 8; // dps private static final int INVALID_WIDTH = -1; private static final int DEFAULT_HEIGHT = 48; // dps private static final int TAB_MIN_WIDTH_MARGIN = 56; //dps static final int FIXED_WRAP_GUTTER_MIN = 16; //dps static final int MOTION_NON_ADJACENT_OFFSET = 24; private static final int ANIMATION_DURATION = 300; private static final Pools.Pool<Tab> sTabPool = new Pools.SynchronizedPool<>(16); /** * Scrollable tabs display a subset of tabs at any given moment, and can contain longer tab * labels and a larger number of tabs. They are best used for browsing contexts in touch * interfaces when users don’t need to directly compare the tab labels. * * @see #setTabMode(int) * @see #getTabMode() */ public static final int MODE_SCROLLABLE = 0; /** * Fixed tabs display all tabs concurrently and are best used with content that benefits from * quick pivots between tabs. The maximum number of tabs is limited by the view’s width. * Fixed tabs have equal width, based on the widest tab label. * * @see #setTabMode(int) * @see #getTabMode() */ public static final int MODE_FIXED = 1; /** * @hide */ @RestrictTo(LIBRARY_GROUP) @IntDef(value = { MODE_SCROLLABLE, MODE_FIXED}) @Retention(RetentionPolicy.SOURCE) public @interface Mode { } /** * Gravity used to fill the { @link TabLayout} as much as possible. This option only takes effect * when used with { @link #MODE_FIXED}. * * @see #setTabGravity(int) * @see #getTabGravity() */ public static final int GRAVITY_FILL = 0; /** * Gravity used to lay out the tabs in the center of the { @link TabLayout}. * * @see #setTabGravity(int) * @see #getTabGravity() */ public static final int GRAVITY_CENTER = 1; /** * @hide */ @RestrictTo(LIBRARY_GROUP) @IntDef(flag = true, value = { GRAVITY_FILL, GRAVITY_CENTER}) @Retention(RetentionPolicy.SOURCE) public @interface TabGravity { } /** * Callback interface invoked when a tab's selection state changes. */ public interface OnTabSelectedListener { /** * Called when a tab enters the selected state. * * @param tab The tab that was selected */ public void onTabSelected(Tab tab); /** * Called when a tab exits the selected state. * * @param tab The tab that was unselected */ public void onTabUnselected(Tab tab); /** * Called when a tab that is already selected is chosen again by the user. Some applications * may use this action to return to the top level of a category. * * @param tab The tab that was reselected. */ public void onTabReselected(Tab tab); } private final ArrayList<Tab> mTabs = new ArrayList<>(); private Tab mSelectedTab; private final SlidingTabStrip mTabStrip; int mTabPaddingStart; int mTabPaddingTop; int mTabPaddingEnd; int mTabPaddingBottom; int mTabTextAppearance; ColorStateList mTabTextColors; float mTabTextSize; float mTabTextMultiLineSize; final int mTabBackgroundResId; int mTabMaxWidth = Integer.MAX_VALUE; private final int mRequestedTabMinWidth; private final int mRequestedTabMaxWidth; private final int mScrollableTabMinWidth; private int mContentInsetStart; int mTabGravity; int mMode; private OnTabSelectedListener mSelectedListener; private final ArrayList<OnTabSelectedListener> mSelectedListeners = new ArrayList<>(); private OnTabSelectedListener mCurrentVpSelectedListener; private ValueAnimatorCompat mScrollAnimator; ViewPager mViewPager; private PagerAdapter mPagerAdapter; private DataSetObserver mPagerAdapterObserver; private TabLayoutOnPageChangeListener mPageChangeListener; private AdapterChangeListener mAdapterChangeListener; private boolean mSetupViewPagerImplicitly; // Pool we use as a simple RecyclerBin private final Pools.Pool<TabView> mTabViewPool = new Pools.SimplePool<>(12); public TabLayout(Context context) { this(context, null); } public TabLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public TabLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); ThemeUtils.checkAppCompatTheme(context); // Disable the Scroll Bar setHorizontalScrollBarEnabled(false); // Add the TabStrip mTabStrip = new SlidingTabStrip(context); super.addView(mTabStrip, 0, new HorizontalScrollView.LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT)); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabLayout, defStyleAttr, R.style.Widget_Design_TabLayout); mTabStrip.setSelectedIndicatorHeight( a.getDimensionPixelSize(R.styleable.TabLayout_tabIndicatorHeight, 0)); mTabStrip.setSelectedIndicatorColor(a.getColor(R.styleable.TabLayout_tabIndicatorColor, 0)); mTabPaddingStart = mTabPaddingTop = mTabPaddingEnd = mTabPaddingBottom = a .getDimensionPixelSize(R.styleable.TabLayout_tabPadding, 0); mTabPaddingStart = a.getDimensionPixelSize(R.styleable.TabLayout_tabPaddingStart, mTabPaddingStart); mTabPaddingTop = a.getDimensionPixelSize(R.styleable.TabLayout_tabPaddingTop, mTabPaddingTop); mTabPaddingEnd = a.getDimensionPixelSize(R.styleable.TabLayout_tabPaddingEnd, mTabPaddingEnd); mTabPaddingBottom = a.getDimensionPixelSize(R.styleable.TabLayout_tabPaddingBottom, mTabPaddingBottom); mTabTextAppearance = a.getResourceId(R.styleable.TabLayout_tabTextAppearance, R.style.TextAppearance_Design_Tab); // Text colors/sizes come from the text appearance first final TypedArray ta = context.obtainStyledAttributes(mTabTextAppearance, R.styleable.TextAppearance); try { mTabTextSize = ta.getDimensionPixelSize( R.styleable.TextAppearance_android_textSize, 0); mTabTextColors = ta.getColorStateList( R.styleable.TextAppearance_android_textColor); } finally { ta.recycle(); } if (a.hasValue(R.styleable.TabLayout_tabTextColor)) { // If we have an explicit text color set, use it instead mTabTextColors = a.getColorStateList(R.styleable.TabLayout_tabTextColor); } if (a.hasValue(R.styleable.TabLayout_tabSelectedTextColor)) { // We have an explicit selected text color set, so we need to make merge it with the // current colors. This is exposed so that developers can use theme attributes to set // this (theme attrs in ColorStateLists are Lollipop+) final int selected = a.getColor(R.styleable.TabLayout_tabSelectedTextColor, 0)