炫酷导航栏实现
创建导航栏图标
使用系统自带的矢量图库文件,鼠标右键点击res->New->Vector Asset
即可跳转到如下界面,按照下图操作:
用使用矢量绘图icon图标,并且在Name
属性栏选择图标名称;
- 根据自己需求选择合适的icon,
Select icon
中图标数量并不是很多,自行选择即可,我这里选择四个icon图标; baseline_person_fill / baseline_person_stroke
,一般后缀名fill和stroke来区分点击前和点击后icon图标;
修改依赖
在build.grade(:app)
中的dependencies
修改以下依赖即可:
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta7'
创建资源文件
创建资源文件,鼠标右键点击res文件夹,依次点击New->New Resource File
即可:
选择Resource types
为Layout
即可,File name
暂定为all_icon_layout
(后期根据情况修改名称)
第一步:将all_icon_layout.xml
文件转换为MotionLayout
:
第二步:在母体中添加ImageView和TextView组件(选中组件后拖拽即可),位置目前任意摆放,暂无要求。将ImageView组件拖动到母版后出现图二所示界面,选择合适的Image(即之前创建的icon的xml文件)
同理,在添加TextView组件之前,需要先添加资源文件,在string.xml
资源文件中添加如下代码:
<resources>
<string name="scanner_icon_title">扫码乘车</string>
<string name="record_icon_title">预约记录</string>
<string name="start_icon_title">校车预约</string>
<string name="account_icon_title">我的</string>
</resources>
利用string.xml
文件,一次设置四个TextView的text属性:
接下来,将ImageView和TextView串联起来,让它们保持整齐。
例如:选中ImageView组件,然后白板中组件的四个白点分别连接到手机四个边即可,TextView组件同理,最后再将两个组件上下连接即可。
我们需要将两种图片进行过度,需要将图片转换为ImageFilterView
选中ImageView组件后,鼠标右键即可选择Convert view...
,并且选择ImageFilterView
即可
分别进行如下操作:
- 点击1号
start
模板 - 点击2号位置的组件
ImageView
- 3号位置有一个✏️图标,点击✏️图标后选择后点击
Create Constraint
,分别对ImageView和TextView组件进行同样操作,直至Source属性列变换为start即可 - 完成上述操作后,点击
end
模版,并且执行2和3同样的操作即可出现下方图中右侧两个效果即成功。
添加动画效果
KeyCycle
:
设置选中状态的图片,在all_icon_layout.xml代码中添加如下代码,目的是为了点击icon图标后,有明显的变化:
过渡画面
ImageView组件的过渡动画
首先设置ImageView组件的过渡动画初始状态选择ImageView组件后,点击CustomAttributes
添加即可:
在start
模版下,Value的值为0;在end
模版下,Value的值为1。
设置颜色过渡,点击icon图标后,矢量图icon图标发生颜色变化:
同理需要设置end模版的的颜色,同样操作,选择end板块后添加颜色即可
到此ImageView组件调整完毕,接下来只需要调整TextView组件的颜色过渡即可,不需要设置动画过渡。
TextView组件的过渡动画
在选中TextView组件后,进行颜色过渡操作即可:
- 设置
start模版
的textColor
属性为?attr/colorControlNormal
; - 设置
end模版
的textColor
属性为#1AFA29
,变换后的颜色根据具体情况修改即可。
上述做的只是其中一个部分,其余三个部分复制粘贴即可,我这里将之前的名字all_icon_layout
修改为scanner_icon_layout
;其他的部分复制粘贴后修改名称,分别是star_icon_layout
、record_icon_layout
、account_icon_layout
。
随后需要修改布局文件中的矢量图icon图标和对应的TextView组件文字;至此,所有的布局文件暂时告一段乱了。接下来设置main布局文件。
设置main布局文件
- 设置导航栏,直接在Layouts部分,设置
LinearLayout(horizontal)
; - 将创建的LinearLayout布局的左右下三个点分别连接到白板的左右下对应的位置,此时这三个点的颜色由白色填充为蓝色;
- 将LinearLayout布局的高度设置为
56dp
.
设置完上面布局文件后,设置gravity
,选择center_vertical
即可:
接下来就开始放置之前创建的四个UI组件,使用Containers/include
即可实现:
- 拖动四个
include
到LinearLayout布局文件中 - 选择之前创建的四个矢量图icon图标.xml文件
为了继续美化点击icon时的效果,在四个矢量图icon图标对应的xml文件中添加水波纹:
android:background="?attr/selectableItemBackgroundBorderless"
创建四个Fragment占位,按照如下图所示操作,选择Fragment(Blank)
,即可创建成功(这里已经创建成功了,不过多演示了):
创建navigation
,res->New->New Resource File
,并且选择Resource type为Navigation
即可,点击ok后,会自动导入依赖,需要提前打开科技工具,否则时间很慢👀:
系统依赖自动导入后,就可以导入其中的Fragment,按照导航栏的顺序导入即可,有home图标的表示是第一个Fragment(按照下面操作即可):
在之前的布局文件中,添加NavHostFragment
即可,然后设置高度,点击下拉菜单,选择0dp(match constraint)
即可:
然后将NavHostFragment添加到activity_main.xml
中,直接拖动即可
设置每个Fragment布局文件中的内容,将TextView居中,且设置文本:
最后一件事就是将四个Fragment装在一起。
组装Fragment
打开MainActivity.java文件,参考下面的代码,修改自己的代码即可
public class MainActivity extends AppCompatActivity {
//NavHostFragment的控制器
NavController navController;
//4个MotionLayout
MotionLayout ScannerMotionLayout;
MotionLayout StarMotionLayout;
MotionLayout RecordMotionLayout;
MotionLayout AccountMotionLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
private void init() {
// 初始化步骤,注意 NavHostFragment 控制器获取方法
// 修改这里以确保正确设置NavController
NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.fragmentContainerView);
navController = navHostFragment.getNavController();
ScannerMotionLayout = findViewById(R.id.scannerMotionLayout);
StarMotionLayout = findViewById(R.id.starMotionLayout);
RecordMotionLayout = findViewById(R.id.recordMotionLayout);
AccountMotionLayout = findViewById(R.id.accountMotionLayout);
// 设置点击事件
ScannerMotionLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 选中直接转换到对应页面
navController.navigate(R.id.scannerFragment);
}
});
StarMotionLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
navController.navigate(R.id.starFragment);
}
});
RecordMotionLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
navController.navigate(R.id.recordFragment);
}
});
AccountMotionLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
navController.navigate(R.id.accountFragment);
}
});
navController.addOnDestinationChangedListener(new NavController.OnDestinationChangedListener() {
@Override
public void onDestinationChanged(@NonNull NavController controller,
@NonNull NavDestination destination,
@Nullable Bundle arguments) {
controller.popBackStack();
AccountMotionLayout.setProgress(0f);
RecordMotionLayout.setProgress(0f);
StarMotionLayout.setProgress(0f);
ScannerMotionLayout.setProgress(0f);
switch (destination.getId()) {
case R.id.accountFragment:
AccountMotionLayout.transitionToEnd();
break;
case R.id.recordFragment:
RecordMotionLayout.transitionToEnd();
break;
case R.id.starFragment:
StarMotionLayout.transitionToEnd();
break;
case R.id.scannerFragment:
ScannerMotionLayout.transitionToEnd();
break;
}
}
});
}
}
在我提供的代码第21行,有一个组件叫做fragmentContainerView
,这个组件其实就是activity_layout.xml布局文件中的导航栏的id,大家根据自己的id修改即可
至此,所有的功能均可运行,炫酷的导航栏终于实现了。
参考教程:第57集 底部导航栏动态效果