NavHostFragment的使用
- 在res文件夹下创建新建navigation文件夹在该文件下创建导航图.xml文件
- 在MainActivity的.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">
<!-- android:name 属性指向 NavHostFragment
app:navGraph 属性指向navigation 文件夹下的nav_graph文件 -->
<fragment
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/my_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
app:navGraph="@navigation/nav_graph"
app:defaultNavHost="true" />
</androidx.constraintlayout.widget.ConstraintLayout>
//----------------------------------------------------------------------
<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"
app:startDestination="@id/loginFragment">
<fragment
android:id="@+id/loginFragment"
android:name="com.example.application.LoginFragment"
android:label="fragment_login"
tools:layout="@layout/fragment_login" >
<action
android:id="@+id/action_loginFragment_to_friendsFragment"
app:destination="@id/friendsFragment"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right"/>
</fragment>
<fragment
android:id="@+id/friendsFragment"
android:name="com.example.application.FriendsFragment"
android:label="fragment_friends"
tools:layout="@layout/fragment_friends" />
</navigation>
navigation标签下可以放多个fragment 可以用线对其连接形成关系可以添加fragment之间传递的参数 也可以添加fragment切换的转场动画等等。。。
例:如果碰到从首页->列表->详情 想从详情直接返回到首页
- 给列表到详情的action的添加属性popUpto这个id设置后返回会回到popUpto指向的fragment
- 另一种可以通过代码动态设置
fun onJump(sesionId: String, sessType: Int) {
val navOption = NavOpitenHelper.defultAnim(fromBundle.popId)//可以直接写死 也可以是参数传过来
findNavController().navigate(CaptureFragmentDirections
.ActionCaptureFragmentToSearchForMedieFragment(sesionId,sessType),navOption)
}
fun defultAnim(popId: Int): NavOptions {
return NavOptions.Builder() //设置转场动画 和popUpto
.setEnterAnim(R.anim.slide_in_right)
.setExitAnim(R.anim.slide_out_left)
.setPopEnterAnim(R.anim.slide_in_left)
.setPopExitAnim(R.anim.slide_out_right)
.setPopUpTo(popId,false)
.build()
}
监听
findNavController(R.id.myNavHostFragment).addOnDestinationChangedListener{
_: NavController, navDestination: NavDestination, _: Bundle? ->
//TODO 具体业务 可以在这里判断正在交互是哪个Fragment 变更状态栏
// 沉浸式状态栏 全屏等都可以在这里处理
}
//这个可以直接获取正在交互的fagment的Id
val id = findNavController(R.id.myNavHostFragment).currentDestination?.id
ViewModel
这里就介绍一种创建方式
class LoginVM(var app:Application):AndroidViewModel(app) {
}
class LoginVM():ViewModel() {
}
//两种建议用第一种带Application
class LoginVM(var app:Application):AndroidViewModel(app) {
class Factory(val app:Application):ViewModelProvider.Factory{
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(LoginVM::class.java)) {
@Suppress("UNCHECKED_CAST")
return LoginVM(app)as T
}else{
throw IllegalAccessException("Unkown ViewModel class")
}
}
}
}
//-----------------------------activity中创建------------------------
class LoginFragment : Fragment() {
private val viewmodel:LoginVM by lazy {
val app = requireNotNull(this.activity).application
ViewModelProvider(this,LoginVM.Factory(app)).get(LoginVM::class.java)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewmodel.login.observe(viewLifecycleOwner, Observer {
it?.let {
if (it==1) {
findNavController().navigate(LoginFragmentDirections.actionLoginFragmentToFriendsFragment())
viewmodel.close()
}
}
})
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding: FragmentLoginBinding =
DataBindingUtil.inflate(inflater, R.layout.fragment_login, container, false)
binding.lifecycleOwner = this
binding.viewmodel = viewmodel
return binding.root
}
}
在UI布局文件中的使用
<layout
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">
<data>
<variable
name="viewmodel"
type="com.example.application.LoginVM" />
</data>
</layout>
//控件绑定数据 和点击事件
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="50dp"
android:text="@{viewmodel.title}"
android:onClick="@{()->viewmodel.login(phone,pwd)}"
android:contentDescription="@string/hello_blank_fragment"
app:layout_constraintBottom_toTopOf="@+id/phone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
//控件绑定数据逻辑处理
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="50dp"
app:bindtitle="@{viewmodel.type}"
android:contentDescription="@string/hello_blank_fragment"
app:layout_constraintBottom_toTopOf="@+id/phone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
//创建一个kotlin文件
@BindingAdapter("bindtitle")
fun bindtitle(titleView: TextView,titleType:Int){
if(titleType==1){
titleView.text = "标题1"
}else{
titleView.text = "标题2"
}
}
//这样就完成了数据逻辑的绑定 可以绑定ImageView 和Url 直接用glide 加载出来
RecyclerView的绑定
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<variable
name="viewmodel"
type="com.example.application.FriendsVM" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".FriendsFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvl"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:layout_width="match_parent"
android:layout_height="0dp"
tools:listitem="@layout/contacts_item"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
//------------------------activity中-----------------
private var adaper :FriendsAdapter?=null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewmodel.friendList.observe(viewLifecycleOwner, Observer {
it?.apply {
adaper?.setList(it)
}
})
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val bindind:FragmentFriendsBinding =
DataBindingUtil.inflate(inflater,R.layout.fragment_friends, container, false)
bindind.lifecycleOwner = this
bindind.viewmodel = viewmodel
adaper = FriendsAdapter(arrayListOf(),AdapterOnClike{
Timber.e(it.toString()) //点击事件
})
bindind.root.findViewById<RecyclerView>(R.id.rvl).apply {
adapter = adaper
}
return bindind.root
}
class AdapterOnClike<T>(val block:(T)->Unit){
fun onClike(t:T) = block(t)
}
class FriendsAdapter(var friends:MutableList<Friend>,val callback:AdapterOnClike<Friend>)
:BaseQuickAdapter<Friend,BaseDataBindingHolder<ContactsItemBinding>>(R.layout.contacts_item,friends) {
override fun convert(holder: BaseDataBindingHolder<ContactsItemBinding>, item: Friend) {
holder.dataBinding.also {
it?.frinds = item
it?.friendCallback = callback
}
}
}
//---------------item布局文件-------------------
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="frinds"
type="com.example.application.Friend" />
<variable
name="friendCallback"
type="com.example.application.AdapterOnClike" />
</data>
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="60dp"
android:onClick="@{()->friendCallback.onClike(frinds)}">
<ImageView
android:layout_marginLeft="16dp"
android:layout_gravity="center_vertical"
android:id="@+id/contacts_item_head"
android:layout_width="@dimen/dp_40"
android:layout_height="@dimen/dp_40"
app:imageUrl="@{frinds}"
android:scaleType="centerCrop"/>
<TextView
android:id="@+id/contacts_item_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="65dp"
android:drawablePadding="4dip"
android:ellipsize="end"
android:fontFamily="sans-serif-medium"
android:singleLine="true"
android:text="@{frinds.displayNick}"
android:textColor="#333333"
android:textSize="16sp" />
<View
style="@style/horizontal_light_thin_divider"
android:layout_gravity="bottom"
android:layout_marginLeft="56dp"/>
</FrameLayout>
</layout>
@BindingAdapter("imageUrl")
fun imageUrl(imageView: ImageView,friend:Friend){
if (TextUtils.isEmpty(friend.imgHead)) {
val decodeResource =
BitmapFactory.decodeResource(imageView.context.resources, R.mipmap.header_bg_5)
val drawTextToBitmap = drawTextToBitmap(decodeResource, friend.nickName.substring(0,1))
imageView.setImageBitmap(drawTextToBitmap)
}else{
Glide.with(imageView.context).load(friend.imgHead).into(imageView)
}
}