引入
一个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>
|