描述

Tabbar:就是每个app下方的那个东西。

image-20230603105531423

实现

直接用三方Tab框架来弄Tabbar效果

添加依赖

1
2
3
//类似TabLayout的控件
//https://github.com/H07000223/FlycoTabLayout
implementation 'io.github.h07000223:flycoTabLayout:3.0.0'

xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">

<androidx.viewpager.widget.ViewPager
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />

<include layout="@layout/divider_small" />

<!--region 底部tab-->
<!--底部tab
tl_indicator_color:高亮颜色
tl_textSelectColor:设置字体选中颜色
tl_textUnselectColor:设置字体未选中颜色-->
<com.flyco.tablayout.CommonTabLayout
android:id="@+id/indicator"
android:layout_width="match_parent"
android:layout_height="@dimen/d55"
android:background="?attr/colorTabBar"
app:tl_iconHeight="@dimen/d20"
app:tl_iconWidth="@dimen/d20"
app:tl_indicator_color="?attr/colorPrimary"
app:tl_indicator_height="0dp"
app:tl_textSelectColor="?attr/colorPrimary"
app:tl_textUnselectColor="?attr/colorOnSurface"
app:tl_textsize="@dimen/s12"
app:tl_underline_color="#DDDDDD"
app:tl_underline_height="0dp" />
<!--endregion-->
</LinearLayout>

分割线:

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8"?>
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/divider_small"
android:layout_width="match_parent"
android:layout_height="@dimen/d0_5"
android:background="?attr/colorDivider">

</View>

?attr/colorTabBar : 就是新建一个attr专门做属性值。

就像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--浅白色,功能是根据主题自动变化的白色,白天:纯白,夜间:浅一点的白色-->
<attr name="colorLightWhite" format="color" />

<!--分割线颜色-->
<attr name="colorDivider" format="color" />

<!--定义一个颜色属性,用来设置TabBar背景-->
<attr name="colorTabBar" format="color" />

<!--Toolbar背景颜色-->
<attr name="colorToolbar" format="color" />

<!--侧滑背景属性-->
<attr name="colorSlideBackground" format="color" />

<!--colorSurface点击颜色-->
<attr name="colorSurfaceClick" format="color" />
</resources>

但是这里没有值,值在对应的theme文件中。

这样处理的好处是:适配深色模式。

theme文件就不贴了,自己去项目里看

初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class MainActivity extends BaseViewModelActivity<ActivityMainBinding> {
//下面这里是控制 toolbar 的数量和现实文字
private static final int[] indicatorTitles = new int[]{R.string.discovery, R.string.video,R.string.feed,R.string.me};
private static final int[] indicatorIcons = new int[]{R.drawable.discovery, R.drawable.video,R.drawable.feed,R.drawable.me};
private static final int[] indicatorSelectedIcons = new int[]{R.drawable.discovery_selected, R.drawable.video_selected,R.drawable.feed_selected,R.drawable.me_selected};

@Override
protected void initViews() {
super.initViews();
SuperStatusBarUtil.myStatusBar(getHostActivity());

//缓存页面数量
// 默认是缓存一个
binding.list.setOffscreenPageLimit(4);

//指示器
ArrayList<CustomTabEntity> indicatorTabs = new ArrayList<>();
for (int i = 0; i < indicatorTitles.length; i++) {
indicatorTabs.add(
new TabEntity(
getString(indicatorTitles[i]),
indicatorSelectedIcons[i],
indicatorIcons[i]
)
);
}
binding.indicator.setTabData(indicatorTabs);

//动态tab显示消息提醒
binding.indicator.showMsg(3,100);
}
@Override
protected void initDatum() {
super.initDatum();
......

adapter = new MainAdapter(getHostActivity(), getSupportFragmentManager());
binding.list.setAdapter(adapter);

adapter.setDatum(Arrays.asList(0, 1, 2, 3));


}
}

这里的TabEntity要自己建立一下,其实就是TabBar哪里的演示布局是怎样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import com.flyco.tablayout.listener.CustomTabEntity;

/**
* 自定义指示器的模型
*/
public class TabEntity implements CustomTabEntity {
/**
* 标题
*/
public String title;

/**
* 选中的图标
*/
public int selectedIcon;
/**
* 未选中图标
*/
public int unSelectedIcon;

public TabEntity(String title, int selectedIcon, int unSelectedIcon) {
this.title = title;
this.selectedIcon = selectedIcon;
this.unSelectedIcon = unSelectedIcon;
}

@Override
public String getTabTitle() {
return title;
}


@Override
public int getTabSelectedIcon() {
return selectedIcon;
}

@Override
public int getTabUnselectedIcon() {
return unSelectedIcon;
}
}

这个时候就是下面的那个TabBar就出来了。但是我们的TabBar是为了和ViewModel配合使用的。

Fragments

DiscoveryFragment,VideoFragment,MeFragment,FeedFragment

自己创建出来,然后重写newInstance方法。

继承BaseViewModelFragment其实就两行代码。

ViewPage适配器

封装的BaseFragmentStatePagereAdapter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import android.content.Context;

import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter;

import java.util.ArrayList;
import java.util.List;

/**
* 通用FragmentPagerAdapter
*/
public abstract class BaseFragmentStatePagerAdapter<T> extends FragmentStatePagerAdapter {
protected final Context context;
protected List<T> datum = new ArrayList<>();

/**
* @param context 上下文
* @param fm Fragment的管理器
*/
public BaseFragmentStatePagerAdapter(Context context, @NonNull FragmentManager fm) {
super(fm);
this.context = context;
}

/**
* 有多少个
*
* @return
*/
@Override
public int getCount() {
return datum.size();
}

/**
* 返回当前位置数据
*
* @param position
* @return
*/
public T getData(int position) {
return datum.get(position);
}

/**
* 设置数据
*
* @param datum
*/
public void setDatum(List<T> datum) {
if (datum != null && datum.size() > 0) {
this.datum.clear();
this.datum.addAll(datum);

//通知数据变更了
notifyDataSetChanged();
}
}
}

MainAdapter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import android.content.Context;

import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;

import com.example.testandroid.adapter.BaseFragmentStatePagerAdapter;
import com.example.testandroid.component.main.fragment.DiscoveryFragment;
import com.example.testandroid.component.main.fragment.FeedFragment;
import com.example.testandroid.component.main.fragment.MeFragment;
import com.example.testandroid.component.main.fragment.VideoFragment;

import io.reactivex.rxjava3.annotations.NonNull;

/**
* 主界面ViewPager的Adapter
*/
public class MainAdapter extends BaseFragmentStatePagerAdapter<Integer> {
/***
* @param context 上下文
* @param fm Fragment管理器
*/
public MainAdapter(Context context, @NonNull FragmentManager fm) {
super(context, fm);
}

@NonNull
@Override
public Fragment getItem(int position) {
switch (position) {
case 1:
return VideoFragment.newInstance();
case 2:
return FeedFragment.newInstance();
case 3:
return MeFragment.newInstance();
default:
return DiscoveryFragment.newInstance();
}
}
}

这里的getItem就是返回对应的ViewPage里面的Fragment。记住别把顺序搞乱了。

indicator和ViewPage配合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Override
protected void initListeners() {
super.initListeners();
binding.indicator.setOnTabSelectListener(new OnTabSelectListener() {
@Override
public void onTabSelect(int position) {
binding.list.setCurrentItem(position);
}

@Override
public void onTabReselect(int position) {

}
});

binding.list.addOnPageChangeListener(new onPageChangeListenerAdapter(){
@Override
public void onPageSelected(int position) {
super.onPageSelected(position);
binding.indicator.setCurrentTab(position);
}
});
}

这个地方的 onPageChangeListenerAdapter 我们包了一层:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import androidx.viewpager.widget.ViewPager;

public class onPageChangeListenerAdapter implements ViewPager.OnPageChangeListener {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

}

@Override
public void onPageSelected(int position) {

}

@Override
public void onPageScrollStateChanged(int state) {

}
}

不然我们是直接继承 Viewpager.OnPageChangeListener

这样包一层的好处是:就不用四个方法哪怕不用也要写出来。

MainActivity 就更加的简洁。

其实那个指示器也可以包:OnTabSelectListener

但是ViewPage这个适用面大我们包。指示器不多就不包了。

总结