导航
导航结构,是一个栈结构,每进入一级页面,压入栈中一级,返回一级,栈中弹出一级
实操
先安装依赖,在 Android 中使用导航需要先安装这两个依赖
arduino
implementation 'androidx.navigation:navigation-fragment-ktx:2.8.7'
implementation 'androidx.navigation:navigation-ui-ktx:2.8.7'
拖拽 要在 Activity 的 xml 文件中处理导航 Fragment,不能缺少这两个属性
如果不存在可以通过这两种方式创建
然后可以看到这个页面
点击创建 host 和目标页面,注意:至少要搞两个,否则也没用 然后连线
连线之后的效果
xml 文件就会变成这个样子
html
<?xml version="1.0" encoding="utf-8"?>
<navigation 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:id="@+id/mobile_navigation.xml"
app:startDestination="@id/navHostFragment">
<fragment
android:id="@+id/navHostFragment"
android:name="com.example.basepractice.NavHostFragment"
android:label="NavHostFragment" >
<action
android:id="@+id/action_navHostFragment_to_secondFragment"
app:destination="@id/secondFragment" />
</fragment>
<fragment
android:id="@+id/secondFragment"
android:name="com.example.basepractice.SecondFragment"
android:label="fragment_second"
tools:layout="@layout/fragment_second" />
</navigation>
接下来需要实现 Fragment 的 OnFragmentInteractionListener 接口,注意:默认情况下有可能没有这个接口,根据提示在 Fragment 中定义一个即可。
java
package com.example.basepractice;
import android.net.Uri;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
/**
* A simple {@link Fragment} subclass.
* Use the {@link SecondFragment#newInstance} factory method to
* create an instance of this fragment. */public class SecondFragment extends Fragment {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
// TODO: Rename and change types of parameters
private String mParam1;
private String mParam2;
public SecondFragment() {
// Required empty public constructor
}
/**
* Use this factory method to create a new instance of * this fragment using the provided parameters. * * @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment SecondFragment.
*/ // TODO: Rename and change types and number of parameters
public static SecondFragment newInstance(String param1, String param2) {
SecondFragment fragment = new SecondFragment();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_second, container, false);
}
public interface OnFragmentInteractionListener {
void onFragmentInteraction(Uri uri);
}
}
java
package com.example.basepractice;
import android.net.Uri;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import androidx.activity.EdgeToEdge;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.constraintlayout.widget.ConstraintSet;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.ViewModelProvider;
import com.example.basepractice.databinding.ActivityMainBinding;
public class MainActivity extends FragmentActivity implements DemoFragment.OnFragmentInteractionListener, SecondFragment.OnFragmentInteractionListener {
public ActivityMainBinding binding;
private DataViewModel dataViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
dataViewModel = new ViewModelProvider(this).get(DataViewModel.class);
dataViewModel.setData("来自Activity的数据");
binding = ActivityMainBinding.inflate(getLayoutInflater());
View view = binding.getRoot();
setContentView(view);
FragmentManager fragmentManager = getSupportFragmentManager();
DemoFragment demoFragment = (DemoFragment) fragmentManager.findFragmentById(R.id.fragment1);
if (demoFragment == null) {
Log.i("Tag", "onCreate: 空");
} else {
// 传递数据给fragment
demoFragment.receiveDataFromActivity("Activity 的数据");
}
}
void fragmentData2Activity() {
// 第一步:获取fragment实例
DemoFragment demoFragment = new DemoFragment();
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment1, demoFragment)
.commitNow();
// 第三步:调用接口方法
demoFragment.buttonClick();
}
// 第二步:实现接口方法
@Override
public void onDataFromFragment(String str) {
Log.i("TAG", "onDataFromFragment: " + str);
}
// 实现导航目标页面接口方法
@Override
public void onFragmentInteraction(Uri uri) {
}
}
在 host Fragment 中做这个操作
scss
Navigation.findNavController(rootView).navigate(R.id.secondFragment);
java
package com.example.basepractice;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import androidx.navigation.Navigation;
/**
* A simple {@link Fragment} subclass.
* Use the {@link NavHostFragment#newInstance} factory method to
* create an instance of this fragment. */public class NavHostFragment extends Fragment {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
// TODO: Rename and change types of parameters
private String mParam1;
private String mParam2;
private Button navButton;
public NavHostFragment() {
// Required empty public constructor
}
/**
* Use this factory method to create a new instance of * this fragment using the provided parameters. * * @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment NavHostFragment.
*/ // TODO: Rename and change types and number of parameters
public static NavHostFragment newInstance(String param1, String param2) {
NavHostFragment fragment = new NavHostFragment();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// 加载片段布局
View rootView = inflater.inflate(R.layout.fragment_nav_host, container, false);
navButton = rootView.findViewById(R.id.nav_btn);
navButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Navigation.findNavController(rootView).navigate(R.id.secondFragment);
}
});
// Inflate the layout for this fragment
return rootView;
}
}
测试效果
添加导航栏
通过ToolBar
html
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:background="#FFFFFF"
>
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="40dp"
android:textSize="25sp"
android:textColor="@color/white"
android:text="首页"
android:textAlignment="center"
android:layout_gravity="center"
>
</TextView>
</androidx.appcompat.widget.Toolbar>
<!-- TODO: Update blank fragment layout -->
<Button
android:id="@+id/nav_btn"
android:layout_width="0dp"
android:layout_height="50dp"
android:text="跳转下一页"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="180dp"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
TabLayout 和 ViewPager2
选项卡交互方式,就类似于头条这类新闻 App 顶部的标签的交互方式,这些标签可以是固定的也可以是滚动的。 ViewPager2
的主要作用是允许用户翻阅不同的信息页面,其中每个页面通常由布局 Fragment 表示。与 ViewPager2
关联的 Fragment 由 FragmentStateAdapter
类的实例管理。 分配给 ViewPager2
的 adapter 必须实现至少两个方法:
第一个:getItemCount()
必须返回可供用户显示的页面片段总数。
第二个:createFrament()
传递一个页码,并且必须返回准备呈现给用户的相应 Fragment 对象。
附加小知识点:
在 Fragment 中开启 Activity 一般使用这个方法
java
startActivity(new Intent(navContext, Demo2Activity.class));
这其中 context
是必不可少的,在 Activity 中可以定义进行直接调用,在 Fragment 中需要通过 onAttach()
方法获取
java
/**
* 在 Fragment 的生命周期里,onAttach 方法会在 Fragment 与 Activity 关联时被调用,
* 此时可以获取到 Activity 的 Context。
* @param context
*/
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
// navContext 是在 Fragment 中自定义的
navContext = context;
}
TabLayout
和 ViewPager2
组合使用 第一步:准备用于切换页面的多个 Fragment 第二步:在 Activity 布局文件中创建
TabLayout
和 ViewPager2
html
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".baseUI.demo2.Demo2Activity">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="30dp"
>
</com.google.android.material.tabs.TabLayout>
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/view_pager2"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@id/tab_layout"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
>
</androidx.viewpager2.widget.ViewPager2>
</androidx.constraintlayout.widget.ConstraintLayout>
第三步:创建 adapter 并继承 FragmentStateAdapter
并重写相关方法
java
package com.example.basepractice.baseUI.demo2.adapter;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import com.example.basepractice.baseUI.demo2.tab.Tab1Fragment;
import com.example.basepractice.baseUI.demo2.tab.Tab2Fragment;
import com.example.basepractice.baseUI.demo2.tab.Tab3Fragment;
import com.example.basepractice.baseUI.demo2.tab.Tab4Fragment;
public class TabPageAdapter extends FragmentStateAdapter {
int tabCount;
public TabPageAdapter(@NonNull FragmentActivity fragmentActivity, int numberOfTabs) {
super(fragmentActivity);
this.tabCount = numberOfTabs;
}
@NonNull
@Override
public Fragment createFragment(int position) {
switch (position){
case 0:
return new Tab1Fragment();
case 1:
return new Tab2Fragment();
case 2:
return new Tab3Fragment();
case 3:
return new Tab4Fragment();
default:
return null;
}
}
@Override
public int getItemCount() {
return tabCount;
}
}
第四步:回到 Activity 中进行 TabLayout
和 ViewPager2
的配置
java
package com.example.basepractice.baseUI.demo2;
import android.net.Uri;
import android.os.Bundle;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.viewpager2.widget.ViewPager2;
import com.example.basepractice.R;
import com.example.basepractice.baseUI.basic.BasicActivity;
import com.example.basepractice.baseUI.demo2.adapter.TabPageAdapter;
import com.example.basepractice.baseUI.demo2.tab.Tab1Fragment;
import com.example.basepractice.baseUI.demo2.tab.Tab2Fragment;
import com.example.basepractice.baseUI.demo2.tab.Tab3Fragment;
import com.example.basepractice.baseUI.demo2.tab.Tab4Fragment;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;
public class Demo2Activity extends BasicActivity implements Tab1Fragment.OnFragmentInteractionListener,
Tab2Fragment.OnFragmentInteractionListener, Tab3Fragment.OnFragmentInteractionListener,
Tab4Fragment.OnFragmentInteractionListener {
private TabLayout tabLayout;
private ViewPager2 viewPager2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_demo2);
tabLayout = findViewById(R.id.tab_layout);
viewPager2 = findViewById(R.id.view_pager2);
configureTabLayout();
}
protected void configureTabLayout() {
// 1、通过 addTab 方法添加Tab,for循环添加4个 Tab for (int i = 0; i < 4; i++) {
tabLayout.addTab(tabLayout.newTab());
}
// 2、创建 adapter 并设置 Tab 个数
TabPageAdapter adapter = new TabPageAdapter(this, tabLayout.getTabCount());
// 3、viewPager2 设置 adapter viewPager2.setAdapter(adapter);
// 4、创建 TabLayoutMediator 将 TabLayout 和 ViewPager2 关联
new TabLayoutMediator(tabLayout, viewPager2, (tab, position) -> {
tab.setText("Tab " + (position + 1));
}).attach();
}
@Override
public void onFragmentInteraction(Uri uri) {
}
}
运行效果