运行效果图

Navigation + Fragment制作APP主页面

  • 前言
  • 正文
    • 1. 添加依赖
    • 2. 添加导航图
    • 3. 添加NavHost
    • 4. NavController控制显示Fragment
    • 5. Fragment之间跳转并传值
    • 6. 添加底部导航
    • 7. 底部导航栏控制Fragment切换
    • 8. 运行效果图和源码
  • 总结

前言

  我相信你肯定见过这样的App主页面,底部或者顶部有多个按钮,点击之后会切换当前的页面,滑动当前页面也会切换底部按钮,这里我用几个App的主页面来说明一下吧

淘宝


微博


CSDN App


支付宝


可以说绝大部分App都是这种主页面布局模式,当然还有很多,在这里举列子是让你有一个概念而已。

那么来看看本文中实现的效果是怎么样的。如果不满意,我想也就不浪费你的时间了。


正文

  从上面的一些APP主页面,在之前这种页面是通过什么来做的呢?这里有好几种组合:
Activity + Fragment + TabLayout + ViewPager
Activity + Fragment + RadioGroup(RadioButton) + ViewPager
相信你在很多的博客上或者自己的项目上看过或者使用过。而现在可以通过另一个更简洁的方式,那就是Activity+ Navigation + Fragment。虽然你看着只是少了一个控件而已,但实际上,大部分的工作都是由Navigation (导航)来完成。

说了这么多也该正式操作了,既然是写博客,自然要详细一些了,那么我们就从创建AS项目开始吧。这应该够详细了吧,首先创建一个名为AppHomeNavigation的项目。如下图所示,包名我就缩减了一下。

创建好之后如下图所示:

从这个图来看,项目本身没有任何问题,为了保险起见,建议先运行一下。

1. 添加依赖

  Navigation 是JetPack中的组件,感兴趣可以去查看Google JetPack官方文档。而如果你想单独查看的Navigation 内容,可以点击Navigation 文档。

打开你的app下的build.gradle。在dependencies闭包中添加如下依赖:

 def nav_version = "2.3.2"// navigation依赖 ui 和 fragmentimplementation "androidx.navigation:navigation-fragment:$nav_version"implementation "androidx.navigation:navigation-ui:$nav_version"

添加位置如下图所示:

添加好之后,点击Sync进行项目同步,同步时会自动下载这些依赖库并配置到你的项目中。

添加完了依赖,就得先来简单介绍一下这个Navigation了,Navigation分为三大件:导航图、NavHost、NavController。

为了方便我介绍下面的三个概念,这里假设有A、B、C三个Fragment。

现在要从A切换到B

导航图:读取这个切换目标及路径
NavHost:包含A、B、C的容器,用于显示Fragment。
NavController:在得知切换目标时,控制NavHost去显示B这个Fragment。

这么一说,你是否有一些理解了呢?

2. 添加导航图

鼠标右键点击resNewAndroid Resource File

然后会弹出一个窗体,在这个窗体里面设置文件名称,并选择文件类型,然后点击OK。

然后查看这个nav_graph.xml,你会发现有报错

不过先不用担心,因为这个里面是用来指向Fragment的,但是现在没有,那就创建。
然后先在com.llw.navigation下新建一个fragment包。
然后建一个Fragment类,这里命名我就用ABCDE来命名了,实际开发中是肯定不能这样的。


然后在layout新建一个布局fragment_a.xml

然后修改一下这个布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"android:orientation="vertical"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="A Fragment"android:textColor="#000"android:textSize="24sp" />
</LinearLayout>

布局有了,然后进入到AFragment中绑定这个布局的id。

public class AFragment extends Fragment {@Nullable@Overridepublic View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {return inflater.inflate(R.layout.fragment_a, container, false);}
}

那么这个AFragment就弄好了,同样按照上面的步骤创建BCDE的Fragment,这个重复的步骤我就不写了,因为有注水的涉嫌。
注意之前fragment_a.xml中我放了一个TextView用来表示这个是A,那么其他的xml中也要放置对应的BCDE,这样你切换的时候才能看到区别。

好了,下面可以打开这个nav_graph.xml进行Fragment的添加,在navigation标签下增加对AFragment的添加。

 <!--AFragment--><fragmentandroid:id="@+id/afragment"android:name="com.llw.navigation.fragment.AFragment"android:label="afragment"tools:layout="@layout/fragment_a" />

上面的也很简单,id表示它在导航图的标识,name指明这个Fragment的路径,包名+类名。label就是标签而已。layout就是绑定这个Fragment对应的布局。

这里你肯定回想,刚才不是在AFragment的onCreateView方法的返回中指明这这个布局吗?

那么这里又添加是为什么,因为你如果在导航图中指明了某一个Fragment的布局,那么在代码中就可以不用指明,也可以两者都指明,但至少要有一个地方指明,所以我这样写是可以的。为了让看的人更了解而已,虽然是多此一举。

那么这个nav_graph.xml的其他的Fragment也要添加,如下所示

<?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/nav_graph"><!--AFragment--><fragmentandroid:id="@+id/afragment"android:name="com.llw.navigation.fragment.AFragment"android:label="afragment"tools:layout="@layout/fragment_a" /><!--BFragment--><fragmentandroid:id="@+id/bfragment"android:name="com.llw.navigation.fragment.BFragment"android:label="bfragment"tools:layout="@layout/fragment_b" /><!--CFragment--><fragmentandroid:id="@+id/cfragment"android:name="com.llw.navigation.fragment.CFragment"android:label="cfragment"tools:layout="@layout/fragment_c" /><!--DFragment--><fragmentandroid:id="@+id/dfragment"android:name="com.llw.navigation.fragment.DFragment"android:label="dfragment"tools:layout="@layout/fragment_d" /><!--EFragment--><fragmentandroid:id="@+id/efragment"android:name="com.llw.navigation.fragment.EFragment"android:label="efragment"tools:layout="@layout/fragment_e" />
</navigation>

然后你会发现还报错,那么你可以现在navigation标签中添加

tools:ignore="UnusedNavigation"

它就不报错了,这句话的意思是未使用导航的许可。因为我现在还没有使用这个nav_graph.xml所以要加上这一句话告诉AS,让它放心。等我们真正去使用时,是没有影响的,去不去掉都行。

3. 添加NavHost

这个在上面是介绍过的,它是用来装载和显示Fragment的,都知道Fragment是要依附在Activity上的,那么很明显这个NavHost也是要放在Activity中,那么下面打开activity_main.xml。修改代码如下:

<?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:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><!--NavHost--><fragmentandroid:id="@+id/nav_host_fragment"android:name="androidx.navigation.fragment.NavHostFragment"android:layout_width="match_parent"android:layout_height="match_parent"app:navGraph="@navigation/nav_graph" /></androidx.constraintlayout.widget.ConstraintLayout>

可能这里你会比较陌生,这里的id,等下要在MainActivity中指明的,这里的name指明的是androidx.navigation.fragment.NavHostFragment,这个属性就表明这个fragment指明的就是NavHost,然后它还要添加需要显示的子Fragment,那么就通过navGraph来绑定这个导航图,之前导航图里面不是就有五个Fragment吗?所以这样NavHost的任务就完成了。

但是这时候又有一个问题,那就是我的这个NavHost初始显示哪一个Fragment,这一点Google的人也想到了,可以在导航图中指明。

打开nav_graph.xml。通过startDestination来指明启动Activity时显示的第一个Fragment。

app:startDestination="@id/afragment"


这里我指明了AFragment。那么值钱说到的三大件,就还差一个NavController。这个是用来控制NavHost显示Fragment,虽然我刚才在导航图nav_graph.xml中指明了第一个要显示的Fragment,但是它还缺少这个显示的动机,而这个动机由NavController来提供。

4. NavController控制显示Fragment

进入到MainActivity,在onCreate添加一句代码:

     //获取navControllerNavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);

这时候你运行代码,然后你就会发现,显示了AFragment。

惊不惊喜意不意外?明明这个NavController还什么都没有做的,为什么就可以显示了呢?实际上它已经在工作了,只是你没有注意而已。

Navigation.findNavController(this, R.id.nav_host_fragment);

通过这一行代码这个工作开关就已经打开了,打开中读取导航图中第一个要显示Fragment,然后显示在NavHostFragment中。

5. Fragment之间跳转并传值

平时在实际的开发中常常会从一个Fragment跳转到另一个Fragment,并且带一些参数过去,之前这些跳转都是比较麻烦的,需要自己去写一些业务逻辑,而且还很容易出问题,让人谈之色变。但是在Navigation中,这个状况得到了很大的改善。

那么具体来看一下是怎么做的,比如我现在从AFragment跳转到BFragment。

下面就是见证骚操作的时候了。打开nav_graph.xml,修改AFragment。

 <!--AFragment--><fragmentandroid:id="@+id/afragment"android:name="com.llw.navigation.fragment.AFragment"android:label="afragment"tools:layout="@layout/fragment_a"><!--添加动作--><actionandroid:id="@+id/action_afragment_to_bfragment"app:destination="@id/bfragment"app:enterAnim="@anim/nav_default_enter_anim"app:exitAnim="@anim/nav_default_exit_anim" /></fragment>

这里又多了一个新的action标签,表示动作,id的命名要规范,从这个命名来看就知道要从A跳转到B。然后就是destination属性,它指明一个跳转到的Fragment。enterAnim表示进入BFragment的动画,exitAnim表示退出BFragment的动画,这些都是Navgation中自带的。

现在动作写好了,那么下面就需要一个地方来触发这个动作,可以写一个简单的按钮来触发。

在fragment_a.xml中修改布局如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"android:orientation="vertical"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="A Fragment"android:textColor="#000"android:textSize="24sp" /><Buttonandroid:id="@+id/jumpBFragment"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="20dp"android:text="跳转到 BFragment"android:textAllCaps="false"android:textSize="16sp" />
</LinearLayout>

然后进入到AFragment中,绑定id,增加点击事件。代码如下:

package com.llw.navigation.fragment;import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;import com.llw.navigation.R;public class AFragment extends Fragment {@Nullable@Overridepublic View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {return inflater.inflate(R.layout.fragment_a, container, false);}@Overridepublic void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {super.onViewCreated(view, savedInstanceState);Button jumpBFragment = view.findViewById(R.id.jumpBFragment);jumpBFragment.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Navigation.findNavController(getView())//导航.navigate(R.id.action_afragment_to_bfragment);}});}
}

这里唯一不好理解的就是navigate,表示导航的意思,这里面我传入了刚才定义在nav_graph.xml中的action的id。就表示这个导航将要执行这个actiion,那么它就会跳转到BFragment。运行一下

很明显,跳过去了,不过感觉还少了点什么,因为平常Fragment之间跳转时都会传递参数过去,那么这个也要传参数,而Navigation也提供了这个功能,可以通过Bundle进行传参。

所以只要简单的修改这个点击的方法就可以了。

     jumpBFragment.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Bundle bundle = new Bundle();bundle.putString("content","How are you?");Navigation.findNavController(getView())//导航.navigate(R.id.action_afragment_to_bfragment,bundle);}});

这种Bundle传递参数我相信都不会陌生,那么在BFragment怎么去接收呢?

打开BFragment,修改代码如下:

package com.llw.navigation.fragment;import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;import com.llw.navigation.R;public class BFragment extends Fragment {@Nullable@Overridepublic View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {String content = getArguments().getString("content");Toast.makeText(getActivity(),content,Toast.LENGTH_SHORT).show();return inflater.inflate(R.layout.fragment_b, container, false);}
}

这里通过

getArguments().getString("content");

来获取刚才放入Bundle里面的值,你以前可能没有见过这个方法,但是你只要把getArguments当成是getBundle()就好理解了,因为它实际上就是返回了一个Bundle。

然后看一下运行的效果吧。


可以看到是不是已经传递了传输过来,简单吧。

现在你会发现跳转是没有问题,但是回退呢?怎么回去呢?如果你这个时候在BFragment点击系统的返回键,你会发现直接退出当前应用了,因为这个时候Fragment还是属于MainActivity,那么它使用的就是Activity的返回栈,可当前只有一个Activity,所以它就退无可退,只能关闭应用,回到桌面了。为了解决这个问题,Navigation也提供了一个属性,在activity_main.xml中的的fragment中增加一个属性

app:defaultNavHost="true"


然后这个时候你再试一下,从A到B,然后点击系统返回键,就会返回到A,然后再点一下返回键就会退出当前应用。

神不神奇?app:defaultNavHost="true"表示这个回退栈由NavController来管理,当这个退无可退时才会调用Activity的回退栈。默认就是false,可以不加。

6. 添加底部导航

鼠标右键点击resNewAndroid Resource File,然后选择Menu


之后打开menu.xml去添加子项。

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"><itemandroid:id="@+id/afragment"android:icon="@mipmap/ic_home"android:title="首页" /><itemandroid:id="@+id/bfragment"android:icon="@mipmap/ic_category"android:title="类别" /><itemandroid:id="@+id/cfragment"android:icon="@mipmap/ic_find"android:title="发现" /><itemandroid:id="@+id/dfragment"android:icon="@mipmap/ic_msg"android:title="消息" /><itemandroid:id="@+id/efragment"android:icon="@mipmap/ic_mine"android:title="我的" />
</menu>

之前我是有5个Fragment,那么这里添加5个item,并且item的id要和之前导航图的fragment的id保持一致。这里面的图标其实很容易搞到。

ic_home.png

ic_category.png

ic_find.png

ic_msg.png

ic_mine.png

现在menu创建好了,那么可以在activity_main.xml中进行添加了。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><!--NavHost--><fragmentandroid:id="@+id/nav_host_fragment"android:name="androidx.navigation.fragment.NavHostFragment"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_above="@+id/bottom_navigation"app:defaultNavHost="true"app:navGraph="@navigation/nav_graph" /><!--底部导航--><com.google.android.material.bottomnavigation.BottomNavigationViewandroid:id="@+id/bottom_navigation"android:layout_width="match_parent"android:layout_height="?attr/actionBarSize"android:layout_alignParentBottom="true"android:background="#FFF"app:menu="@menu/menu" /></RelativeLayout>

通过menu来指定导航栏的菜单,这样就把刚才的item都添加进去了

现在你通过预览已经可以看到这个底部导航栏了,不是吗?不过保险起见,我还是运行一手。

可以看到,底部的导航栏已经出来了,而且还可以点击,点击之后还有动画效果,并且图标和文字的颜色还有变化,因为实际上我只是放了灰色图标而已。那么这些工作就都是BottomNavigationView帮我们完成的,的确是省了不少事情,当然这个动画效果和点击之后的颜色都是可以让开发者自行改的。这是Google要做的UI统一,通过material来实现一些效果和动画。

7. 底部导航栏控制Fragment切换

在上面已经添加了底部导航栏,但是这个导航栏还没有和NavHost绑定起来,所以自然就无法在切换底部导航的同时,改变NavHost中的Fragment。进入到MainActivity。

public class MainActivity extends AppCompatActivity {BottomNavigationView bottomNavigation;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);bottomNavigation = findViewById(R.id.bottom_navigation);//获取navControllerNavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);//通过setupWithNavController将底部导航和导航控制器进行绑定NavigationUI.setupWithNavController(bottomNavigation,navController);}
}

像这样绑定之后,你现在点击底部导航之后,NavController就会控制NavHost去显示相应的Fragment。

不过在运行之前把BFragment中接收参数并且弹Toast的代码删掉,否则切换的时候拿不到这个参数,就会ANR。

运行看看吧。


下面来改一下切换后的图标颜色和文字颜色吧。

右键点击drawable然后新建一个menu_item_selected.xml。

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"><item android:color="#888888" android:state_checked="false" /><item android:color="#1296DB" android:state_checked="true" />
</selector>

意思很简单,就是设置未选中和选中时的颜色。进入activity_mian.xml修改BottomNavigationView

 <!--底部导航--><com.google.android.material.bottomnavigation.BottomNavigationViewandroid:id="@+id/bottom_navigation"android:layout_width="match_parent"android:layout_height="?attr/actionBarSize"android:layout_alignParentBottom="true"android:background="#FFF"app:itemIconTint="@drawable/menu_item_selected"app:itemTextColor="@drawable/menu_item_selected"app:labelVisibilityMode="labeled"app:menu="@menu/menu" />
     app:itemIconTint="@drawable/menu_item_selected"app:itemTextColor="@drawable/menu_item_selected"

可以看到itemIconTint和itemTextColor分别表示图标和文字,这里传入刚才传入的颜色样式。

app:labelVisibilityMode="labeled"

这个labeled表示一直显示标签文字,它还有三种模式,分别是auto、selected、unlabeled。

auto表示自动,默认就是这种模式,selected和auto差不多,unlabeled表示一直不显示标签文字。可以自行去尝试。

8. 运行效果图和源码

那么下面再运行一下吧。

可以看到现在有很多APP都是这样的效果。那么本篇文章要做的事情就做完了。

源码地址:AppHomeNavigation


总结

  其实我这里没有做通过滑动Fragment来切换BottomNavigationView。如果要做的话,就要加ViewPager来控制Fragment,而不是NavHost来控制了,那样就脱离了这个文章的目的了。而且使用ViewPager的话就是相当于你把BottomNavigationView替换RadioButton或者TabLayout来使用,这种方式也有很多,我就过多的说明了,上高水长,后会有期~

Android Navigation + Fragment 制作APP主页面导航(步骤 + 源码)相关推荐

  1. Android 腾讯位置服务使用(详细步骤+源码)

    腾讯位置服务使用 前言 正文 一.注册腾讯位置服务账号 二.创建平台应用Appkey 三.创建并配置AS工程 四.定位 ① 连续定位 ② 单次定位 ③ 后台定位 ④ 地理围栏 五.地图 ① 基础地图 ...

  2. app公司制作app的方法和步骤

    随着互联网的快速发展,许多实用应用越来越受欢迎.同时,许多朋友想制作和开发应用程序,但他们不知道如何制作和开发应用程序.今天,应用开发公司将告诉你. app公司制作app的方法和步骤 一.手机app怎 ...

  3. 软件导航页源码+可封装APP/下载软件引流整站源码

    正文: 完整标题: 源码介绍: APP软件应用下载导航网站源码/APP分享下载页引流导航网站源码带后台版 这款源码 安装非常便捷干净,源码只有十几MB 只需要上传源码修改数据库连接信息即可使用. 后台 ...

  4. android 快传 源码_安卓APP仿茄子快传源码,Android项目源码类似茄子快传的快传项目包括服务端...

    适用范围:安卓APP仿茄子快传源码,Android项目源码类似茄子快传的快传项目包括服务端 演示地址:(以截图为准) 运行环境:Android+PC+web 其他说明: 本项目是一个基于安卓的类似茄子 ...

  5. 直播App中Android酷炫礼物动画如何通过直播程序源码实现方案

    直播App中Android酷炫礼物动画如何通过直播程序源码实现方案 这个动画,如果输出成GIF的话,要3MB,尺寸是750*750.在上一篇文章中已经介绍过几种被否掉的方法,这里再稍作补充说明,如果使 ...

  6. APP软件应用下载导航网站源码+搭建教程

    APP软件应用下载导航网站源码/APP分享下载页引流导航网站源码带后台版. 功能简介: 1.源码包中带有安装教程 2.网站自适应PC+手机自适应 3.用户可以自行封装成app应用引流导航 4.可以在网 ...

  7. Android Glide图片加载框架(二)源码解析之with()

    文章目录 一.前言 二.如何阅读源码 三.源码解析 1.with() Android Glide图片加载框架系列文章 Android Glide图片加载框架(一)基本用法 Android Glide图 ...

  8. 有视频APP上线,一对一交友源码和抖音短视频源码稳步前行

    有视频APP上线,一对一交友源码和抖音短视频源码稳步前行 最近几年,网络上谈论直播和短视频的随处可见.茶余饭后,闲暇时间,经常会看会儿直播或者刷下短视频,全民皆可参与. 艾媒咨询数据显示,2017年中 ...

  9. HTML网页设计结课大作业~仿凡客服装商城主页纯HTML模板源码(HTML+CSS+JS)

    HTML网页设计结课作业~服装商城主页纯HTML模板源码(HTML+CSS+JS) 临近期末, 你还在为HTML网页设计结课作业,老师的作业要求感到头大?HTML网页作业无从下手?网页要求的总数量太多 ...

最新文章

  1. sklearn决策树模型机器学习参数解释很详细中文
  2. 云计算应用还有不少的障碍尚需解决
  3. session监听器统计在线人数存入数值后不能取出其值
  4. 多于2个字符串的拼接,禁止使用“+”,而应该用join
  5. ListView的Columns自适应内部文字
  6. 微信小程序获取openid
  7. mat 释放_Square Ma?mat住宅区:释放公共空间,连接社区居民
  8. linux在python的虚拟环境下运行程序_在win10和linux上分别安装Python虚拟环境的方法步骤...
  9. accept - 指示打印系统接受发往指定目标打印机的打印 任务
  10. Blender:超详细的甜甜圈制作教程(一)【原教程 油管:Blender Guru】——建模篇
  11. 机器学习基石 作业0
  12. 协会元宇宙产业园基地孵化器授牌案例:循环经济元宇宙加速基地
  13. LOMO Effect预览界面没有立即完全隐藏
  14. kotlin的先决条件函数
  15. kali2019安装netspeed教程
  16. 天池-淘宝用户行为数据分析(python+Tableau)
  17. iOS开发——frame和bounds详解
  18. UE4球体根据镜头方向前进
  19. 零售商商品管理系统——需求分析
  20. HDU oj 自动交题爬虫

热门文章

  1. 视频教程-Vue-cli3.x从入门到项目实战视频课程-Vue
  2. 随手记Android无障碍实践
  3. java swarm有什么用,Docker Swarm的使用总结
  4. 透支自己生命的健身房老板们
  5. 计算机专业买哪一款华硕电脑好,华硕笔记本哪款好 2018华硕笔记本销量排行
  6. Java使用FFmpeg处理视频文件的方法教程
  7. 辞职了要不要退微信群,如果要,如何“优雅地退群”?
  8. 批量将Txt文件内容拆分成Excel文件
  9. WIN10桌面图标开机后一种变乱问题解决的一种方法
  10. 公众号文章、底部菜单栏设置关键词自动回复问卷调查、地图导航、报名填报?