TabLayout自定义选择背景滑块
2016-10-26 13:48
471 查看
TabLayout是Android 的
Material Design包中的一个控件,可以和V4包中的
ViewPager搭配产生一个联动的效果。这里我自定义了一个滑块能够跟随
TabLayout进行滑动选择的
SliderLayout。效果见下图(白色方框):
下面是
MTabLayout的源码(之前的代码有一些bug,当TabLayout本身进行滑动的时候会和Slider错开,所以就自定义了一个
MTabLayout,并把
SliderLayout作为内部类放到
MTabLayout):
import android.content.Context; import android.graphics.drawable.Drawable; import android.support.annotation.DrawableRes; import android.support.design.widget.TabLayout; import android.support.v4.content.ContextCompat; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import java.lang.ref.SoftReference; /** * <p/> * author yyw * date 2016/12/22 * version 1.0 * desc */ public class MTableLayout extends TabLayout { private SliderLayout mLayout; private LinearLayout mTabStrip; public MTableLayout(Context context) { this(context, null, 0); } public MTableLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public MTableLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mTabStrip = (LinearLayout) getChildAt(0); removeView(mTabStrip); FrameLayout frameLayout = new FrameLayout(context); addView(frameLayout, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); mLayout = new SliderLayout(context); frameLayout.addView(mLayout, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); frameLayout.addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); } @Override public void setupWithViewPager(ViewPager viewPager) { super.setupWithViewPager(viewPager); viewPager.clearOnPageChangeListeners(); viewPager.addOnPageChangeListener(new SliderOnPageChangeListener(this, mLayout)); } public LinearLayout getTabStrip() { return mTabStrip; } public void addTab(TabLayout.Tab tab, boolean setSelected) { super.addTab(tab, setSelected); mLayout.setTotalNum(mTabStrip.getChildCount()); } public void addTab(TabLayout.Tab tab, int position, boolean setSelected) { super.addTab(tab, position, setSelected); mLayout.setTotalNum(mTabStrip.getChildCount()); } @Override public void removeAllTabs() { super.removeAllTabs(); mLayout.setTotalNum(mTabStrip.getChildCount()); } @Override public void removeTabAt(int position) { super.removeTabAt(position); mLayout.setTotalNum(mTabStrip.getChildCount()); } @Override public void removeTab(Tab tab) { super.removeTab(tab); mLayout.setTotalNum(mTabStrip.getChildCount()); } /** * 设置滑块的背景图片 * @param resId */ public void setSliderImage(@DrawableRes int resId){ mLayout.setSliderImage(ContextCompat.getDrawable(getContext(),resId)); } /** * Created by yyw on 2016/4/28. * 一个用来显示当前的index的滑块 */ public class SliderLayout extends LinearLayout { private int totalNum = 0; private ImageView mSlider; private Drawable mSliderImage; private TabLayout tabLayout; public SliderLayout(Context context) { super(context); init(context); } public Drawable getSliderImage() { return mSliderImage; } public void setSliderImage(Drawable sliderImage) { mSliderImage = sliderImage; mSlider.setImageDrawable(mSliderImage); } private void init(Context context) { mSliderImage = ContextCompat.getDrawable(context,R.drawable.slider); mSlider = new ImageView(context); mSlider.setImageDrawable(mSliderImage); addView(mSlider, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); resetSlider(); } /** * 重新设置滑块 */ private void resetSlider() { LinearLayout mTabStrip = getTabStrip(); if (mTabStrip == null) return; totalNum = mTabStrip.getChildCount(); if (totalNum > 0) { View firstView = mTabStrip.getChildAt(0); int width = firstView.getMeasuredWidth(); resetSlider(width); } } private LinearLayout getTabStrip() { if (tabLayout == null) return null; if (tabLayout instanceof MTableLayout) { return ((MTableLayout) tabLayout).getTabStrip(); } else { return (LinearLayout) tabLayout.getChildAt(0); } } private void resetSlider(int width) { LayoutParams params = (LayoutParams) mSlider.getLayoutParams(); params.width = width;//重新设置滑块的大小 params.height = getHeight() / 2; params.gravity = Gravity.CENTER_VERTICAL; mSlider.setPadding(width / 10, 0, width / 10, 0); mSlider.setLayoutParams(params); } public void setupWithTabLayout(TabLayout tabLayout) { this.tabLayout = tabLayout; resetSlider(); } /** * 把滑块滑动到指定的位置 * * @param position 当前位置 * @param positionOffset 滑动到下一个或上一个位置比例 */ private void setScrollPosition(int position, float positionOffset) { final int roundedPosition = Math.round(position + positionOffset); if (roundedPosition < 0 || roundedPosition >= totalNum) { return; } float scrollX = calculateScrollXForTab(position, positionOffset); scrollTo((int) scrollX, 0); } /** * 计算滑块需要滑动的距离 * * @param position 当前选择的位置 * @param positionOffset 滑动位置的百分百 * @return 滑动的距离 */ private int calculateScrollXForTab(int position, float positionOffset) { // TabLayout tabLayout = mTabLayoutRef.get(); if (tabLayout == null) return 0; LinearLayout mTabStrip = getTabStrip(); if (mTabStrip == null) return 0; //当前选择的View final View selectedChild = mTabStrip.getChildAt(position); //下一个View final View nextChild = position + 1 < mTabStrip.getChildCount() ? mTabStrip.getChildAt(position + 1) : null; //当前选择的View的宽度 final int selectedWidth = selectedChild != null ? selectedChild.getWidth() : 0; //下一个View的宽度 final int nextWidth = nextChild != null ? nextChild.getWidth() : 0; //当前选择的View的左边位置,view的方位 final int left = selectedChild != null ? selectedChild.getLeft() : 0; //计算滑块需要滑动的距离,左 + ,右 - ; int scrollX = -(left + ((int) ((selectedWidth + nextWidth) * positionOffset * 0.5f))); //现在是把SliderLayout直接放在TableLayout中的所以就不许要考虑TableLayout本身的滑动 /*if (tabLayout.getTabMode() == TabLayout.MODE_SCROLLABLE) {//当为滑动模式的时候TabLayout会有水平方向的滑动 scrollX += tabLayout.getScrollX();//计算在TabLayout有滑动的时候,滑块相对的滑动距离 }*/ return scrollX; } public void setTotalNum(int totalNum) { this.totalNum = totalNum; resetSlider(); } } public static class SliderOnPageChangeListener extends TabLayout.TabLayoutOnPageChangeListener { private final SoftReference<SliderLayout> mSliderLayoutRef; public SliderOnPageChangeListener(TabLayout tabLayout, SliderLayout layout) { super(tabLayout); mSliderLayoutRef = new SoftReference<SliderLayout>(layout); layout.setupWithTabLayout(tabLayout); } @Override public void onPageScrollStateChanged(int state) { super.onPageScrollStateChanged(state); } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { super.onPageScrolled(position, positionOffset, positionOffsetPixels); final SliderLayout layout = mSliderLayoutRef.get(); if (layout != null) { layout.setScrollPosition(position, positionOffset); } } } }
其中比较关键的一个类是
SliderOnPageChangeListener这个类继承的
TabLayout.TabLayoutOnPageChangeListener类这个类我们看源码(下面)这个是监听
ViewPager滑动选择的一个接口。我们要做的就是在这个类基础上进行扩展让
SliderLayout也能监听到
ViewPager的滑动。
public static class TabLayoutOnPageChangeListener implements ViewPager.OnPageChangeListener { private final WeakReference<TabLayout> mTabLayoutRef; private int mPreviousScrollState; private int mScrollState; public TabLayoutOnPageChangeListener(TabLayout tabLayout) { mTabLayoutRef = new WeakReference<>(tabLayout); } @Override public void onPageScrollStateChanged(int state) { mPreviousScrollState = mScrollState; mScrollState = state; } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { //省略 } } @Override public void onPageSelected(int position) { //省略 } private void reset() { mPreviousScrollState = mScrollState = SCROLL_STATE_IDLE; } }
计算每次SliderLayout需要滑动的距离的方法是
calculateScrollXForTab(int position, float positionOffset)(详细看源码)根据监听到的ViewPager滑动进行相关的计算并滑动
SliderLayout
应用的时候一定要注意
viewPager.setOnPageChangeListener(new SliderLayout.SliderOnPageChangeListener(mTabLayout,layout));要在
mTabLayout.setupWithViewPager(viewPager);之后调用:
public class MainActivity extends AppCompatActivity { public static final String TAG = MainActivity.class.getName(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ViewPager viewPager = (ViewPager) findViewById(R.id.vp); viewPager.setAdapter(new MViewPagerAdapter(getSupportFragmentManager())); mTabLayout.setupWithViewPager(viewPager); } class MViewPagerAdapter extends FragmentPagerAdapter { public final String[] names = new String[]{"音乐","电影","电视","综艺","直播","音乐","电影","电视","综艺","直播"}; public MViewPagerAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { return BlankFragment.newInstance("param1", "param2"); } @Override public int getCount() { return 10; } @Override public CharSequence getPageTitle(int position) { return names[position]; } } }
布局:
<?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"> <com.example.yyw.waterripple.MTableLayout android:id="@+id/tab_layout" android:layout_width="match_parent" android:layout_height="50dp" android:background="@color/cardview_dark_background" app:tabGravity="center" app:tabIndicatorColor="#00000000" app:tabMode="scrollable" app:tabSelectedTextColor="#ff0000" app:tabTextColor="#ffffff"/> <android.support.v4.view.ViewPager android:id="@+id/vp" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
<declare-styleable name="SliderLayout"> <attr name="slider_pic" format="reference" /> </declare-styleable>
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <corners android:topLeftRadius="10dp" android:topRightRadius="10dp" android:bottomLeftRadius="10dp" android:bottomRightRadius="10dp" /> <solid android:color="#ffffff" /> </shape>
相关文章推荐
- Android 中TabLayout自定义选择背景滑块的实例代码
- TabLayout和ViewPager搭配使用中如何设置Tab背景颜色(无须自定义TabLayout)
- Android---Tablayout自定义Tab的背景和字体的颜色变化
- Android---Tablayout自定义Tab的背景和字体的颜色变化
- Android中自定义SeekBar的背景颜色,进度条颜色,以及滑块的图片
- Android布局之LinearLayout自定义高亮背景的方法
- tableLayout 属性 ; 背景选择属性
- 自定义表格控件(通过TabLayout+TabRow)获取表格所有数据,并对表格进行相关事件监听
- WPF后台自定义文字带背景的选择状态按钮
- TabLayout的自定义实现
- 自定义TabLayout的Indicator
- Android中自定义SeekBar的背景颜色,进度条颜色,以及滑块的图片
- Android 设置TabLayout背景和字体大小
- YY项目之TabLayout自定义Tab的title
- 自定义SeekBar使用双向滑块选择
- github上面的demo(包含了状态栏沉浸式,进度动画大全,自定义封装好的webView,recycleview的+swiprefreshLayout,tablayout,okhttp)
- Android中自定义SeekBar的背景颜色,进度条颜色,以及滑块的图片
- iOS自定义Tab选择
- Android中自定义SeekBar的背景颜色,进度条颜色,以及滑块的图片
- 两种实现选项卡功能----自定义和TabLayout