nullPointerException between fragment and main activity, android/android-studio [duplicate] - java

This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 4 years ago.
I want to make a communication between my fragment and main activity so I need to get switch.isCheked but get:
java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.View android.app.Activity.findViewById(int)' on a null object reference
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new NotesFragment()).commit();
BottomNavigationView bottomNavigationView = findViewById(R.id.bottom_navigation);
final NotesFragment notesFragment = new NotesFragment();
final RemindersFragment remindersFragment = new RemindersFragment();
final SettingsFragment settingsFragment = new SettingsFragment();
final Switch aSwitch = settingsFragment.getView().findViewById(R.id.switch_pop_up);
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem menuItem) {
Fragment selectedFragment = null;
switch (menuItem.getItemId()) {
case R.id.action_notes:
if(aSwitch.isChecked())
Toast.makeText(MainActivity.this, "Your Notes", Toast.LENGTH_SHORT).show();
selectedFragment = notesFragment;
break;
case R.id.action_reminders:
if(aSwitch.isChecked())
Toast.makeText(MainActivity.this, "Your Reminders", Toast.LENGTH_SHORT).show();
selectedFragment = remindersFragment;
break;
case R.id.action_settings:
if(aSwitch.isChecked())
Toast.makeText(MainActivity.this, "Settings", Toast.LENGTH_SHORT).show();
selectedFragment = settingsFragment;
break;
}
assert selectedFragment != null;
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,
selectedFragment).commit();
return true;
}
});
}
}
SettingsFragment.java
public class SettingsFragment extends Fragment {
Switch switch_pop_up;
boolean stateSwitchPopUp;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_settings, null);
switch_pop_up = v.findViewById(R.id.switch_pop_up);
return inflater.inflate(R.layout.fragment_settings,container,false);
}
}
I think it is because of :
final Switch aSwitch = settingsFragment.getView().findViewById(R.id.switch_pop_up);
How can I fix this?

The problems are:
you are not attaching your SettingsFragment to activity to make it visible on screen (like you do with your NotesFragment)
you're trying to access it's child views from your MainActivity's onCreate method.
Fragments get initialized after activity is being created and ready to host them. Therefore, settingsFragment.getView() is returning null. It's onCreateView lifecycle method has not being called yet.
You need to reconsider your approach and try to find other solution. I suggest you to read this threads in SO to get more ideas on how to solve your problem.
How to access Fragment's child views inside fragment's parent Activity?
Access Fragment View from Activity's onCreate
Android: how to notify Activity when Fragments views are ready?

On your fragment, in the onCreateView method, change this line:
View v = inflater.inflate(R.layout.fragment_settings, null);
into:
View v = inflater.inflate(R.layout.fragment_settings, container, false);
Edit 1:
This happens because you're doing v.findViewById(), but you're not returning v on onCreateView().
Change your return line in the fragment to return v;
Edit 2:
Add a getter on the fragment to the switch_pop_up:
public Switch getSwitchPopUp() {
return switch_pop_up;
}
On your activity, replace this line:
final Switch aSwitch = settingsFragment.getView().findViewById(R.id.switch_pop_up);
with:
final Switch aSwitch = settingsFragment.getSwitchPopUp();
And you'll have to inflate your SettingsFragment before this line.
Note: Whichever result you need, this is not a good aproach anyway, so you might want to look into other ways to do it.

Related

Swipe between fragments with help of BottomNavigationBar

I have trouble finding a good example on how to swipe between fragments with help of bottom navigatiom bar. Since FragmentStatePagerAdapter is deprecated and a new ViewPager2 is now recommended instead of the old ViewPager I want to use ViewPager2 and FragmentStateAdapter in my code instead. I have found an example of how to combine BottomNavigationBar and ViewPager here and I want to do something similar. My code have many similarities to the one in the example with the only difference that I have my code in a fragment instead of an activity. Here is a picture of how my FrontendFragment display look like. I can switch between the views using the bottomnavigationbar but I also want to be able to swipe between the views. Can someone help me or at least direct me on the right way? Here is my code:
FragmentPagerAdapter Class:
public class FragmentPagerAdapter extends FragmentStateAdapter {
private static final int mFragmentCount = 5;
public FragmentPagerAdapter(#NonNull Fragment fragment) {
super(fragment);
}
#NonNull
#Override
public Fragment createFragment(int position) {
switch (position){
case 0:
return new HomeFragment();
case 1:
return new SearchFragment();
case 2:
return new AddFragment();
case 3:
return new MessageFragment();
case 4:
return new ProfileFragment();
}
return null;
}
#Override
public int getItemCount() {
return mFragmentCount;
}
}
FrontendFragment Class:
public class FrontendFragment extends Fragment implements BottomNavigationView.OnNavigationItemSelectedListener{
private BottomNavigationView mBottomNavigationView;
private ViewPager2 mViewPager2;
private FragmentPagerAdapter mFragmentPagerAdapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_frontend, container, false);
loadFragment(new HomeFragment());
mBottomNavigationView = v.findViewById(R.id.bottomNavigationBar);
mBottomNavigationView.setOnNavigationItemSelectedListener(this);
return v;
}
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Fragment selectedFragment = null;
switch (item.getItemId()) {
case R.id.home_icon:
selectedFragment = new HomeFragment();
break;
case R.id.search_icon:
selectedFragment = new SearchFragment();
break;
case R.id.add_icon:
selectedFragment = new AddFragment();
break;
case R.id.message_icon:
selectedFragment = new MessageFragment();
break;
case R.id.profile_icon:
selectedFragment = new ProfileFragment();
break;
}
return loadFragment(selectedFragment);
}
private boolean loadFragment(Fragment selectedFragment) {
if(selectedFragment != null){
MainActivity.sFm.beginTransaction().replace(R.id.relLayoutMiddle, selectedFragment).commit();
return true;
}
return false;
}
}
Thanks in advance!
As I'm already using BottomNav with ViewPager2 in one of my app, I can help.
Your code is partially correct which means your FragmentPagerAdapter is fine, but not your FrontEndFragment.
See, the FragmentPagerAdapter has to be set to a ViewPager2 as
//this here is the FrontEndFragment
mViewPager2.setAdapter(new FragmentPagerAdapter(this));
Then, You don't have to do the FragmentTransaction at all, you just have to change the ViewPager2's current item position through the BottomNavigationBar as
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.home_icon:
mViewPager2.setCurrentItem(0);
break;
case R.id.search_icon:
mViewPager2.setCurrentItem(1);
break;
case R.id.add_icon:
mViewPager2.setCurrentItem(2);
break;
case R.id.message_icon:
mViewPager2.setCurrentItem(3);
break;
case R.id.profile_icon:
mViewPager2.setCurrentItem(4);
break;
}
return false;
}
This is all, you don't have to deal with Fragments at all apart from the FragmentPagerAdapter. Also, don't forget to remove the loadFragment(new HomeFragment()); which is not required, nor the function loadFragment() is required.
(Optional), Furthermore, if you want to disable the Swipe Action of the ViewPager2 and want the Fragments to be selected based on the Selected BottomNav item only, then you can set setUserInputEnabled() property of ViewPager2 as false.
Next, to set the BottomNavigationBar's item as selected based on the swipe of the ViewPager2, what you've to do is,
Create a global var
MenuItem previousMenuItem;
Then, set a default item (first) to be selected of BottomNav on activity start as
mBottomNavigationView.getMenu().getItem(0).setChecked(true);
Finally, set an OnPageSelected() callback on ViewPager2 to update the selected Menu Item as:
mViewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels);
}
#Override
public void onPageSelected(int position) {
super.onPageSelected(position);
if (previousMenuItem != null) {
previousMenuItem.setChecked(false);
}
else {
mBottomNavigationView.getMenu().getItem(0).setChecked(false);
}
mBottomNavigationView.getMenu().getItem(position).setChecked(true);
previousMenuItem = mBottomNavigationView.getMenu().getItem(position);
}
#Override
public void onPageScrollStateChanged(int state) {
super.onPageScrollStateChanged(state);
}
});
What you're doing here is that you're setting default item to previousMenuItem and then on swiping to a different page, deselecting the previousMenuItem and selecting the new one, which means updating the BottomNav based on ViewPager's current item. This is the complete code you require to acheive your objective.
If you want to swipe between views, a simple solution would be to store all views in your parent view, but set all layouts for views except the initial view to android:visibility="gone". Make sure to set the initial view layout to android:visibility="visible" though. Now on you button clicks, you will have to implement onClick such that they turn on/ off view visibilities accordingly. For example, store views in order and control them via array index. But the whole thing you're trying to do is generally not a good design pattern in my opinion.
Why don't you load another Activity onClick instead of crowding your single activity? This will cause load time issues. Even if the views are non-visible, it's just an overall hassle to maintain all that in one place.

How can I add back to previous page on webview fragments?

I have an android app created with android studio that has a main activity and fragments with webview. Each fragment contains webview code with a link to a website. The problem that I have is that when I press back it closes the app. I want to use back to go to the previous page and the previous activity.
Main Activity code:
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()){
case R.id.nav_facebook:
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,
new FacebookFragment()).commit();
break;
case R.id.nav_twitter:
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,
new TwitterFragment()).commit();
break;
case R.id.nav_instagram:
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,
new InstagramFragment()).commit();
break;
case R.id.nav_linkedin:
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,
new LinkedinFragment()).commit();
break;
case R.id.nav_snapchat:
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,
new SnapchatFragment()).commit();
break;
case R.id.nav_pinterest:
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,
new PinterestFragment()).commit();
break;
case R.id.nav_youtube:
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,
new YoutubeFragment()).commit();
break;
case R.id.nav_about:
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,
new AboutFragment()).commit();
break;
case R.id.nav_privacy:
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,
new PrivacyFragment()).commit();
break;
}
drawer.closeDrawer(GravityCompat.START);
return true;
}
#Override
public void onBackPressed() {
DrawerLayout drawer = findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
}
Here is the code of one of the fragment's code (the rest of the fragments has similar code):
public class FacebookFragment extends Fragment {
public FacebookFragment() {
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_facebook, container, false);
WebView webView = (WebView)v.findViewById(R.id.webView);
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebViewClient(new WebViewClient());
webView.loadUrl("https://www.facebook.com/");
return v;
}
}
After replacing the fragment just make sure to add it to back stack using:
replace(R.id.fragment_container,
new YoutubeFragment()).addToBackStack("tag").commit()
and override the onBackPressed function to pop up stack while it's not empty.
All these fragments have same function is go back fragment. Why don't add the base fragment for them and the code will be easier.

Intent to activity is not working while using fragment for card view on click

I am using a fragment to hold 4 card views, which should start a new activity on click. It can display the toast I generate which shows that the click function is working but it is unable to activate the intent.
I have tried using several names for the activity and also
Intent intent = new Intent (getActivity(), activity name.class)
code to start activity
package com.example.eb.ui.home;
import ...
public class HomeFragment extends Fragment implements View.OnClickListener{
private HomeViewModel homeViewModel;
public View onCreateView(#NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
homeViewModel = ViewModelProviders.of(this).get(HomeViewModel.class);
View root = inflater.inflate(R.layout.fragment_home, container, false);
final CardView java = root.findViewById(R.id.javacardId);
final CardView html = root.findViewById(R.id.htmlcardId);
final CardView c_prog = root.findViewById(R.id.C_cardId);
final CardView cpp = root.findViewById(R.id.cppcardId);
//set on click listener
java.setOnClickListener(this);
html.setOnClickListener(this);
cpp.setOnClickListener(this);
c_prog.setOnClickListener(this);
return root;
}
#Override
public void onClick(View v) {
Intent i;
switch(v.getId()){
case R.id.javacardId :
i = new Intent(this,html.class);
startActivity(i);
break;
case R.id.htmlcardId :
i = new Intent(this,html_prog.class);
startActivity(i);
break;
case R.id.cppcardId :
i = new Intent(this,cpp_prog.class);
startActivity(i);
break;
case R.id.C_cardId :
i = new Intent(this,c_prog.class);
startActivity(i);
break;
default: break;
}
}
}
---
I expect the card view in that fragment to open up new activity
Try changing the "this" keyword to getContext()
"i = new Intent(this,html.class);"
should be
i = new Intent(getContext,html.class);
or initialize and set the clicklistener in onViewCreated() lifecycle method of Fragment.
You have to use getActivity() instead of 'this' in a fragment for context like below.
#Override
public void onClick(View v) {
Intent i;
switch(v.getId()){
case R.id.javacardId :
i = new Intent(getActivity(),html.class);
startActivity(i);
break;
default: break;
}
The first parameter required by Intent is Context
As mentioned in the Android documentation (Here):
packageContext Context: A Context of the application package implementing this class.
Hence, you need to pass getContext() or getActivity() (since Activity extends Context) as follows:
i = new Intent(getActivity(), html.class);
startActivity(i);
I tried building and launching an app using your code (after making the change above) and it worked fine.

How to replace a fragment from onOptionsItemSelected? [duplicate]

This question already has answers here:
findViewById in Fragment
(37 answers)
Closed 5 years ago.
I've got an AppCompatActivity which is the main View of the app.
There I've got an OptionsMenu with a button to display a drawer menu and another button to filter the results (Because each MenuItem in the drawer are different Listfragments).
What I need to accomplish is, when clicking the filter button on the OptionsMenu, open a fragment with all the filtering criteria.
So this is the code managing the explained above:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_menu:
if (drawer.isDrawerOpen(GravityCompat.START)) {
hideDrawer();
} else {
showDrawer();
}
return true;
case R.id.action_filter:
fragmentManager = getFragmentManager();
fragment = new FilterFragment();
args.putInt("idList", idList);
fragment.setArguments(args);
fragmentManager.beginTransaction().replace(R.id.drawer_layout, fragment).commit();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
Doing that the Fragment shows up and then I get a NPE:
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TableRow.setVisibility(int)' on a null object reference
Fragment onCreateViewMethod
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
setHasOptionsMenu(true);
getActivity().invalidateOptionsMenu();
skbrDays = getActivity().findViewById(R.id.seekBar);
daysLostRow = getActivity().findViewById(R.id.daysLostRow);
genderRow = getActivity().findViewById(R.id.genderRow);
locationRow = getActivity().findViewById(R.id.locationRow);
rgupGender = getActivity().findViewById(R.id.rgupGender);
rgupType = getActivity().findViewById(R.id.rgupType);
btnBreed = getActivity().findViewById(R.id.btnBreed);
recyclerView = getActivity().findViewById(R.id.recyclerView);
daysLostRow.setVisibility(View.VISIBLE);
return inflater.inflate(R.layout.activity_filter, container, false);
}
Ok, so I'm getting the NPE because getActivity() is returning null. And now I have to guess, not really knowing the reason, that is beacuse I'm not replacing the fragment on onCreate method from the activity?
If that is the reason how can I achieve the same result calling the fragment from onCreate?
Replace your code in onCreateView to below code.
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.activity_filter, container, false);
setHasOptionsMenu(true);
getActivity().invalidateOptionsMenu();
skbrDays = rootView.findViewById(R.id.seekBar);
daysLostRow = rootView.findViewById(R.id.daysLostRow);
genderRow = rootView.findViewById(R.id.genderRow);
locationRow = rootView.findViewById(R.id.locationRow);
rgupGender = rootView.findViewById(R.id.rgupGender);
rgupType = rootView.findViewById(R.id.rgupType);
btnBreed = rootView.findViewById(R.id.btnBreed);
recyclerView = rootView.findViewById(R.id.recyclerView);
daysLostRow.setVisibility(View.VISIBLE);
return rootView;
}

how to pass onOptionsItemSelected item id from Main activity to another activity

I want to pass itemId from main activity to another activity.
Now i wrote code like below, But i dont want like this, Just i want pass itemId from this activity to another when option is selected. In another activity i will write switch case option.
Here my code:
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
switch(item.getItemId()) {
case android.R.id.home:mDrawerToggle.onOptionsItemSelected(item);
case R.id.action_settings:SettingsFragment fragmentS = new SettingsFragment();
getSupportFragmentManager().beginTransaction().replace(R.id.content_frame, fragmentS).commit();
break;
}
return super.onOptionsItemSelected(item);
}
Use Intents to pass data between activities.
Intent intent = new Intent(current.this, Next.class);
intent.putExtra("itemid", item.getItemId());
startActivity(intent);
And in the other activity.
String itemId= getIntent().getStringExtra("itemid");
switch(itemId)
{
...
}
From your code it seems you want to pass value from a fragment to another fragment. If so you should edit the question. Its saying you want to pass value from one activity to another. I am answering according to fragment.
Do this
switch(item.getItemId()) {
case android.R.id.home:
mDrawerToggle.onOptionsItemSelected(item);
case R.id.action_settings:
SettingsFragment fragmentS = new SettingsFragment();
Bundle args = new Bundle();
args.putInt("ItemID", item.getItemId());
fragmentS.setArguments(args);
getSupportFragmentManager().beginTransaction().replace(R.id.content_frame, fragmentS).commit();
break;
}
In other fragment get the value like this
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
int itemId = getArguments().getInt("ItemID");
return inflater.inflate(R.layout.fragment, container, false);
}
So, you want to pass data to a fragment actually?
Try adding before strating a fragment transaction:
Bundle bundle = new Bundle();
bundle.putInt("id", tem.getItemId());
fragmentS.setArguments(bundle);

Categories