The application has a BottomNavigationView
on the MainActivity
which deals with the navigation between Fragments. One of the Fragments has a RecyclerView
in it and the items of the RecyclerView
have a button.
I am trying to make the RecyclerView
Items' button navigate to another Fragment and pass a value along with it but I keep receiving a null object reference with passing the data.
MainActivity
for the Fragment switch and created a Bundle to communicate the received value to the required Fragment// The interface
public interface OnItemClickListener {
void onItemClick();
The Adapter class
//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);
}
});
}
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);
}
}
//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();
}
}
Thanks in advance.
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);
Here the mapFragment
is just a created object that is not used in the navigation graph. i.e. it's a standalone object that is independent of the navigation, so it is not involved in the navigation as it's not the same as the current mapFragment
fragment in the navGraph.
Solution:
In order to solve this, you need to pass the argument whenever you switch among fragments of the BottomNavigationView, and registering OnNavigationItemSelectedListener
is a good place for that:
First allow the adapter to send the arguments back to the activity through the listener callback
So, modify the listener callback to accept a parameter
interface OnItemClickListener listener {
void onItemClick(Bundle args);
}
Apply that on adapter
@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);
}
});
}
And finally, register OnNavigationItemSelectedListener
to the navView
and modify the callback to accept the Bundle in activity:
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;
}
}