我正在使用android中的导航组件,其中最初设置了6个片段。问题是当我添加新片段(ProfileFragment)时。
当我从起始位置导航到此新片段时,按本机后退按钮不会弹出当前片段。相反,它只是停留在我所在的片段上-后退按钮什么也不做。
这是我的 navigation.xml :
<?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/dashboard_navigation"
app:startDestination="@id/dashboardFragment"
>
<fragment
android:id="@+id/dashboardFragment"
android:name="com.devssocial.localodge.ui.dashboard.ui.DashboardFragment"
android:label="DashboardFragment"
>
<action
android:id="@+id/action_dashboardFragment_to_newPostFragment"
app:destination="@id/newPostFragment"
app:enterAnim="@anim/slide_in_up"
app:exitAnim="@anim/slide_out_down"
app:popEnterAnim="@anim/slide_in_up"
app:popExitAnim="@anim/slide_out_down"
/>
<action
android:id="@+id/action_dashboardFragment_to_notificationsFragment"
app:destination="@id/notificationsFragment"
app:enterAnim="@anim/slide_in_up"
app:exitAnim="@anim/slide_out_down"
app:popEnterAnim="@anim/slide_in_up"
app:popExitAnim="@anim/slide_out_down"
/>
<action
android:id="@+id/action_dashboardFragment_to_mediaViewer"
app:destination="@id/mediaViewer"
app:enterAnim="@anim/slide_in_up"
app:exitAnim="@anim/slide_out_down"
app:popEnterAnim="@anim/slide_in_up"
app:popExitAnim="@anim/slide_out_down"
/>
<action
android:id="@+id/action_dashboardFragment_to_postDetailFragment"
app:destination="@id/postDetailFragment"
app:enterAnim="@anim/slide_in_up"
app:exitAnim="@anim/slide_out_down"
app:popEnterAnim="@anim/slide_in_up"
app:popExitAnim="@anim/slide_out_down"
/>
====================== HERE'S THE PROFILE ACTION ====================
<action
android:id="@+id/action_dashboardFragment_to_profileFragment"
app:destination="@id/profileFragment"
app:enterAnim="@anim/slide_in_up"
app:exitAnim="@anim/slide_out_down"
app:popEnterAnim="@anim/slide_in_up"
app:popExitAnim="@anim/slide_out_down"
/>
=====================================================================
</fragment>
<fragment
android:id="@+id/profileFragment"
android:name="com.devssocial.localodge.ui.profile.ui.ProfileFragment"
android:label="fragment_profile"
tools:layout="@layout/fragment_profile"
/>
</navigation>
在上图中,突出显示的箭头(在左侧)是我遇到麻烦的导航操作。
在我的 Fragment 代码中,我的导航方式如下:
findNavController().navigate(R.id.action_dashboardFragment_to_profileFragment)
其他导航操作正在按预期方式工作。但是由于某种原因,这个新添加的片段无法达到预期的效果。
当我导航到ProfileFragment以及按返回按钮时,没有日志显示。
我错过了什么吗?还是我的动作/片段配置有问题?
编辑: 我没有在 ProfileFragment 中执行任何操作。这是它的代码:
class ProfileFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_profile, container, false)
}
}
还有包含导航主机的活动xml :
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/dashboard_navigation"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:navGraph="@navigation/dashboard_navigation"
app:defaultNavHost="true"/>
</FrameLayout>
答案 0 :(得分:16)
对于在上一个片段(即家庭片段)中使用LiveData的任何人,只要您通过按“后退”按钮返回到上一个片段,片段就开始观察数据,并且由于ViewModel幸免于此操作,它会立即发出最后一个发出的值在我的情况下,这打开了我按下后退按钮的片段,因此后退按钮似乎不起作用,解决方案是使用仅发射一次数据的东西。我用了这个:
class SingleLiveData<T> : MutableLiveData<T>() {
private val pending = AtomicBoolean()
/**
* Adds the given observer to the observers list within the lifespan of the given
* owner. The events are dispatched on the main thread. If LiveData already has data
* set, it will be delivered to the observer.
*
* @param owner The LifecycleOwner which controls the observer
* @param observer The observer that will receive the events
* @see MutableLiveData.observe
*/
@MainThread
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
super.observe(owner, Observer { t ->
if (pending.compareAndSet(true, false)) {
observer.onChanged(t)
}
})
}
/**
* Sets the value. If there are active observers, the value will be dispatched to them.
*
* @param value The new value
* @see MutableLiveData.setValue
*/
@MainThread
override fun setValue(value: T?) {
pending.set(true)
super.setValue(value)
}
答案 1 :(得分:11)
如果要在导航组件中使用setupActionBarWithNavController,例如:
setupActionBarWithNavController(findNavController(R.id.fragment))
然后还在您的主要活动中覆盖并配置此方法:
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.fragment)
return navController.navigateUp() || super.onSupportNavigateUp()
}
我的MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setupActionBarWithNavController(findNavController(R.id.fragment))
}
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.fragment)
return navController.navigateUp() || super.onSupportNavigateUp()
}
}
答案 2 :(得分:6)
您可以将以下内容用于Activity
onBackPressedDispatcher.addCallback(
this,
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
onBackPressed()
// if you want onBackPressed() to be called as normal afterwards
}
}
)
对于fragment
,将需要requireActivity()
和回叫
requireActivity().onBackPressedDispatcher.addCallback(
this,
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
requireActivity().onBackPressed()
// if you want onBackPressed() to be called as normal afterwards
}
}
)
如果您有Button
或其他东西来执行操作,则可以使用
this.findNavController().popBackStack()
答案 3 :(得分:6)
在使用MutableLiveData
在片段之间导航时,我发生了这个问题,并在多个片段上观察实时数据对象。
我仅通过一次观察实时数据对象或使用SingleLiveEvent而不是MutableLiveData
来解决了这个问题。因此,如果您在此处遇到相同的情况,请尝试仅观察一次实时数据对象或使用SingleLiveEvent
。
答案 4 :(得分:1)
导航完成后,您需要将MutableLiveData设置为null。
例如
private val _name = MutableLiveData<String>()
val name: LiveData<String>
get() = _name
fun printName(){
_name.value = "John"
}
fun navigationComplete(){
_name.value = null
}
现在说您正在观察片段中的“名称”,并且一旦名称为John,便在进行导航,那么应该是这样的:
viewModel.name.observe(viewLifecycleOwner, Observer { name ->
when (name) {
"John" -> {
this.findNavController() .navigate(BlaBlaFragmentDirections.actionBlaBlaFragmentToBlaBlaFragment())
viewModel.navigationComplete()
}
}
})
现在,您的后退按钮将可以正常工作。
有些数据几乎只使用一次,例如Snackbar消息或导航事件,因此一旦使用完毕,您必须告知将值设置为null。
问题在于_name中的值保持为true,并且无法返回上一个片段。
答案 5 :(得分:0)
如果您使用Moxy或类似的库,请在从一个片段导航到另一个片段时签出该策略。
策略为AddToEndSingleStrategy
时,我遇到了同样的问题。
您需要将其更改为SkipStrategy
。
interface ZonesListView : MvpView {
@StateStrategyType(SkipStrategy::class)
fun navigateToChannelsList(zoneId: String, zoneName: String)
}
答案 6 :(得分:0)
在OnCreateView中调用onBackPressed
import JobCard from './JobCard';
import BlocSection from './BlocSection';
import axios from 'axios';
import '../assets/Carousel.scss';
import next from '../assets/img/next.png';
import back from '../assets/img/back.png';
export default class CarouselNew extends Component {
constructor(props) {
super(props);
this.state ={
jobs : [],
position : 0
};
}
async componentDidMount() {
try{
const jobs = await axios.get("API LINK", {responseType : "json"})
console.log('response : ', jobs)
this.setState({jobs : jobs.data})
}catch(e){
console.log(e)
}
}
changePositionNext(){
{this.state.position < this.state.jobs.length-3 ? (this.setState({position : this.state.position +3}))
: (this.setState({position : 0})) }
}
changePositionPrev(){
{this.state.position == 0 ? (this.setState({position : this.state.jobs.length-3}))
: (this.setState({position : this.state.position -3})) }
}
render(){
let selected = [];
selected = this.state.jobs.length >0 ? this.state.jobs.slice(this.state.position, this.state.position +3) : []
console.log('position : ', this.state.position);
console.log('selected : ', selected);
return(
<div className='main_container' id='job'>
<BlocSection section_title={"Nos offres d'emploi"} section_intro={'Lorem ipsum dolor sit amet consectetur, adipisicing elit. Sequi alias iste ducimus tenetur saepe reprehenderit quasi reiciendis ab architecto.'} />
<div className='slider_container'>
<div onClick={() => this.changePositionPrev()} className="arrow_container"><img src={back} alt="" className="arrow"/></div>
{this.state.position === this.state.jobs.length-2 || this.state.position === this.state.jobs.length-1 && this.state.jobs.length %3 != 0 ? (this.setState({position : this.state.jobs.length-3})) : null}
{this.state.position === -2 || this.state.position === -1 && this.state.jobs.length %3 != 0 ? (this.setState({position : 0})) : null}
<div className="card_container">
{selected.map(job =>{
const title = job.title.rendered;
const description = job.acf.job_description;
const lien = job.acf.job_linkedin_url;
console.log('title : ', title);
return(
<JobCard key={title} title={title} description={description} lien={lien} />
);
})}
</div>
<div onClick={() => this.changePositionNext()} className="arrow_container"><img src={next} alt="" className="arrow"/></div>
</div>
</div>
)
}
}
答案 7 :(得分:0)
导航后将MutableLiveData设置为false
将此代码放入您的ViewModel.kt
private val _eventNextFragment = MutableLiveData<Boolean>()
val eventNextFragment: LiveData<Boolean>
get() = _eventNextFragment
fun onNextFragment() {
_eventNextFragment.value = true
}
fun onNextFragmentComplete(){
_eventNextFragment.value = false
}
假设您要导航到另一个片段,请在导航操作后立即从viewModel调用onNextFragmentComplete方法。
将此代码放入您的Fragment.kt
private fun nextFragment() {
val action = actionFirstFragmentToSecondFragment()
NavHostFragment.findNavController(this).navigate(action)
viewModel.onNextFragmentComplete()
}
答案 8 :(得分:0)
对于使用 LiveData 设置导航ID的每个人,都无需使用SingleLiveEvent。设置初始值后,您可以将destinationId设置为null。
例如,如果您想从片段A导航到片段B。
ViewModel A:
"@aws-cdk/core:newStyleStackSynthesis": "true"
这仍然会触发片段中的Observer并进行导航。
片段A
val destinationId = MutableLiveData<Int>()
fun onNavigateToFragmentB(){
destinationId.value = R.id.fragmentB
destinationId.value = null
}