文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

【Android】Jetpack全组件实战开发短视频应用App(三)

2022-06-06 13:57

关注

前言

这一篇我们将使用

Navigation
搭建我们
App
的基础架构,我们先看下效果
在这里插入图片描述

Navigation
介绍

官网地址
快速入门

导航组件由以下三个关键部分组成:

导航图:在一个集中位置包含所有导航相关信息的 XML 资源。这包括应用内所有单个内容区域(称为目标)以及用户可以通过应用获取的可能路径。 NavHost:显示导航图中目标的空白容器。导航组件包含一个默认 NavHost 实现 (NavHostFragment),可显示 Fragment 目标。 NavController:在 NavHost 中管理应用导航的对象。当用户在整个应用中移动时,NavController 会安排 NavHost 中目标内容的交换。
Navigation
使用

这里我们直接使用AS自带的模板创建一个

Activity

看下它的布局,很简单就是两部分,底部的

BottomNavigationView
和上面的
fragment
,我们先看下上面的这个
fragment
,可以看见它是一个叫
NavHostFragment
Fragment
,我们在上面介绍里面说过,这个是一个容器,我们简单看下它的源码

public class NavHostFragment extends Fragment implements NavHost {
	......
	@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    final Context context = getContext();
    //实例化NavController,它才是真正处理导航的,并且把FragmentNavigator实例化添加到SimpleNavigatorProvider 
    mNavController = new NavController(context);
   mNavController.getNavigatorProvider().addNavigator(createFragmentNavigator());
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
   @Nullable Bundle savedInstanceState) {
   FrameLayout frameLayout = new FrameLayout(inflater.getContext());
   frameLayout.setId(getId());
   return frameLayout;
    }
//onViewCreated传入当前的根布局以及NavController实例,并且给根布局设置一个tag,所以在每次findNavController的时候都会通过这个tag取唯一的实例,如果取不到会循环从父布局在去找。
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        if (!(view instanceof ViewGroup)) {
           throw new IllegalStateException("created host view " + view + " is not a ViewGroup");
        }
        View rootView = view.getParent() != null ? (View) view.getParent() : view;
        Navigation.setViewNavController(rootView, mNavController);
}
    public static void setViewNavController(@NonNull View view,
            @Nullable NavController controller) {
        view.setTag(R.id.nav_controller_view_tag, controller);
    }
	......
}

通过上面源码我们知道

NavHostFragment
里面实例化一个
NavController
,他是处理导航的,主要就是通过
navigate
方法实现跳转的

    
public void navigate(int resId, Bundle args, NavOptions navOptions) {
    //回退栈为null返回NavGraph
    //不为null返回回退栈中的最后一项
    NavDestination currentNode = mBackStack.isEmpty() ? mGraph : mBackStack.peekLast();
    if (currentNode == null) {
        throw new IllegalStateException("no current navigation node");
    }
    int destId = resId;
    //获取resId对应的NavAction
    final NavAction navAction = currentNode.getAction(resId);
    if (navAction != null) {
        if (navOptions == null) {
            navOptions = navAction.getNavOptions();
        }
        //通过NavAction获取目的地id
        destId = navAction.getDestinationId();
    }
    //若destId为0而navOptions又不为null则弹出到该navOptions的指定的页面
    if (destId == 0 && navOptions != null && navOptions.getPopUpTo() != 0) {
        popBackStack(navOptions.getPopUpTo(), navOptions.isPopUpToInclusive());
        return;
    }
    //为0报错
    if (destId == 0) {
        throw new IllegalArgumentException("Destination id == 0 can only be used" + " in conjunction with navOptions.popUpTo != 0");
    }
    //找到准备前往的目的地
    NavDestination node = findDestination(destId);
    if (node == null) {
        final String dest = NavDestination.getDisplayName(mContext, destId);
        throw new IllegalArgumentException("navigation destination " + dest
                + (navAction != null
                ? " referenced from action " + NavDestination.getDisplayName(mContext, resId)
                : "")
                + " is unknown to this NavController");
    }
    if (navOptions != null) {
        //是否清除回退栈
        if (navOptions.shouldClearTask()) {
            popBackStack(0, true);
            mBackStack.clear();
        } else if (navOptions.getPopUpTo() != 0) {
            //导航之前弹出栈到指定栈
            // 是否将该页面也弹出
            popBackStack(navOptions.getPopUpTo(), navOptions.isPopUpToInclusive());
        }
    }
    //进行导航
    node.navigate(args, navOptions);
}

这里我们还需要介绍两个类

NavDestination
NavGraph
NavGraph
NavDestination
的集合,
NavDestination
就相当于是节点,它的源码有一个这个方法

接下来说说我们

activity_main
中的另一个布局
BottomNavigationView


用它的话你需要添加依赖

implementation 'com.google.android.material:material:1.1.0'

我们需要给他配置一个

menu
,告诉它我们需要几个tab,比如我们这个例子中配置了3个




我们回到

MainActivity
中看看它们是怎么使用的

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        BottomNavigationView navView = findViewById(R.id.nav_view);
        // Passing each menu ID as a set of Ids because each
        // menu should be considered as top level destinations.
        AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(
                R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications)
                .build();
        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
        NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
        NavigationUI.setupWithNavController(navView, navController);
    }
}

主要是在

NavigationUI.setupWithNavController(navView, navController);
上,我们点进去看下

 public static void setupWithNavController(
            @NonNull final BottomNavigationView bottomNavigationView,
            @NonNull final NavController navController) {
        bottomNavigationView.setOnNavigationItemSelectedListener(
                new BottomNavigationView.OnNavigationItemSelectedListener() {
                    @Override
                    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                        return onNavDestinationSelected(item, navController);
                    }
                });
        final WeakReference weakReference =
                new WeakReference(bottomNavigationView);
        navController.addOnDestinationChangedListener(
                new NavController.OnDestinationChangedListener() {
                    @Override
                    public void onDestinationChanged(@NonNull NavController controller,
                            @NonNull NavDestination destination, @Nullable Bundle arguments) {
                        BottomNavigationView view = weakReference.get();
                        if (view == null) {
                            navController.removeOnDestinationChangedListener(this);
                            return;
                        }
                        Menu menu = view.getMenu();
                        for (int h = 0, size = menu.size(); h < size; h++) {
                            MenuItem item = menu.getItem(h);
                            if (matchDestination(destination, item.getItemId())) {
                                item.setChecked(true);
                            }
                        }
                    }
                });
    }

这里分别监听了

BottomNavigationView
NavController
NavController
我们在上面说了下,这里我们简单看下
BottomNavigationView

    public static boolean onNavDestinationSelected(@NonNull MenuItem item,
            @NonNull NavController navController) {
        NavOptions.Builder builder = new NavOptions.Builder()
                .setLaunchSingleTop(true)
                .setEnterAnim(R.anim.nav_default_enter_anim)
                .setExitAnim(R.anim.nav_default_exit_anim)
                .setPopEnterAnim(R.anim.nav_default_pop_enter_anim)
                .setPopExitAnim(R.anim.nav_default_pop_exit_anim);
        if ((item.getOrder() & Menu.CATEGORY_SECONDARY) == 0) {
            builder.setPopUpTo(findStartDestination(navController.getGraph()).getId(), false);
        }
        NavOptions options = builder.build();
        try {
            //TODO provide proper API instead of using Exceptions as Control-Flow.
            navController.navigate(item.getItemId(), null, options);
            return true;
        } catch (IllegalArgumentException e) {
            return false;
        }
    }

最终还是会调用

navController.navigate(item.getItemId(), null, options);

OK,我们运行下看看效果
在这里插入图片描述

这里跟我们要实现的效果很像,但是还有些问题,一个是我们想动态添加底部,不在xml写死;我们在点击底部item的时候不要这个放大缩小动画;切换的时候

Fragment
不反复重建,源码使用的是
replace
方式,我们需要改进;这些问题我们将在下一篇解决


作者:Greathfs


阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     801人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     348人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     311人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     432人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     220人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯