问题描述
为了防万一,让我解释一下整个事情。我正在将BottomNavigationView与Jetpack的Mobile Navigation图形一起使用。我重新实现了该方法:
NavigationUI.setupWithNavController(...)
在导航设置过程中,我没有使用这种方法,而是做了一个非常相似的事情:
customSetupWithNavController(...)
我所做的唯一更改是更改片段之间的默认淡入淡出过渡,并开始使用更自然的(在我看来)侧滑动画,就像onNavDestinationSelected中一样:
if(origin == 0){
builder.setEnteranim(R.anim.slide_in_from_right)
.setExitAnim(R.anim.slide_out_to_left)
.setPopEnteranim(R.anim.slide_in_from_left)
.setPopExitAnim(R.anim.slide_out_to_right);
}else if(origin == 1){
builder.setEnteranim(R.anim.slide_in_from_left)
.setExitAnim(R.anim.slide_out_to_right)
.setPopEnteranim(R.anim.slide_in_from_right)
.setPopExitAnim(R.anim.slide_out_to_left);
}
原点代表传入片段的来源方向。
它带来一个问题:我的两个碎片需要一个FAB才能将元素添加到回收站中,并且侧滑片的过渡突然变得丑陋。因此,我在MainActivity的Layout中添加了一个FAB,并且只有在调用这两个片段时,逻辑才会显示FAB。
我找不到一种将click事件从Activity传递给Fragments的好方法,因为导航无法处理整个过程,所以我无法实例化Fragments。
因此,我所做的就是创建一个ViewHolder,因为我知道它可以在生命周期变化中生存下来。这个ViewHolder拥有一个 int 和一个 mutablelivedata ,在MainActivity逻辑中,我将BottomNavigationView选择的元素的当前选定ID传递给 int >,并且只有在单击MainActivity的FAB时,实时的 Boolean 才设置为true。因此,在片段 onViewCreated()中,当该值设置为true且传递给ViewHolder的ID与该ID匹配时,我向此 Boolean 添加了观察者。当前片段中,布尔值设置回false,可以完成某些操作,如下所示:
eventsNotificationHandler.getClickEvent().observe(requireActivity(),new Observer<Boolean>() {
@Override
public void onChanged(Boolean aBoolean) {
if(aBoolean && eventsNotificationHandler.getPositionId() == R.id.nav_contacts){
eventsNotificationHandler.setClickEvent(false);
//do something here
}
}
});
此notificationHandler是ViewHolder。
到目前为止,到目前为止,我可以做到
:1-在BottomNavigationView的片段之间自由导航,FAB仅显示所需的片段。
2-随时可以在观察者内部使用Log.d(...),并看到调试消息就可以了。
3-随时随地敬酒一条消息,仅,如果上下文参数是在Observer外部初始化的,则如下所示:
Toast.makeText(以前为DefinedContext,“ SOME TEXT”,Toast.LENGTH_SHORT).show();
我不能做什么:
1-只要有需要,就可以在观察者内部使用与以前相同的想法启动一个活动,仅在观察者之前和之后初始化上下文,就像我能够启动意图一样,这个:
eventsNotificationHandler.getClickEvent().observe(requireActivity(),new Observer<Boolean>() {
@Override
public void onChanged(Boolean aBoolean) {
if(aBoolean && eventsNotificationHandler.getPositionId() == R.id.nav_contacts){
eventsNotificationHandler.setClickEvent(false);
Intent newContact = new Intent(prevIoUslyDefinedContext,NewContactActivity.class);
startActivity(newContact);
requireActivity().overridePendingTransition(R.anim.slide_in_from_right,R.anim.slide_out_to_left);
}
}
});
但是在这种情况下,如果我直接导航到这个特定的片段(在打开应用程序后定义了观察者的位置),我可以随意启动新的Activity 但仅限决定先浏览其他片段,然后再转到该片段尝试启动“活动”,应用程序崩溃
我注意到,当我从Observer内部调用requireContext()时,会发生这种确切的行为,但是它可以工作,但是随后停止工作。
该应用崩溃的原因:
E / Android运行时:致命异常:主要 流程:cu.arrowtech.bpawreckproject,PID:18019 java.lang.IllegalStateException:片段FragmentContacts {883259b}(9f127bdb-127d-4366-b90b-c8900a5a771e)}未附加到活动
我想要的:
1-如果可能的话,通过在MainActivity中按FAB从片段内部启动新Activity的正确方法。
2-如果可能的解决方案意味着改变我已经拥有的逻辑,那么切换片段的一种好方法。
3-继续使用Jetpack和导航图。
我可以通过在每个Fragment中使用2个独立的FAB来做我想做的事,但是过渡并不美观。
我愿意提出建议,即使这暗示着改变逻辑。我几乎可以肯定,这一定是一种更好的方法,而不是为此目的使用ViewHolder。
我想得到类似于Google Pay的东西,似乎是用于添加付款方式,通过和转账的按钮是相同的按钮,但是它可以适应每种情况。
解决方法
经过一些研究,我确实找到了一种方法来保持碎片之间的流畅过渡 AND 移动导航组件 AND MainMainity布局中的单个FAB。
我所做的是使用接口而不是ViewModels(我一直知道这种方法是错误的):
public interface SharedViewsInterface {
void onFabClicked();
}
因此,在MainActivity中,我将该接口定义为null,并根据情况创建了一种定义该接口的方法。我的MainActivity看起来像:
public class MainActivity extends AppCompatActivity {
//UI elements
private FloatingActionButton main_fab;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Same dode to get the custom animations
//.......
//Main and only Fab configuration
main_fab = findViewById(R.id.main_fab);
main_fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(sharedViewsInterface !=null){
sharedViewsInterface.onFabClicked();
}
}
});
}
//Other auxiliary methods
public void setInterface(SharedViewsInterface sharedViewsInterface){
this.sharedViewsInterface = sharedViewsInterface;
}
}
这样做,我可以从每个片段中通过执行以下操作在onCreate中实现接口:
((MainActivity) requireActivity()).setInterface(new SharedViewsInterface() {
@Override
public void onFabClicked() {
Intent newContact = new Intent(requireContext(),NewContactActivity.class);
startActivity(newContact);
requireActivity().overridePendingTransition(R.anim.slide_in_from_right_short,R.anim.slide_out_to_left_medium);
}
});
这很好用,因为仅当可见带有接口实现的片段时才显示FAB,请参见我的示例gif