0%

Navigation

引入

一个APP不可能只有一个页面吧,这时候就需要 Navigation 和 Fragment 来帮助我们切换页面。效果图如下。

Fragment 生命周期如下图所示。

代码

MainActivity 部分

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
package com.example.navigation1;

import androidx.appcompat.app.AppCompatActivity;
import androidx.navigation.NavController;
import androidx.navigation.fragment.NavHostFragment;
import androidx.navigation.ui.NavigationUI;

import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.fragment);
NavController controller = navHostFragment.getNavController();
// 显示导航栏上方返回按钮
NavigationUI.setupActionBarWithNavController(this, controller);
}

// 导航栏上方返回按钮事件
@Override
public boolean onSupportNavigateUp() {
NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.fragment);
NavController controller = navHostFragment.getNavController();
return controller.navigateUp();
}
}

Navigation.xml 中可以写出各个 Fragment 的思维导图(具体跳转代码写在 java 文件中),也可以指定切换动画。

创建 Fragment 自动生成的代码就略去了。

1
2
3
4
5
6
7
8
	@Override
// 跳转页面
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Button button = getView().findViewById(R.id.button2);
button.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.action_secondFragment_to_firstFragment));
}
}

传递数据

(1) 使用Bundle

有时界面会伴随着数据一起跳转,这个时候就可以使用 Bundle 这个键值对存储数据进行传送了。

效果图如下。

发送方 Fragment

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
	@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Button button = getView().findViewById(R.id.button);
EditText editText = getView().findViewById(R.id.editTextTextPersonName);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String text = editText.getText().toString();
if(TextUtils.isEmpty(text)){
Toast.makeText(getActivity(), "请输入姓名", Toast.LENGTH_SHORT).show();
return;
}
Bundle bundle = new Bundle();
bundle.putString("name", text);
NavController navController = Navigation.findNavController(view);
navController.navigate(R.id.action_firstFragment_to_secondFragment, bundle);
}
});
}
}

接收方 Fragment

1
2
3
4
5
6
7
8
9
    @Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
TextView textView = getView().findViewById(R.id.textView);
textView.setText(getArguments().getString("name"));
Button button = getView().findViewById(R.id.button2);
button.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.action_secondFragment_to_firstFragment));
}
}

(2) 使用 ViewModel,效果图如下。

FirstFragment部分。

要注意 onCreateView 是创建的时候调用,onViewCreated 是在 onCreateView 后被触发的事件,前后关系。

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
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
MyViewModel myViewModel = new ViewModelProvider(getActivity()).get(MyViewModel.class);
FragmentFirstBinding binding = DataBindingUtil.inflate(inflater, R.layout.fragment_first, container, false);
binding.setData(myViewModel);
binding.setLifecycleOwner(getActivity());
binding.buttonNext.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
NavController navController = Navigation.findNavController(view);
navController.navigate(R.id.action_firstFragment_to_secondFragment);
}
});
binding.seekBar.setProgress(myViewModel.getNumber().getValue());
binding.seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
myViewModel.getNumber().setValue(binding.seekBar.getProgress());
}

@Override
public void onStartTrackingTouch(SeekBar seekBar) {

}

@Override
public void onStopTrackingTouch(SeekBar seekBar) {

}
});
return binding.getRoot();
}

SecondFragment部分。

两边获取 ViewModelProvider 都是使用的 getActivity() 而不是 this,因此请求的 ViewModel 是一个对象,这就是 Singleton 的含义,故能数据共享。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
FragmentSecondBinding binding = DataBindingUtil.inflate(inflater, R.layout.fragment_second, container, false);
MyViewModel myViewModel = new ViewModelProvider(getActivity()).get(MyViewModel.class);
binding.setData(myViewModel);
binding.setLifecycleOwner(getActivity());
binding.buttonBack.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
NavController navController = Navigation.findNavController(view);
navController.navigate(R.id.action_secondFragment_to_firstFragment);
}
});
return binding.getRoot();
}

动画补间

各个 Fragment 之间的切换动作。效果图如下。

Slide_From_Left 进入动画。

fromXDelta:起始位置。(0% 表示 Fragment 中的位置)

toXDelta:结束位置。

duration:持续时间。

1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="-100%"
android:toXDelta="0%"
android:duration="1000"/>
</set>

Slide_To_Right 移出动画。

1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="0%"
android:toXDelta="100%"
android:duration="1000"/>
</set>

也可以定制其它花里胡哨的动画。

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
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<rotate
android:fromDegrees="0"
android:toDegrees="720"
android:pivotX="50%"
android:pivotY="50%"
android:duration="1000"/>
<scale
android:fromXScale="0%"
android:toXScale="100%"
android:fromYScale="0%"
android:toYScale="100%"
android:pivotX="50%"
android:pivotY="50%"
android:duration="1000"/>
<alpha
android:fromAlpha="0"
android:toAlpha="100"
android:duration="1000"/>
<translate
android:fromXDelta="-100%"
android:toXDelta="0%"
android:duration="1000"/>
</set>