Android—TabLayout很好的操作教程,指示器下划线设置图片与传统流行的XtabLayout对比

释放双眼,带上耳机,听听看~!

话不多说,先看图

这个采用的是

这个采用的是渐变的图片,TabLayout采用的是自定义的。

————————————————————————————————————————————————————————-

这个采用的是纯色color,TabLayout采用的是 implementation ‘com.androidkun:XTabLayout:1.1.3’

————————————————————————————————————————————————————————-

对比。二者都可以达到切换字体方法,点击或者滑动切换,指示器均可以含有动画的移动过渡。

下面贴一下用法。

方法一:带自定义指示器图片的,为drawable资源下的图片

①采用SlidingTabLayout自定义控件

/**
* ================================================
*
* @author :Vip
* @version :V 1.0.0
* @date :2019/6/21 17:38
* 描    述:支持自定义下标,自定义tab宽度
* 修订历史:
* ================================================
*/
public class SlidingTabLayout extends TabLayout {
/**
* 每个tab的宽度
*/
private int tabWidth;
/**
* 屏幕宽度
*/
private int mScreenWidth;
/**
* 自定义指示器
*/
private Bitmap mSlideIcon;
/**
* 滑动过程中指示器的水平偏移量
*/
private int mTranslationX;
/**
* 指示器初始X偏移量
*/
private int mInitTranslationX;
/**
* 指示器初始Y偏移量
*/
private int mInitTranslationY;
/**
* 默认的页面可见的tab数量
*/
private static final int COUNT_DEFAULT_VISIBLE_TAB = 4;
/**
* 页面可见的tab数量,默认4个
*/
private int mTabVisibleCount = COUNT_DEFAULT_VISIBLE_TAB;
public SlidingTabLayout(Context context) {
super(context);
}
public SlidingTabLayout(Context context, AttributeSet attrs) {
super(context, attrs);
this.mSlideIcon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_indicator_yellow);
this.mScreenWidth = getResources().getDisplayMetrics().widthPixels;
@SuppressLint({"Recycle", "CustomViewStyleable"}) TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TabLayoutLine);
//画笔的颜色
mTabVisibleCount = ta.getInteger(R.styleable.TabLayoutLine_selectedNum, 2);
//异步修改Tab宽度
post(new Runnable() {
@Override
public void run() {
resetTabParams();
}
});
}
/**
* 重绘下标
*/
public void redrawIndicator(int position, float positionOffset) {
mTranslationX = (int) ((position + positionOffset) * tabWidth);
invalidate();
}
/**
* 动态设图
**/
public void setmSlideIcon(Bitmap mSlideIcon) {
this.mSlideIcon = mSlideIcon;
}
/**
* tab的父容器,注意空指针
*/
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Nullable
public LinearLayout getTabStrip() {
Class<?> tabLayout = TabLayout.class;
Field tabStrip = null;
try {
tabStrip = tabLayout.getDeclaredField("mTabStrip");
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
assert tabStrip != null;
tabStrip.setAccessible(true);
LinearLayout llTab = null;
try {
llTab = (LinearLayout) tabStrip.get(this);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
assert llTab != null;
llTab.setClipChildren(false);
return llTab;
}
/**
* 绘制指示器
*/
@Override
protected void dispatchDraw(Canvas canvas) {
if (mSlideIcon == null) {
return;
}
canvas.save();
// 平移到正确的位置,修正tabs的平移量
canvas.translate(mInitTranslationX + mTranslationX, this.mInitTranslationY);
canvas.drawBitmap(this.mSlideIcon, 0, 0, null);
canvas.restore();
super.dispatchDraw(canvas);
}
/**
* 重设tab宽度
*/
private void resetTabParams() {
LinearLayout tabStrip = getTabStrip();
if (tabStrip == null) {
return;
}
for (int i = 0; i < tabStrip.getChildCount(); i++) {
LinearLayout tabView = (LinearLayout) tabStrip.getChildAt(i);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams((mScreenWidth / (mTabVisibleCount)), LinearLayout.LayoutParams
.WRAP_CONTENT);
tabView.setLayoutParams(params);
//tab中的图标可以超出父容器
tabView.setClipChildren(false);
tabView.setClipToPadding(false);
tabView.setPadding(0, 30, 0, 30);
}
initTranslationParams(tabStrip, mScreenWidth);
}
/**
* 初始化三角下标的坐标参数
*/
private void initTranslationParams(LinearLayout llTab, int screenWidth) {
if (mSlideIcon == null) {
return;
}
tabWidth = (screenWidth / (mTabVisibleCount));
View firstView = llTab.getChildAt(0);
if (firstView != null) {
this.mInitTranslationX = (firstView.getLeft() + tabWidth / 2 - this.mSlideIcon.getWidth() / 2);
this.mInitTranslationY = (getBottom() - getTop() - this.mSlideIcon.getHeight());
}
}
}

补充:

在attrs.xml文件下,设置这个自定义控件的一个属性。设置默认数量,方便在你布局引用的地方,提前设置你想显示几个。

②布局引用:

<!--有数据-->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/include_top_base"
tools:ignore="UselessParent">
<!--标签-->
<com.yc.stscf.widget.SlidingTabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
app:selectedNum="2"
app:tabGravity="fill"
app:tabSelectedTextColor="@color/cs_F88441"
app:tabTextColor="@color/cs_999999"
app:tabIndicatorHeight="0dp"
app:tabMaxWidth="0dp"
app:tabMode="fixed" />
<View
android:id="@+id/view_line"
style="@style/App_RootView_H"
android:layout_below="@+id/tab_layout" />
<com.yc.stscf.widget.TabViewPager
android:id="@+id/vp_all"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/view_line"
android:layout_marginTop="@dimen/ds_10dp" />
</RelativeLayout>

③在java代码中写:

/**
* ================================================
*
* @author :Vip
* @version :V 1.0.0
* @date :2019/6/12 9:31
* 描    述:全部审批
* 修订历史:
* ================================================
*/
public class MyTabApprovalFragment extends BaseFragment {
@BindView(R.id.iv_back)
ImageView ivBack;
@BindView(R.id.tv_tittle)
TextView tvTittle;
@BindView(R.id.tv_base_some)
TextView tvBaseSome;
@BindView(R.id.iv_white_search)
ImageButton ivWhiteSearch;
@BindView(R.id.tv_add)
TextView tvAdd;
@BindView(R.id.tv_commit)
TextView tvCommit;
@BindView(R.id.vp_all)
TabViewPager vpAll;
@BindView(R.id.tab_layout)
SlidingTabLayout tabLayout;
@BindView(R.id.iv_triangle_white)
ImageView ivTriangleWhite;
@BindView(R.id.rl_title_all)
RelativeLayout rlTitleAll;
Unbinder unbinder;
/**
* 总布局
**/
private View view = null;
/**
* 标志位,标志已经初始化完成
**/
private boolean isPrepared;
private List<String> strings = new ArrayList<String>();
private List<Fragment> fragments = new ArrayList<>();
private String[] tabTitles = {"待审批", "已审批"};
@SuppressLint("InflateParams")
@Override
protected View initLayout(LayoutInflater inflater, ViewGroup container, boolean b) {
view = inflater.inflate(R.layout.fragment_tab_my_approval, null);
isPrepared = true;
return view;
}
@Override
protected void initView(Bundle savedInstanceState) {
initFragments();
vpAll.setAdapter(new TabFragmentAdapter(fragments, strings, getSupportFragmentManager(), getActivity()));
tabLayout.setupWithViewPager(vpAll);
vpAll.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
// 将滑动偏移量传给Indicator
tabLayout.redrawIndicator(position, positionOffset);
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int i) {
}
});
}
private void initFragments() {
for (String tabTitle : tabTitles) {
ShowApprovalFragment fragment = new ShowApprovalFragment();
fragments.add(fragment);
strings.add(tabTitle);
}
}
@Override
protected void lazyLoad() {
if (!isPrepared || !isVisible) {
return;
}
}
}

④用的的viewPager

/**
* ================================================
*
* @author:vip 版    本:V 1.0.0
* 创建日期:2019/06/12
* 描    述:Tab控件采用的适配器(防止卡顿)
* 修订历史:
* ================================================
*/
public class TabFragmentAdapter extends FragmentPagerAdapter {
private Context context;
private List<Fragment> fragments;
private List<String> strings;
public TabFragmentAdapter(List<Fragment> fragments, List<String> strings, FragmentManager fragmentManager, Context context) {
super(fragmentManager);
this.strings = strings;
this.context = context;
this.fragments = fragments;
}
@Override
public Fragment getItem(int position) {
return fragments.get(position);
}
@Override
public int getCount() {
return strings.size();
}
@Override
public CharSequence getPageTitle(int position) {
return strings.get(position);
}
/**
* 重写该方法,取消调用父类该方法 * 可以避免在viewpager切换
* fragment不可见时执行到onDestroyView,可见时又从onCreateView重新加载视图
* * 因为父类的destroyItem方法中会调用detach方法,将fragment与view分离,
* (detach()->onPause()->onStop()->onDestroyView())
* 然后在instantiateItem方法中又调用attach方法,此方法里判断如果fragment与view分离了,
** 那就重新执行onCreateView,再次将view与fragment绑定(attach()->onCreateView()->onActivityCreated()->onStart()->onResume()) *
*/
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
// super.destroyItem(container, position, object); }
}
}

————————————————————————————————————————————————————————

方法二:原始常用的XTabLayout

①引入依赖

implementation 'com.androidkun:XTabLayout:1.1.3'

②引入布局

<!--有数据-->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/include_top_base"
tools:ignore="UselessParent">
<!--标签-->
<com.androidkun.xtablayout.XTabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="@dimen/ds_42dp"
android:background="@color/white"
app:tabBackground="@null"
app:xTabDividerWidthWidthText="false"
app:xTabIndicatorColor="@color/load_red"
app:xTabSelectedTextColor="@color/load_red"
app:xTabSelectedTextSize="14sp"
app:xTabTextBold="false"
app:xTabTextColor="@color/cs_727272"
app:xTabTextSelectedBold="true"
app:xTabTextSize="@dimen/ds_12sp"
tools:ignore="RtlHardcoded" />
<View
android:id="@+id/view_line"
style="@style/App_RootView_H"
android:layout_below="@+id/tab_layout" />
<com.yc.stscf.widget.TabViewPager
android:id="@+id/vp_all"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/view_line"
android:layout_marginTop="@dimen/ds_10dp" />
</RelativeLayout>

③ 代码部分

/**
* ================================================
*
* @author :Vip
* @version :V 1.0.0
* @date :2019/6/12 9:31
* 描    述:我的申请
* 修订历史:
* ================================================
*/
public class MyTabApplyFragment extends BaseFragment {
@BindView(R.id.iv_back)
ImageView ivBack;
@BindView(R.id.tv_tittle)
TextView tvTittle;
@BindView(R.id.iv_triangle_white)
ImageView ivTriangleWhite;
@BindView(R.id.rl_title_all)
RelativeLayout rlTitleAll;
@BindView(R.id.tv_base_some)
TextView tvBaseSome;
@BindView(R.id.iv_white_search)
ImageButton ivWhiteSearch;
@BindView(R.id.tv_add)
TextView tvAdd;
@BindView(R.id.tv_commit)
TextView tvCommit;
@BindView(R.id.tab_layout)
XTabLayout tabLayout;
@BindView(R.id.view_line)
View viewLine;
@BindView(R.id.vp_all)
TabViewPager vpAll;
/**
* 总布局
**/
private View view = null;
/**
* 标志位,标志已经初始化完成
**/
private boolean isPrepared;
@SuppressLint("InflateParams")
@Override
protected View initLayout(LayoutInflater inflater, ViewGroup container, boolean b) {
view = inflater.inflate(R.layout.fragment_tab_my_apply, null);
isPrepared = true;
return view;
}
@Override
protected void initView(Bundle savedInstanceState) {
ivBack.setVisibility(View.GONE);
tvTittle.setText("全部申请");
//系统默认
tabLayout.setTabMode(TabLayout.MODE_FIXED);
//添加完所有tab后调用!!
final List<String> tabList = new ArrayList<>();
tabList.clear();
tabList.add("未完成");
tabList.add("已完成");
tabLayout.removeAllTabs();
for (int i = 0; i < tabList.size(); i++) {
tabLayout.addTab(tabLayout.newTab().setText(tabList.get(i)));
}
List<Fragment> fragments = new ArrayList<Fragment>();
for (int i = 0; i < tabList.size(); i++) {
Fragment fragment = new ShowApprovalFragment();
Bundle bundle = new Bundle();
bundle.putString("text", tabList.get(i));
fragment.setArguments(bundle);
fragments.add(fragment);
}
vpAll.setAdapter(new TabFragmentAdapter(fragments, tabList, getSupportFragmentManager(), getActivity()));
tabLayout.setupWithViewPager(vpAll);
tabLayout.setTabTextColors(getResources().getColor(R.color.cs_999999), getResources().getColor(R.color.cs_F88441));
//点击
tabLayout.setOnTabSelectedListener(new XTabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(XTabLayout.Tab tab) {
Tt.showShort(mContext, tab.getPosition() + "点击的页");
vpAll.setCurrentItem(tab.getPosition());
}
@Override
public void onTabUnselected(XTabLayout.Tab tab) {
}
@Override
public void onTabReselected(XTabLayout.Tab tab) {
}
});
vpAll.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int i, float v, int i1) {
}
@Override
public void onPageSelected(int position) {
//                Tt.showShort(mContext, position + "滑动的页");
}
@Override
public void onPageScrollStateChanged(int i) {
}
});
}
@Override
protected void lazyLoad() {
if (!isPrepared || !isVisible) {
return;
}
}
} 

总结:

没什么好总结的了,这俩都挺好的,方法一能有效的解决以图片为指示器的效果,美工提供好图片,比如三角形,或者是圆点,再或者是如图我展示的渐变图片,不需要使用点9图片,也可以通过算法进行自行拉伸。我觉得是很好用。

人已赞赏
Android文章

Android滑动菜单特效实现,仿人人客户端侧滑效果,史上最简单的侧滑实现

2020-3-19 21:44:29

Android文章

Android双向滑动菜单完全解析,教你如何一分钟实现双向滑动特效

2020-3-19 21:52:21

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
有新消息 消息中心
搜索