【问题标题】:Passing value from Adapter to a BottomNavigationView Fragment in Navigation components将值从适配器传递到导航组件中的底部导航视图片段
【发布时间】:2021-04-18 06:28:54
【问题描述】:

应用程序在MainActivity 上有一个BottomNavigationView,用于处理片段之间的导航。其中一个片段中有一个RecyclerViewRecyclerView 的项目有一个按钮。

我正在尝试让 RecyclerView Items 按钮导航到另一个 Fragment 并随之传递一个值,但我在传递数据时不断收到空对象引用。

  1. 我创建了一个界面来获得对 Fragment 开关的 MainActivity 的点击,并创建了一个 Bundle 以将接收到的值传达给所需的 Fragment
// The interface
public interface OnItemClickListener {

    void onItemClick();

适配器类

//Define interface
 OnItemClickListener listener;

//Create constructor for interface
    public LocationsAdapter(Activity activity) {
        listener = (MainActivity)activity;

...
 @Override
    public void onBindViewHolder(@NonNull LocationsViewHolder holder, int position) {

//A click listener for the button
        holder.showMarkerButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
//Create instance of MapFragment to pass data as Bundle
                MapFragment mapFragment = new MapFragment();
 LatLng latLng = new LatLng(markerObject.getLatitude(),markerObject.getLongitude());
//Create Bundle and add data
                Bundle bundle = new Bundle();
bundle.putString("latitude",markerObject.getLatitude().toString());
bundle.putString("longitude",markerObject.getLongitude().toString());
                listener.onItemClick(bundle);
                    }
        });
}
  1. 然后在MainActivity中实现了切换BottomNavigation的View的接口。
public class MainActivity extends AppCompatActivity implements OnItemClickListener {
    BottomNavigationView navView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        navView = findViewById(R.id.nav_view);
        // Passing each menu ID as a set of Ids because each
        // menu should be considered as top level destinations.
        AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(
                 R.id.navigation_home,R.id.navigation_map, R.id.navigation_locations, R.id.navigation_profile)
                .build();
        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
        NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
        NavigationUI.setupWithNavController(navView, navController);
    }

    @Override
    public void onItemClick() {
        navView.setSelectedItemId(R.id.navigation_map);
    }
}
  1. 作为最后一步,我调用了我在第一步中创建的 Bundle,但这就是出现空对象引用的地方。
//MapFragment
private void setCam(){
        Bundle bundle = getArguments();
        if (getArguments() != null ){
            String SLatitude = bundle.getString("latitude");
            String SLongitude = bundle.getString("longitude");
            Double latitude = Double.parseDouble(SLatitude);
            Double longitude = Double.parseDouble(SLongitude);
            LatLng latLng = new LatLng(latitude,longitude);
            mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 20));
            Toast.makeText(getActivity(), latLng.toString(), Toast.LENGTH_SHORT).show();
        }
        else{
            Toast.makeText(getActivity(), "error present", Toast.LENGTH_SHORT).show();
        }
    }

提前致谢。

【问题讨论】:

  • 能否在主活动中包含设置片段的代码?
  • 您正在创建捆绑包并将数据放入其中,但没有将其传递到任何地方。跟踪您创建的 mapFragment 对象发生了什么。
  • @ShayKin 我添加了完整的 MainActivity
  • @M.Azyoksul 是的,现在我明白了问题所在,必须将 Bundle 提交到我将切换到的 Fragment 才能访问 Bundle。如何引用 BottomNavigation 正在使用的同一 MapFragment 实例?我了解通常使用 FragmentManager 提交 Bundle,例如 getFragmentManager().beginTransaction().add(R.id.container, fragment).commit(); ,但由于导航由 NavController 负责,因此我不能使用 FragmentManager。
  • 在这种情况下为什么不使用ViewModel

标签: java android android-fragments android-recyclerview bundle


【解决方案1】:
MapFragment mapFragment = new MapFragment();
LatLng latLng = new LatLng(markerObject.getLatitude(),markerObject.getLongitude());
//Create Bundle and add data
Bundle bundle = new Bundle();
bundle.putString("position",latLng.toString());
mapFragment.setArguments(bundle);

这里的mapFragment 只是一个未在导航图中使用的已创建对象。即它是一个独立于导航的独立对象,因此它不参与导航,因为它与 navGraph 中的当前mapFragment 片段不同。

解决方案:

为了解决这个问题,你需要在BottomNavigationView的片段之间切换时传递参数,注册OnNavigationItemSelectedListener是一个好地方:

首先允许适配器通过侦听器回调将参数发送回活动

所以,修改监听回调以接受参数

interface OnItemClickListener listener {
    void onItemClick(Bundle args);
}

在适配器上应用

@Override
    public void onBindViewHolder(@NonNull LocationsViewHolder holder, int position) {

        //A click listener for the button
        holder.showMarkerButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                
                LatLng latLng = new LatLng(markerObject.getLatitude(),markerObject.getLongitude());
                
                //Create Bundle and add data
                Bundle bundle = new Bundle();
                bundle.putString("position",latLng.toString());
                
                //Interface to transport the click event to the MainActivity to switch Fragment in the BottomNavigationView
                listener.onItemClick(bundle);
            }
        });
}

最后,将OnNavigationItemSelectedListener 注册到navView 并修改回调以接受活动中的Bundle:

public class MainActivity extends AppCompatActivity implements OnItemClickListener {
    BottomNavigationView navView;
    
    private Bundle mapArgs;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        navView = findViewById(R.id.nav_view);
        // Passing each menu ID as a set of Ids because each
        // menu should be considered as top level destinations.
        AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(
                 R.id.navigation_home,R.id.navigation_map, R.id.navigation_locations, R.id.navigation_profile)
                .build();
        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
        NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
        NavigationUI.setupWithNavController(navView, navController);
        
        
        navView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                navController.navigate(item.getItemId(), mapArgs);
                return true;
            }
        });     
        
    }

    @Override
    public void onItemClick(Bundle args) {
        navView.setSelectedItemId(R.id.navigation_map);
        mapArgs = args;
    }


}

【讨论】:

  • 谢谢,我搞定了,但我还有一个问题。为了更好地理解,我更新了问题中的 MapFragment 方法。如果我按下 RecyclerView 的 Item 内的按钮,它会导航到 MapFragment 但 getArguments() 方法返回 null,但如果我返回 RecyclerView 并再次按下按钮,则会打开 MapFragment 并显示带有 LatLng 的 Toast但它不会移动相机。 setCam 方法在 onMapReady() 内部调用。
  • 嗨@EdwardAarma 我看到listener.onItemClick(); 仍然在listener.onItemClick(bundle); 之后被调用你能检查它是否还在吗?如果是,则将其删除
  • 那是我的错,没有注意到将其留在问题中,我现在修复了它,但除此之外我只有 onItemClick 包含捆绑包。
  • @EdwardAarma 你还有问题吗?
  • 是的,基本上,当我第一次使用按钮从 RecyclerView 导航到 MapFragment 时,bundle 返回 null,如果我重新创建 MapFragment,然后我得到 Bundle 但 moveCamera();方法不行。
【解决方案2】:

当使用导航控制器时,你传入带有参数的数据。转到您的导航组件 XML 文件并进行以下更改;

1.在导航动作内部,需要添加一个参数。

<action
    android:id="@+id/action_id"
    app:destination="@id/destinationFragment">
    <argument
        android:name="paramterName"
        app:argType="string" />
</action>

2.将相同的参数添加到目标片段

<fragment
    android:id="@+id/coursesNavFragment"
    android:name="com.example.example.DestinationFragment"
    android:label="destination_fragment"
    tools:layout="@layout/destination_fragment" >
    <argument
        android:name="parameterName"
        app:argType="string" />
</fragment>

您也可以使用设计视图轻松完成此操作。

现在,您调用以导航到目标片段的函数希望您向它传递一个参数。

navigationController.navigate(actionSourceFragmentToDestinationFragment("text123"))

然后您可以在目标片段中提取捆绑包。

val parameter = DestinationFragmentArgs.fromBundle(requireArguments()).parameterName
// parameter has the value of "text123"

由于导航组件使用安全参数,您可以传递从Parcellable 继承的任何类型的参数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-06-02
    • 1970-01-01
    • 2020-04-19
    • 2022-06-16
    • 2019-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多