当前位置: 首页 > news >正文

Android Jetpack学习系列——Navigation

写在前面

Google在2018年就推出了Jetpack组件库,但是直到今天我才给重视起来,这真的不得不说是一件让人遗憾的事。过去几年的空闲时间里,我一直在尝试做一套自己的组件库,帮助自己快速开发,虽然也听说过Jetpack,但是压根儿也没去了解,但是其实自己已经无形之中用到过很多Jetpack中的库了,只是自己不知道,比如说databinding、viewmodel、camerax等等

所以我打算推出一个Jetpack的学习记录,今天是第一个组件:Navigation

老规矩,文末有demo的源码(永久0积分)

demo效果

正文

关于Navigation

据通义千问的说法:

Android Jetpack Navigation组件是Google推出的一个用于简化Android应用导航的库。旨在提供一种结构化和统一的方式来管理应用程序中的屏幕切换和导航流程,特别是对于基于Fragment的应用。

关于Navigation,我觉得可能大家在生活里对于它的功能并不会陌生,拿微信来说,底部有四个按钮,分别是“微信”、“通讯录”、“发现”、“我”,如下图

当我们分别点击四个按钮的时候,界面区域也会随之切换到对应的页面。这就是一种比较常见的底部导航功能。当然这种结合底部导航栏的fragment切换只是Navigation能够实现的其中一种,还有其他的并不需要底部导航栏的,比如说登录模块,登录模块可能包含着登录、注册和重置密码这三个子模块,那么按照UI的设计,就需要三张页面去实现,我们知道可以使用一个Activity去嵌套三个Fragment去实现,那么显然这种纯fragment切换,这也是Navigation可以实现的范畴

整体设计思路

今天展示Navigation的使用的demo的整体设计思路为:LoginActivity+MainActivity

其中LoginActivity包含着LoginFragment、RegisterFragment、ResetPasswordFragment

其中MainActivity包含HomeFragment、ContactFragment、FindFragment、MeFragment

编码开始

我的环境:AndroidStudio 4.2.2

(1)引用

项目级build.gradle

dependencies {...classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.5.0"...}

模块级build.gradle

plugins {....id 'androidx.navigation.safeargs.kotlin'
}dependencies {...//Navigationimplementation "androidx.navigation:navigation-fragment-ktx:2.4.2"implementation "androidx.navigation:navigation-ui-ktx:2.4.2"
}
(2)创建导航文件

步骤如下:

按照这样的步骤,即可创建login_nav_graph.xml和main_nav_graph.xml两个导航文件,如图:

此时,两份文件里内容还是空的,具体内容还需要自行添加

AndroidStudio支持可视化添加fragment以及相互之间的导航关系,这点真的是非常方便

1. 点击加号,可以添加fragment到导航文件

我们首先把使用到的fragment全部添加到导航文件中,如下图:

这里先看一下AndroidStudio自动为我们生成的代码

<fragmentandroid:id="@+id/loginFragment"android:name="com.swy.navigationdemo.login.LoginFragment"android:label="fragment_login"tools:layout="@layout/fragment_login" />

 这个fragment标签,与我们拖进来的三个fragment是一一对应的,它包含了四个参数

  • id:比唯一标识符,可供本文件其他地方调用
  • name:是对应Fragment的路径
  • label:是对应Fragment的一个标签
  • tools:layout:对应Fragment的布局文件

还有另一个属性也是值得关注的,就是最外层navigation标签的

app:startDestination="@id/loginFragment"

这表明了,在LoginActivity中,默认优先显示的是LoginFragment,

说明:我们首次添加的Fragment会被默认为优先显示的Fragment,即如果我这里优先添加LoginFragment到导航图,navigation自动生成app:startDestination="@id/loginFragment",那么如果我首先把RegisterFragment添加进导航图,那么这个属性就会是app:startDestination="@id/registerFragment"

2.增加Fragment间的导航关系 

点LoginFragment,框体右边中部会出现一个圆环

点击该圆环,并拖动,就会出现一条蓝线,将该蓝线指向RegisterFragment后松手

此时就会看到,LoginFragment到RegisterFragment的导航关系被建立了,观察新增的action代码

<actionandroid:id="@+id/action_loginFragment_to_registerFragment"app:destination="@id/registerFragment" />
  •  id:唯一标识符,可供Activity调用
  • destination:用来表示跳转的目的地,可见此时这个action表示的是跳转到registerFragment。需要说明的是,这个跳转每次都会新建实例,也就是我可以从LoginFragment跳转到LoginFragment,但是这两个LoginFragment是不同的实例,也就是栈内会同时存在两个不同的LoginFragment。

其他的属性:

  • app:launchSingleTop:类似于Android活动中singleTop启动模式,当该属性为true时,如果目标Fragment已经在回退栈的顶部(即用户最近访问的Fragment),那么Navigation组件将不会创建新的Fragment实例,而是重用已经存在的那个Fragment实例。如果目标Fragment已经在回退栈中,但不在栈顶,那么app:launchSingleTop属性将不会起作用。在这种情况下,即使app:launchSingleTop设置为true,Navigation组件也会创建一个新的目标Fragment实例并将其推送到回退栈的顶部。
  • app:popUpTo:出栈直到某个元素。比如目前栈内有fragment1 - fragment2 - fragment3,当我在fragment4中定义了app:popUpTo:"@id/fragment1"时,那么fragment2和fragment3会被出栈,最终栈内情况为fragment1 - fragment4。
  • app:popUpToInclusive:这个属性是配合上面的app:popUpTo使用的,用来判断到达指定元素时是否把指定元素也出栈。还是以上面的例子来说明,如果该值为true,那么作为指定元素,fragment1也会被出栈,最终栈内只剩下fragment4.
  • app:enterAnim、app:exitAnim、app:popEnterAnim、app:popExitAnim:这四个属性都是跳转动画相关的,前两个用来配置移动到目的地的动画,后两个配置离开目的地的动画。
    举例说明fragment1跳转到fragment2:
    (1)enterAnim和exitAnim发生于fragment1跳转到fragment2的过程中:
    enterAnim是fragment2的入场动画、exitAnim是fragment1的离场动画
    (2)popEnterAnim和popExitAnim发生于fragment2返回到fragment1的过程中:
    popEnterAnim为返回发生后,fragment1的入场动画
    popExitAnim为返回时,fragment2的离场动画

按照我们最开始设计的跳转关系去完成导航文件,最终是这样的:

即:默认展示登录页面,登录页面可以跳转到注册页面或者是重置密码页面,同时在注册页面和重置密码页面也可以返回到登陆页面

同理,我们完成main_nav_graph的导航关系,如下

因为我们仿照的是微信的底部导航栏,home、contact、find、me四个fragment其实是平级的,它们之间并不存在导航关系。当然我们也可以根据自己的实际业务使用不同的action来设计不同维度和复杂度的导航关系。 

至此,导航文件也有了,剩下的就是创建一个支持导航关系的容器了

(3)导航主机

以LoginActivity为例

布局文件:

<?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=".LoginActivity"><androidx.fragment.app.FragmentContainerViewandroid:id="@+id/login_nav_host_fragment"android:name="androidx.navigation.fragment.NavHostFragment"android:layout_width="match_parent"android:layout_height="match_parent"app:defaultNavHost="true"app:navGraph="@navigation/login_nav_graph"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout>

创建FragmentContainerView作为导航主机(Navigation Host),这里有几个地方需要说明:

  • android:name="androidx.navigation.fragment.NavHostFragment"是固定的写法
  • app:defaultNavHost的作用是将该FragmentContainerView标记为默认的导航主机,这意味着这个FragmentContainerView会拦截系统的后退按钮事件。当用户点击后退按钮时,Navigation组件会按照导航图中的历史记录进行后退操作,而不是直接关闭Activity。并且在一个Activity中,通常只需要一个NavHostFragment作为导航主机。设置app:defaultNavHost="true"可以确保只有这一个NavHostFragment响应导航操作和后退按钮事件,避免多个NavHostFragment之间的冲突。
  • app:navGraph是将导航文件与导航主机相关联

LoginActivity:

class LoginActivity : AppCompatActivity() {private lateinit var binding: ActivityLoginBindingprivate lateinit var navController:NavControlleroverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = ActivityLoginBinding.inflate(layoutInflater)setContentView(binding.root)val navHostFragment = supportFragmentManager.findFragmentById(R.id.login_nav_host_fragment) as NavHostFragmentnavController = navHostFragment.navController}public fun getNavController(): NavController {return navController}override fun onSupportNavigateUp(): Boolean {return findNavController(R.id.login_nav_host_fragment).navigateUp()}
}

核心代码:获取NavController

说明,这里声明了一个方法,将获取到的navController返回了出去,主要是供Fragment中进行调用,因为这里Activity只是容器,具体的UI交互,是在对应的Fragment上面的,以LoginFragment为例:

class LoginFragment : Fragment() {private lateinit var binding: FragmentLoginBindingprivate lateinit var navController: NavControlleroverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)}override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View? {binding = FragmentLoginBinding.inflate(layoutInflater)val activity = requireActivity() as LoginActivitynavController = activity.getNavController()initListener()return binding.root}private fun initListener() {binding.login.setOnClickListener {val intent = Intent(activity, MainActivity::class.java)startActivity(intent)}binding.register.setOnClickListener {navController.navigate(R.id.action_loginFragment_to_resetPasswordFragment)}binding.reset.setOnClickListener {navController.navigate(R.id.action_loginFragment_to_resetPasswordFragment)}}}

通过这两行代码,fragment获取到了activity的navcontroller

  val activity = requireActivity() as LoginActivitynavController = activity.getNavController()

之后就可以操作导航事件了,如下

binding.register.setOnClickListener {navController.navigate(R.id.action_loginFragment_to_resetPasswordFragment)}binding.reset.setOnClickListener {navController.navigate(R.id.action_loginFragment_to_resetPasswordFragment)}

说明,这里面的

R.id.action_loginFragment_to_resetPasswordFragment

R.id.action_loginFragment_to_resetPasswordFragment

就是login_nav_graph.xml文件中定义的两个action的id

我相信写到这里,你基本上就能够看出来整个的调用机制了

 (4)Navigation+BottomNavigationView实现底部导航

上面讲了普通的fragment切换,那么关于带底部导航栏的切换,也还是很有必要说明以下的

主界面布局

<?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"><androidx.fragment.app.FragmentContainerViewandroid:id="@+id/main_nav_host_fragment"android:name="androidx.navigation.fragment.NavHostFragment"android:layout_width="0dp"android:layout_height="0dp"app:defaultNavHost="true"app:layout_constraintBottom_toTopOf="@+id/bottomNavigationView"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="0.0"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintVertical_bias="0.0"app:navGraph="@navigation/main_nav_graph" /><com.google.android.material.bottomnavigation.BottomNavigationViewandroid:id="@+id/bottomNavigationView"android:layout_width="match_parent"android:layout_height="60dp"app:menu="@menu/main_menu"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout>

显然,页面中只是增加了BottomNavigationView,对应的UI结构如下

 说明:

我这里使用了menu来实现了底部导航栏的几个item内容的导入,代码如下

<?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/homeFragment"android:icon="@mipmap/message"android:title="首页"app:showAsAction="ifRoom" /><itemandroid:id="@+id/contactFragment"android:icon="@mipmap/contact"android:title="联系人"app:showAsAction="ifRoom" /><itemandroid:id="@+id/findFragment"android:icon="@mipmap/find"android:title="发现"app:showAsAction="ifRoom" /><itemandroid:id="@+id/meFragment"android:icon="@mipmap/me"android:title="我"app:showAsAction="ifRoom" />
</menu>

重点说明:这里面四个item的id并不是随意定义的,一定要与main_nav_graph.xml文件中对应的几个fragment的id保持一致,否则,点击底部导航栏的按钮,是无法触发对应的fragment切换的!!!如下

 这里面只是UI上对应了,如何让bottomnavigationview与navcontroller也关联到一起呢

MainActivity.class

class MainActivity : AppCompatActivity() {private lateinit var binding:ActivityMainBindingprivate lateinit var navController: NavControlleroverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = ActivityMainBinding.inflate(layoutInflater)setContentView(binding.root)val navHostFragment = supportFragmentManager.findFragmentById(R.id.main_nav_host_fragment) as NavHostFragmentnavController = navHostFragment.navControllerbinding.bottomNavigationView.setupWithNavController(navController)}override fun onSupportNavigateUp(): Boolean {return findNavController(R.id.main_nav_host_fragment).navigateUp()}}

核心代码就是这一句了:

  binding.bottomNavigationView.setupWithNavController(navController)

补充内容

Fragment间数据通信的两种方式

先看效果

说明:

LoginFragment跳转到RegisterFragment使用SafeArgs方式

LoginFragment跳转到ResetpasswordFragment使用Bundle方式 

(1)SafeArgs(推荐)

Android官方推荐使用Safe Args来实现Fragment间数据通信,原因主要包括以下几个方面:

  1. 类型安全: Safe Args提供了类型安全的方式来传递参数。在navigation graph XML文件中定义的每个参数都有明确的数据类型(例如字符串、整数、布尔值等)。这将自动为这些参数生成对应的Args类,并提供get和set方法,从而确保在编译时就能捕获到类型不匹配的问题,而不是在运行时才出现崩溃。

  2. 清晰性与可读性: 在navigation graph中直接指定参数及其类型使得整个应用导航结构更加清晰。通过查看XML文件,开发者可以很容易地了解哪些参数在Fragment之间传递,以及它们的类型是什么。

  3. 减少代码量和错误: 使用Safe Args不需要手动创建和解析Bundle对象来传递数据,这大大减少了出错的可能性。自动生成的Args类简化了参数传递过程,使得开发者可以直接操作对象而非键值对,提高了编码效率。

  4. 生命周期感知: Safe Args配合Navigation组件一起使用时,能够更好地适应Android组件的生命周期变化。即使目标Fragment因为配置更改(如屏幕旋转)而重新创建,传递的参数也能得到妥善保存和恢复。

  5. 易于维护: 随着项目规模的增长,Safe Args能帮助保持代码的整洁和组织有序。当需要修改或添加新的参数时,只需要在navigation graph文件中更新即可,同时会自动反映到相关的Args类中,无需在多个地方手动同步修改。

具体的使用过程如下:

1.引用插件
项目级build.gradle

buildscript {...dependencies {...classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.5.3"}
}

模块级build.gradle

plugins {...id 'androidx.navigation.safeargs.kotlin'
}

引用完成之后,Sync项目,Rebuild项目

2.实际使用:修改login_nav_graph.xml文件,增加argument参数

<fragmentandroid:id="@+id/loginFragment"android:name="com.swy.navigationdemo.login.LoginFragment"android:label="fragment_login"tools:layout="@layout/fragment_login"><actionandroid:id="@+id/action_loginFragment_to_registerFragment"app:destination="@id/registerFragment"><argumentandroid:name="data1"app:argType="string"android:defaultValue=""/></action><actionandroid:id="@+id/action_loginFragment_to_resetPasswordFragment"app:destination="@id/resetPasswordFragment"></action></fragment>

说明:我在自己环境上调试的时候,发现我定义的argument属性在定义name的时候,总是会提示'xxx' is not a valid destination for tag 'argument'这样的错误,网上也没有找到相关的解释和解决方法,但是经过实际测试,这个地方报红并不影响使用,如图

LoginFragment.java

binding.register.setOnClickListener {val data1 = "这是使用safe args方式从登录界面传递的数据"navController.navigate(LoginFragmentDirections.actionLoginFragmentToRegisterFragment(data1))}

RegisterFragment.java

val data1 = arguments?.getString("data1")
binding.textData1.text = data1

 说明:

上面展示的是单一参数,多参数也是支持的,如下:

比如这里,我又增加了一个data3,那么在LoginFragment中,使用逗号隔开两个参数即可,如下

binding.register.setOnClickListener {val data1 = "这是使用safe args方式从登录界面传递的数据"val data3 = "data3"navController.navigate(LoginFragmentDirections.actionLoginFragmentToRegisterFragment(data1,data3))}

RegisterFragment

val data1 = arguments?.getString("data1")
val data3 = arguments?.getString("data3")
binding.textData1.text = data1+data3

最终的效果 

可见, 上面说的safeargs的优点,确实是做到了易于维护

易于维护: 随着项目规模的增长,Safe Args能帮助保持代码的整洁和组织有序。当需要修改或添加新的参数时,只需要在navigation graph文件中更新即可,同时会自动反映到相关的Args类中,无需在多个地方手动同步修改。

(2)Bundle

LoginFragment

binding.reset.setOnClickListener {val data2 = "这是使用普通Bundle方式从登录界面传递的数据"val bundle = Bundle();bundle.putString("data2",data2)navController.navigate(R.id.action_loginFragment_to_resetPasswordFragment,bundle)}

ResetPasswordFragment

val bundle = arguments
val data2 = bundle?.getString("data2")
binding.textData2.text = data2

至此,Navigation fragment间数据通信的两种方式的简单介绍就结束了

说明:关于safeargs的使用,前面讲解的并不是全部的实现方式,只是其中的一种,就比如我这里从LoginFragment跳转到RegisterFragment,我的argument是在LoginFragment里面定义的,我看网上还有讲解的是也可以在RegisterFragment里面定义,包括Bundle也可以与argument属性有联动关系等等之类的吧,大家有兴趣可以都了解一下

到目前为止,简单的demo算是初具雏形,源码如下

demo 源码

相关文章:

Android Jetpack学习系列——Navigation

写在前面 Google在2018年就推出了Jetpack组件库&#xff0c;但是直到今天我才给重视起来&#xff0c;这真的不得不说是一件让人遗憾的事。过去几年的空闲时间里&#xff0c;我一直在尝试做一套自己的组件库&#xff0c;帮助自己快速开发&#xff0c;虽然也听说过Jetpack&#…...

编程语言的新趋势

随着科技的飞速发展&#xff0c;IT行业经历了巨大的变革&#xff0c;其中编程语言作为技术生态的核心要素&#xff0c;其演变趋势对整个行业影响深远。从过去到现在&#xff0c;再到未来&#xff0c;编程语言的发展都呈现出明显的时代特征。本文将探讨当前IT行业的现状&#xf…...

C++:类和对象(2)

目录 1.strcut和class的区别 2.将成员属性设置为私有 3.对象的初始化和清理 3.1 构造函数和析构函数 3.1.1 构造函数语法 3.1.2 析构函数语法 3.1.3 检验 3.2 构造函数的分类和调用 3.3 拷贝构造函数调用 1.strcut和class的区别 struct和class的唯一区别在于默认的访问…...

【React系列】网络框架axios库的使用

本文来自#React系列教程&#xff1a;https://mp.weixin.qq.com/mp/appmsgalbum?__bizMzg5MDAzNzkwNA&actiongetalbum&album_id1566025152667107329) 一. axios库的基本使用 1.1. 网络请求的选择 目前前端中发送网络请求的方式有很多种&#xff1a; 选择一:传统的Aj…...

pygame学习(二)——绘制线条、圆、矩形等图案

导语 pygame是一个跨平台Python库(pygame news)&#xff0c;专门用来开发游戏。pygame主要为开发、设计2D电子游戏而生&#xff0c;提供图像模块&#xff08;image&#xff09;、声音模块&#xff08;mixer&#xff09;、输入/输出&#xff08;鼠标、键盘、显示屏&#xff09;模…...

TCL学习笔记(持续更新)

前言&#xff1a; TCL&#xff08;tool common language&#xff09;是一种通用工具语言&#xff0c;很多eda tool都支持tcl&#xff0c;学习了解一些tcl基本语法还是很有必要的。 1&#xff1a;基础概念 解释器&#xff1a; #!/usr/bin/tclsh 打印&#xff1a; puts -> p…...

Xpath的问题:为什么在DOM中确定存在(可见)的元素,用//表达式匹配不到(附解决办法)

今天遇到一个很有意思的问题&#xff0c;我的爬取的目标页面上有时会出现一个弹窗&#xff0c;它挡住我点击其它按钮了&#xff0c;我想找到它的关闭按钮&#xff0c;自动点击一下关闭掉&#xff0c;本来是很简单的事情&#xff0c;但偏偏出问题了&#xff0c;DOM中看到的html是…...

有没有游泳可以戴的耳机?游泳耳机入耳式好,还是骨传导好

游泳是一项既能锻炼身体又能让人放松心情的运动。我们知道&#xff0c;音乐能够为我们的水上时光增添更多的乐趣。那么&#xff0c;在众多游泳耳机中&#xff0c;如何选择一款既适合自己的需求又具备良好性能的产品呢&#xff1f; 首先&#xff0c;我们要了解的是&#xff0c;…...

【绘图软件】自用安装教程

链接&#xff1a;https://pan.baidu.com/s/17r9Pr460FzkULU7fTr91_w?pwdftv7 提取码&#xff1a;ftv7 --来自百度网盘超级会员V6的分享解压并且右键打开set up 解压crack软件前需要退出杀毒软件&#xff0c; 关闭实时保护 域网络关闭&#xff0c;专用网络关闭&#xff0…...

AIGC时代-GPT-4和DALL·E 3的结合

在当今这个快速发展的数字时代&#xff0c;人工智能&#xff08;AI&#xff09;已经成为了我们生活中不可或缺的一部分。从简单的自动化任务到复杂的决策制定&#xff0c;AI的应用范围日益扩大。而在这个广阔的领域中&#xff0c;有两个特别引人注目的名字&#xff1a;GPT-4和D…...

springBoot集成RabbitMQ实现(直连模式\路由模式\广播模式\主题模式)的消息发送和接收

该项目介绍了springboot如何集成rabbitMQ消息中间件,实现(直连模式\路由模式\广播模式\主题模式)的消息发送和接收 pom依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId...

Attention机制

目录 提出背景 Attention原理 Attention机制本质思想 Attention机制总结 改进&#xff1a;Self-Attention 前置知识&#xff1a;RNN&#xff0c;LSTM/GRU 提出背景 Attention模型是基于Encoder-Decoder框架提出的。Encoder-Decoder框架&#xff0c;也就是编码-解码框架&…...

Rust 常用的第三方库

Rust 的标准库虽然已经很强大&#xff0c;但如果恰当地使用第三方库&#xff0c;可以大大改善编程效率。以下是一些常用的 Rust 第三方库。 tokio ---- 最通用的异步编程库&#xff0c;几乎可以说是行业标准了&#xff0c;大量的其它库依赖于tokio。reqwest ---- HTTP 客户端库…...

构建高可用性Java应用:介绍分布式系统设计与开发

构建高可用性Java应用需要考虑许多因素&#xff0c;其中之一是设计和开发分布式系统。分布式系统是由多个独立计算机或节点组成的系统&#xff0c;这些节点通过网络连接&#xff0c;共同完成一项任务。 在分布式系统设计和开发中&#xff0c;有几个关键方面需要考虑&#xff1…...

x-cmd pkg | gitui - git 终端交互式命令行工具

目录 简介首次用户功能特点类似工具与竞品进一步探索 简介 gitui 由 Stephan D 于 2020 年使用 Rust 语言构建的 git 终端交互式命令行工具&#xff0c;旨在终端界面中便捷管理 git 存储库。 首次用户 使用 x gitui 即可自动下载并使用 在终端运行 eval "$(curl https:/…...

javaWeb案例知识点

一.rest风格编程 二.综合案例结构 三.分页查询 分页插件PageHelper 四.部门管理开发 五.员工管理开发 六.文件上传...

SQL日期列更新操作详解

在实际的数据库管理过程中&#xff0c;有时我们需要对数据库中的日期列进行更新。这篇博客将详细介绍一条 SQL 语句&#xff0c;该语句用于更新 referral_up_order 表中的多个日期列&#xff0c;并将它们的日期部分更改为 2023-10-24&#xff0c;同时保留原始时间部分。 1、背…...

stable diffusion 基础教程-图生图

界面 图生图大概有以下几个功能: 图生图涂鸦绘制局部绘制局部绘制(涂鸦蒙版)其常用的也就上面四个,接下来逐步讲解。 以图反推提示词 图生图可以根据反推提示词来获取相应图片的提示词,目前3种主流方式,如下: CLIP反推提示词:推导出的文本倾向于自然语言的描述方式,…...

如何获取高质量的静态住宅代理?常见问题与误区

静态住宅IP代理在今天的网络营销领域扮演着至关重要的角色&#xff0c;静态住宅IP代理以其稳定性和高匿名性&#xff0c;为互联网业务提供了一个安全的执行环境。通过模拟真实用户的网络行为&#xff0c;这些IP代理降低了企业在网络营销活动中被识别和封禁的风险。它保护了企业…...

基于SpringBoot的旅游网站281

文章目录 项目介绍主要功能截图:部分代码展示设计总结项目获取方式🍅 作者主页:超级无敌暴龙战士塔塔开 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题库【关注我,都给你】 🍅文末获取源码联系🍅 项目介绍 基于SpringBoot的旅游网站281,java项目。…...

Linux 文件类型,目录与路径,文件与目录管理

文件类型 后面的字符表示文件类型标志 普通文件&#xff1a;-&#xff08;纯文本文件&#xff0c;二进制文件&#xff0c;数据格式文件&#xff09; 如文本文件、图片、程序文件等。 目录文件&#xff1a;d&#xff08;directory&#xff09; 用来存放其他文件或子目录。 设备…...

HTML前端开发:JavaScript 常用事件详解

作为前端开发的核心&#xff0c;JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例&#xff1a; 1. onclick - 点击事件 当元素被单击时触发&#xff08;左键点击&#xff09; button.onclick function() {alert("按钮被点击了&#xff01;&…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)

文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

IT供电系统绝缘监测及故障定位解决方案

随着新能源的快速发展&#xff0c;光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域&#xff0c;IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选&#xff0c;但在长期运行中&#xff0c;例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...

在WSL2的Ubuntu镜像中安装Docker

Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包&#xff1a; for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

【Oracle】分区表

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...

dify打造数据可视化图表

一、概述 在日常工作和学习中&#xff0c;我们经常需要和数据打交道。无论是分析报告、项目展示&#xff0c;还是简单的数据洞察&#xff0c;一个清晰直观的图表&#xff0c;往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server&#xff0c;由蚂蚁集团 AntV 团队…...

无人机侦测与反制技术的进展与应用

国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机&#xff08;无人驾驶飞行器&#xff0c;UAV&#xff09;技术的快速发展&#xff0c;其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统&#xff0c;无人机的“黑飞”&…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.

ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #&#xff1a…...