Android toolbar save state when visible/gone - java

I'm making a calculator (those bits are not shown in the code below) and I have a toolbar at the bottom of the application, and I can toggle its visibility from a menu. This is my current MainActivity class:
public class MainActivity extends Activity implements OnClickListener {
public Toolbar toolbar_btm;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toolbar_btm = findViewById(R.id.toolbar_bottom);
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//Toolbar toolbar_btm = findViewById(R.id.toolbar_bottom);
outState.putInt("TOOLBAR_VISIBLE", toolbar_btm.getVisibility());
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
//Toolbar toolbar_btm = findViewById(R.id.toolbar_bottom);
toolbar_btm.setVisibility(savedInstanceState.getInt("TOOLBAR_VISIBLE"));
}
//Toggles the toolbar to show/hide
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.toolbar_toggle:
Toolbar toolbar_btmtog = findViewById(R.id.toolbar_bottom);
if (toolbar_btmtog.getVisibility() == View.GONE)
toolbar_btmtog.setVisibility(View.VISIBLE);
else if (toolbar_btmtog.getVisibility() == View.VISIBLE)
toolbar_btmtog.setVisibility(View.GONE);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}
However, I cannot seem to keep this visible/gone state in memory. I've tried using onSaveInstanceState and onRestoreInstanceState, but when I run my code, it throws the following exception:
java.lang.RuntimeException: Unable to start activity
ComponentInfo{com.example.calculator/com.example.calculator.MainActivity}:
java.lang.NullPointerException: Attempt to invoke virtual method 'void
android.widget.Toolbar.setVisibility(int)' on a null object reference
I simply cannot figure out what the issue is. I'm using putInt and getInt and I'm declaring the toolbar with the right name. So, why am I getting a null object reference?

You have two varaibles named toolbar_btm. One is global and unassigned, and another is in onOptionsItemSelected(MenuItem item). That local variable "overwrites" local variable (in function)
You should declare Toolbar as global, and then assign its value in OnCreate() or, if that doesn't work, in overridden function OnResume(). You assign value by not using Toolbar keyword for a second time, so like this.
toolbar_btm = findViewById(R.id.toolbar_bottom);
UPDATE:
You could also drop Toolbar altogether and use ActionBar instead. This snippet will toggle action bar visibility in Activity (not Fragment):
ActionBar actionBar = getActionBar();
if(actionBar.isShowing())
actionBar.hide();
else
actionBar.show();
this.invalidateOptionsMenu();

You are not assigning your Toolbar variable. After menu option was selected you are using local one. Put this code in onCreate() :
toolbar_btm = findViewById(R.id.toolbar_bottom);

Related

How can i access the find view by id of bottom nav outside on create in android

I am working on an application where I am using bottom navigation on Home where i have three fragments on the second fragment called Post Ad I have a button called enter fragment zone through that i enter into another fragment now when I enter inside another fragment now I don't want there to show the bottom navigation so to hide it I am using a method inside my main activity called "setBottomNavigationVisibility" where I am writing code to set the visibility of bottom nav. but the problem is that it is throwing the null pointer exception in the mainactivty's method saying that
"void com.google.android.material.bottomnavigation.BottomNavigationView.setVisibility(int)' on a null object reference" on the method's line where i am setting the visibility
code of MainActivity
public class MainActivity extends AppCompatActivity {
NavController navController;
BottomNavigationView bottomNavigationView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
navController = Navigation.findNavController(this, R.id.fragmentContainerView);
bottomNavigationView = findViewById(R.id.activity_main_bottom_navigation_view);
NavigationUI.setupWithNavController(bottomNavigationView, navController);
}
public void setBottomNavigationVisibility(int visibility) {
bottomNavigationView.setVisibility(visibility);
}}
On the above method when i am trying to setting the visibility on the line bottomNavigationView.setVisibility(visibility); that's where it is thrwoing the exception
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = FragmentSecondBinding.inflate(inflater, container, false);
// Inflate the layout for this fragment
View view = binding.getRoot();
viewModel = new ViewModelProvider(requireActivity()).get(PageViewModel.class);
((MainActivity) requireActivity()).setBottomNavigationVisibility(View.GONE);
binding.toolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Navigation.findNavController(view).navigate(R.id.action_secondFragment2_to_postad);
}
});
No Please guide me how can i solve this error.
do something like this, define interface
interface DashboardActivityDelegate {
void hideBottomBar();
void showBottomBar();
}
your activity must implement this interface
and in your fragment in which you want to hide bottom nav view do this:
declare global variable
private DashboardActivityDelegate dashboardActivityDelegate;
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof DashboardActivityDelegate) {
dashboardActivityDelegate = (DashboardActivityDelegate)context;
}
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (dashboardActivityDelegate != null) {
dashboardActivityDelegate.hideBottomBar();
}
}
#Override
public void onDestroy() {
super.onDestroy();
if (dashboardActivityDelegate != null) {
dashboardActivityDelegate.showBottomBar();
}
}
in fragment you want hide bottom bar, just call this line in onViewCreated
getActivity().findViewById(R.id.activity_main_bottom_navigation_view).setVisibility(View.GONE);
and when you leave fragment, call this line in onDestroy or onStop to visible bottom nav for another fragment:
getActivity().findViewById(R.id.activity_main_bottom_navigation_view).setVisibility(View.VISIBLE)

invoke virtual method on null object reference (setDisplayHomeAsUpEnabled)

I want my update_activity to display a back arrow button but this code gives me this error:
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.app.ActionBar.setDisplayHomeAsUpEnabled(boolean)' on a null object reference
at this line:
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
What do you suggest?
public class UpdateActivity extends AppCompatActivity {
TextView textView;
AppBarLayout appbar;
private static Socket s;
private static PrintWriter pw;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_update);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
Button button = findViewById(R.id.UpdateButton);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
connect();
}
});
}
when I substitute that command with
Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
it returns me this other error (which I think is the same):
Unable to start activity
ComponentInfo{io.anycopy.googleplusdemo/io.anycopy.googleplusdemo.UpdateActivity}: java.lang.NullPointerException
Most likely you are using NoActionBar themes, in which case getSupportActionBar () return null and you must either change the theme or use getActionBar ()

Navigation Drawer onNavigationDrawerItemSelected called before MainActivity onCreate?

I have setup a new project with the template implementation of Navigation Drawer Fragment and a MainActivity.
It provides me with the following relevant methods:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = getIntent();
token = intent.getStringExtra(EXTRA_TOKEN);
mNavigationDrawerFragment = (NavigationDrawerFragment)
getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);
mNavigationDrawerFragment.activityMain = this;
mTitle = getTitle();
// Set up the drawer.
mNavigationDrawerFragment.setUp(
R.id.navigation_drawer,
(DrawerLayout) findViewById(R.id.drawer_layout));
}
My MainActivity is started by a splash activity which gets a saved access token via the EXTRA_TOKEN.
This is the override of the Navigation Drawer item select listener in the MainAcitivity:
#Override
public void onNavigationDrawerItemSelected(int position) {
// update the main content by replacing fragments
FragmentManager fragmentManager = getSupportFragmentManager();
onSectionAttached(position + 1);
switch(position) {
case 0:
fragmentManager.beginTransaction()
.replace(R.id.container, FeedFragment.newInstance(token, ""))
.commit();
break;
case 1:
fragmentManager.beginTransaction()
.replace(R.id.container, PeopleFragment.newInstance("", ""))
.commit();
break;
case 2:
if(qbloggedin) {
fragmentManager.beginTransaction()
.replace(R.id.container, MessagesFragment.newInstance(token, ""))
.commit();
}
break;
default:
break;
}
}
It starts three different fragments depending on which item is selected in the NavDrawer. While instantiating the new fragments, the token string is passed into its constructor, which is saved in the fragment's class for further use.
On the first start of the App however, it seems that onNavigationDrawerItemSelected is called before onCreate! This results me passing a null value token into the fragments, causing them to be all messed up.
How is this possible? As I understand it, the NavigationDrawerFragment should not have been setup yet!
I set breakpoints on both onCreate and on onNavigationDrawerItemSelected switch position = 0. onNavigationDrawerItemSelected is indeed hit before onCreate.
How can I make sure to get the token first before trying to handle the onNavigationDrawerItemSelected?
Any help would be appreciated.
I believe I figured this out as it was happening to me for anyone who searches this and can't find the answer.
If you use the Android Studio DrawerActivity then there is boilerplate code that they create for you. In this code in the activity_main.xml or whichever XML your DrawerActivity sets as its' content view, there is a tag.
When setContentView() is called in onCreate(), this fragment is automatically created and so technically onCreate() is still being called first but then the onNavigationDrawerItemSelected() method is called before anything else in create. Since setContentView is typically kept up top, this causes problems when trying to store the state of the fragments in your drawer.
Simply move any code that checks for savedInstanceBundle above setContentView() and it will fix the problem.
Example with comments:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// THIS IS WHERE YOU CHECK FOR SAVED INSTANCE
// Check for frag
if (savedInstanceState != null) {
Log.i(TAG, "Get QuestionDayFragment");
mQuestionDaysFragment = (QuestionDaysFragment) getSupportFragmentManager().getFragment(savedInstanceState, QUESTION_DAY_FRAGMENT);
}
// View injection
setContentView(R.layout.activity_main);
ButterKnife.inject(this);
// THIS IS WHERE THE CODE WAS BEFORE
// THIS WOULD BE CALLED AFTER onNavigationDrawerItemSelected()
// Singleton injection
LifeboxApplication.graph().inject(this);
// Toolbar
setSupportActionBar(mToolbar);
// FB
uiHelper = new UiLifecycleHelper(this, callback);
uiHelper.onCreate(savedInstanceState);
// Drawer
mNavigationDrawerFragment = (NavigationDrawerFragment) getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);
mTitle = getTitle();
mNavigationDrawerFragment.setUp(R.id.navigation_drawer, (DrawerLayout) findViewById(R.id.drawer_layout));
}
You could move the intent to a constructor and save your tokens there like so:
Intent i;
......
public FragmentConstructor() {
i = getIntent();
token = intent.getStringExtra(EXTRA_TOKEN);
}
What I had to do to make it work was to check if the page has loaded before executing onNavigationDrawerItemSelected
private Boolean loaded=false;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Your code here
this.loaded=true;
}
public void onNavigationDrawerItemSelected(int position) {
if (!this.loaded){
return;
}
I also agree with using a boolean to check if onCreate() has finished loading. My only other suggestions is that for a quick fix you can use onSectionAttached(int number) to process each item selected instead of onNavigationDrawerItemSelected.

Where should i declare the button of different layout?

I've made a calculator apps and I'm trying to create an about page showing some information.
The OK button will be coded setContentView(originallayout.xml) to return to the calculator layout.
Where should i put these codes to declare the OK button?
private Button btnOK;
btnOK = (Button)findViewById(R.id.btnOK);
btnok.setOnClickListener(OKListener);
I tried to put these code just below where i did for the buttons at the main layout but the apps just stopped after launch.
07-18 09:39:43.290: E/AndroidRuntime(6984): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.hychentsa.calculator/com.hychentsa.calculator.CalculatorActivity}: java.lang.NullPointerException
Instead of using setContentView() to change screens, you should have separate activities. Then, in your about activity, you can simply call finish() on button click to go back to the main activity.
http://developer.android.com/reference/android/app/Activity.html#startActivity(android.content.Intent)
if your layout doesn't content the id of the button (in your case btnOK), Eclipse throws NullPointerException - it can not find it in your layout's content.
So when you set your layout (or menu), it must content the id btnOK. Check it!
Put your button initialization after setContentView(R.layout.your_about_layout_name);
Put all this code in
Button btnOK;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.about);
btnOK = (Button)findViewById(R.id.btnOK);
btnok.setOnClickListener(OKListener);
}
Update:
Look at the answer of invertigo:
It's wrong to change the layout when you click on the button.
You have to do it this way:
CalculatorActivity
public class CalculatorActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.calculator_black);
// initialization of your views stays here
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.calculator_menu, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
switch (item.getItemId()) {
case R.id.your_id_to_go_in_about_activity:
Intent intent = new Intent(CalculatorActivity.this, AboutActivity.class);
// put some extras if you need to send information from this page to the
// AboutActivity page with this code: intent.putExtra();
startActivity(intent); // with this code you go to AboutActivity
return true;
case R.id.theme:
// Do Something with the theme
return true;
default:
return super.onOptionsItemSelected(item);
}
}
Now, the place for your initialization of OKButton is in new class, lets call it
AboutActivity
here you can put my earlier code:
public class AboutActivity extends Activity{
Button btnOK;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.about);
btnOK = (Button)findViewById(R.id.btnOK);
btnok.setOnClickListener(OKListener);
}
// and the listener for your OK button have to look like this:
OnClickListener OKListener = new OnClickListener() {
#Override
public void onClick(View v) {
// Do something here if you need
finish(); // with finish() you are returning to the previous page
// which is CalculatorActivity
}
};
}

Null PointerExeption ALL the time?

Why is mainB_news allways null? (in the method onBackPressed())
In onCreate I set a value to the button!!! :(
PS: with findViewById() I get the same error...
public class MainActivity extends Activity {
private Button mainB_news;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mainB_news = (Button) findViewById(R.id.mainB_news);
mainB_news.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
setContentView(R.layout.news);
}
});
}
#Override
public void onBackPressed() {
// check if page 2 is open
if (mainB_news != null && mainB_news.isShown()){
setContentView(R.layout.main); // open main view again
return;
}else
super.onBackPressed(); // allows standard use of backbutton for page 1
}
}
THANKS a lot!
Because you change contentView on button click. mainB_news doesn't exist after contentView changing. You shouldn't use setContentView() in this manner. Consider using another activity for showing news.
Because it is not defined in res/layout/main.xml ?

Categories