I've started to change from using ActionBarActivity to AppCompatActivity, but I've also started using the Toolbar as well instead of a standard ActionBar.
However, in one of my activities which has a swiping tab type of layout, the following line seems to be deprecated:
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
Although I have looked at some other answers on Stack Overflow regarding this, is there any built-in way of changing this to support using the Toolbar. If so, could you explain exactly how I would go about changing it? How would deprecated methods like onTabSelected be changed?
Also, I've noticed that the Google Play Music app has what looks like an extended toolbar/section underneath the tabs (see this image for what I am talking about). How would I be able to have that type of layout?
Thanks in advance.
So after a few days of lots of research, and looking through GitHub, I finally managed to solve my problem.
Steps to updating the ActionBar tabs to Toolbar tabs using AppCompatActivity:
UPDATE: After Friday 29th May 2015:
Thankfully, using a TabLayout with the Toolbar has become much simpler since the announcement of the Android Design Support Library in Google I/O 2015.
We no longer need to download custom view classes, and this is something Google really should have done a long time ago.
From the Android Developers' Blogspot post on the Android Design Support Library:
Tabs:
Switching between different views in your app via tabs is not a new concept to material design and they are equally at home as a top level navigation pattern or for organizing different groupings of content within your app (say, different genres of music).
The Design library’s TabLayout implements both fixed tabs, where the view’s width is divided equally between all of the tabs, as well as scrollable tabs, where the tabs are not a uniform size and can scroll horizontally. Tabs can be added programmatically:
TabLayout tabLayout = ...;
tabLayout.addTab(tabLayout.newTab().setText("Tab 1"));
However, if you are using a ViewPager for horizontal paging between tabs, you can create tabs directly from your PagerAdapter’s getPageTitle() and then connect the two together using setupWithViewPager(). This ensures that tab selection events update the ViewPager and page changes update the selected tab.
Prior to Google I/O 2015:
Firstly, I downloaded the SlidingTabLayout.java and SlidingTabStrip.java files from Google's I/O Conference app on GitHub. These would be the views that would be used in the tab layout, so I created a folder with my other Java activities called 'view' and placed them there.
Next, I edited my activity layout .xml to look a bit like this:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:ads="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.mycompany.myapp.MyActivity" >
<!-- This is the Toolbar with the tabs underneath -->
<LinearLayout android:id="#+id/detail_headerBar"
style="#style/HeaderBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<include android:id="#+id/detail_toolbar" layout="#layout/toolbar" />
<com.mycompany.myapp.view.SlidingTabLayout
android:id="#+id/sliding_tabs"
android:background="?attr/colorPrimary"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<!-- This is the ViewPager (which I had used before) and
it would be responsible for the swiping to change layouts -->
<android.support.v4.view.ViewPager
android:id="#+id/view_pager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/detail_headerBar"
android:layout_above="#+id/detail_adView" />
<!-- I also had an AdView in my layout,
but this is not necessary for creating tab layouts -->
<com.google.android.gms.ads.AdView
android:id="#+id/detail_adView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
ads:adSize="SMART_BANNER"
ads:adUnitId="#string/banner_ad_unit_id" >
</com.google.android.gms.ads.AdView>
</RelativeLayout>
The line which references the Toolbar (<include android:id="#+id/detail_toolbar" layout="#layout/toolbar" />), is referencing the following layout (for those who aren't sure how to use the Toolbar yet):
<android.support.v7.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/toolbar"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:minHeight="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light" />
And in both of the .xml layout files above, ?attr/colorPrimary, refers to the primary colour of my app (which I had defined in a style).
Also, in the first layout, the style I had mentioned as #style/HeaderBar refers to the following:
<style name="HeaderBar">
<item name="android:background">?colorPrimary</item>
<item name="android:elevation">4dp</item>
<!-- You may have to override this in a v21 version of this file -->
</style>
Before I started setting up the layouts in Java, I had to make sure to change the package names in SlidingTabLayout.java and SlidingTabStrip.java corresponding to where they were placed. In my case, I used: package com.mycompany.myapp.view; in both of these files.
Now, in my Activity (which was extending AppCompatActivity), I first added the following in the onCreate method:
Toolbar toolbar = (Toolbar) findViewById(R.id.detail_toolbar);
setSupportActionBar(toolbar);
This would be reponsible for displaying the Toolbar.
Then I setup the ViewPager and SlidingTabLayout parts:
mViewPager = (ViewPager) findViewById(R.id.view_pager);
mViewPager.setAdapter(new ViewPagerAdapter(getSupportFragmentManager()));
mSlidingTabLayout = (SlidingTabLayout) findViewById(R.id.sliding_tabs);
mSlidingTabLayout.setSelectedIndicatorColors(getResources().getColor(R.color.tab_line));
mSlidingTabLayout.setDistributeEvenly(true);
mSlidingTabLayout.setViewPager(mViewPager);
The colour 'tab_line' was a colour I had declared in color.xml which would be the colour of the tab line indicator. Also note that the variables above were global which I defined previously in this activity:
SlidingTabLayout mSlidingTabLayout;
ViewPager mViewPager;
The final thing to do was to setup the ViewPagerAdapter which I had called eariler. This would be responsible for changing the page depending on which tab was selected. I used the following:
public class ViewPagerAdapter extends FragmentPagerAdapter {
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
// Returns the number of tabs
return 3;
}
#Override
public Fragment getItem(int position) {
// Returns a new instance of the fragment
switch (position) {
case 0:
return new FragmentOne();
case 1:
return new FragmentTwo();
case 2:
return new FragmentThree();
}
return null;
}
#Override
public CharSequence getPageTitle(int position) {
Locale l = Locale.getDefault();
switch (position) {
case 0:
return getString(R.string.title_section1).toUpperCase(l);
case 1:
return getString(R.string.title_section2).toUpperCase(l);
case 2:
return getString(R.string.title_section3).toUpperCase(l);
}
return null;
}
}
I hope this was a thorough enough answer for those who are having the same trouble as I did, when switching from ActionBarActivity to AppCompatActivity and starting to use Toolbar instead of ActionBar. If there is anything that is unclear, feel free to comment below.
Related
So, I have an activity with navigation view. By click on its item I change fragment in activity. All fragment have the same toolbar. But one have this toolbar and TabLayout to it. I would like to know what is better to declare toolbar once on activity like this
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="#layout/toolbar" />
<FrameLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/toolbar" />
</RelativeLayout>
or declare it in each fragment.
The disadvantage of the first method is default toolbar shadow. When I add tabs in fragment, shadow looks like
When I tried 2 solution. All my toolbar was with back icon instead drawer animated logo.
Thanks.
I had the exact same problem. This is how I solved it:
Move the toolbars to the fragments like you suggested (so you won't have a shadow separating the two). This allows for a way more flexible way to implement (different) toolbars in your layouts too.
Replace the Toolbar's navigation icon by a custom one like this:
toolbar.setNavigationIcon(R.drawable.ic_action_menu);
(I used the Android Asset Studio to easily create an icon with the preferred color)
Now open the NavigationView with the new menu(home) icon. You can do this through the MainActivity (the one with the NavigationView). Create a public method in that Activity that opens the drawer:
public void openDrawer(){
mDrawerLayout.openDrawer(Gravity.LEFT);
}
Now call this method in the OnOptionsItemSelected in your fragments like this:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// handle item selection
switch (item.getItemId()) {
case android.R.id.home: //Menu icon
((MainActivity)getActivity()).openDrawer();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
That's it. Of course the downside is that you must implement the Toolbar in each Fragment. However, this is the only way (that I know of) that enables you to have the Toolbar (+TabLayout) in a Fragment and still be able to control your NavigationView.
You can use AppBarLayout from design support library like:
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
...
app:layout_scrollFlags="scroll|enterAlways" />
<android.support.design.widget.TabLayout
...
/>
</android.support.design.widget.AppBarLayout>
and then you can change visibility of tablayout.
For more information about desing layout library : link
I want to implement a tabbed view pager where the tabs are not part of the action bar.
What is the correct way of doing this since it doesn't seem supported by standard.
So far I have been using the following approach and it seems like it's been so much more effort than it should be that it bothers me:
Create horizontally oriented linear layout to act as tab bar view
Create individual tab views to populate tab bar along with styling
Create custom ongesturelistener for left and right swipe to go between tabs
Create animations for sliding-in/out upon swiping (fragment transactions)
Create animations for the tab bar upon swiping
This seems like it's way too much work when there is a viewpager and tabbar built in. It seems though this "proper" approach is limited to only being used in conjunction with the actionbar. Can anyone provide a simple example of what I am trying to achieve? (see following picture for clarity).
You don't need to build a ViewPager bundled with an ActionBar, they are both independent views.
You can simply hide the ActionBar on that activity doing something like:
ActionBar mActionBar = activity.getActionBar();
mActionBar.hide();
Although it's an ugly solution you can do that to test your code. I would suggest either add a Theme to that specific Activity without an ActionBar (something like Theme.Material.NoActionBar) - not sure it's the correct name of the theme, but you get the idea.
Not sure I answered your question accordingly, but if not please show us some code.
You just add a viewpager with a pager tab strip. Plenty examples out there.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
FragmentManager fm = getSupportFragmentManager();
fragmentPagerAdapter = new MyTabsPagerAdapter(
fm, tabsArray);
ViewPager mPager = (ViewPager) findViewById(R.id.pager);
mPager.setAdapter(fragmentPagerAdapter);
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/default_screen_bg"
android:minWidth="1000dp">
put your image here
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<android.support.v4.view.PagerTabStrip
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:paddingBottom="4dp"
android:paddingTop="4dp" />
</android.support.v4.view.ViewPager>
I was using SupportActionBar with tabs and a custom ActionBar theme (created with http://jgilfelt.github.io/android-actionbarstylegenerator/), that show the tabs only when the user expands the search view.
public boolean onMenuItemActionExpand(MenuItem item) {
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
return true;
}
}
I migrated from ActionBar to Toolbar. My app really needs to support API 9.
Is there a way to use this code to add the tabs back?:
Toolbar toolbar = (Toolbar) findViewById(R.id.new_actionbar);
setSupportActionBar(toolbar);
getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
If possible, how can I use my custom theme or style the toolbar?
The documentation says that this is deprecated and suggests using a different type of navigation.
But I don't know of any other components in Android that have the same functionality.
Some help?
With the API 21 the method setNavigationMode(ActionBar.NAVIGATION_MODE_TABS) is deprecated.
UPDATE 01/08/2019 (Material Components Library)
Add the dependency to your build.gradle:
dependencies { implementation ‘com.google.android.material:material:1.1.0’ }
Then you can use the new TabLayout.
<androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.appbar.AppBarLayout ...>
<androidx.appcompat.widget.Toolbar .../>
<com.google.android.material.tabs.TabLayout
...
/>
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager.widget.ViewPager
android:id="#+id/viewpager"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
</androidx.constraintlayout.widget.ConstraintLayout>
The code is simple:
TabLayout tabs = (TabLayout) findViewById(R.id.tabs);
tabs.setupWithViewPager(pager);
UPDATE 29/05/2015 (Support Library)
With the new Design Support Library now you can use the TabLayout.
Just add this dependency to your build.gradle
compile 'com.android.support:design:22.2.0'
The code is very simple:
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
tabLayout.setupWithViewPager(viewPager);
To implement many of the features of material designs you should use it within a
CoordinatorLayout and a AppBarLayout.
Something like this:
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:layout_height="wrap_content"
android:layout_width="match_parent">
<android.support.v7.widget.Toolbar
...
app:layout_scrollFlags="scroll|enterAlways"/>
<android.support.design.widget.TabLayout
...
app:layout_scrollFlags="scroll|enterAlways"/>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
</android.support.design.widget.CoordinatorLayout>
OLD
You can use a different pattern. For example you can use the same example that you can see in googleio14.
It uses a SlidingTabLayout which works with a ViewPager.
Here you can find the example (it is in your sdk example)
Here you can find the Google io14 example:
Layout
Java
Although probably a little late to answer this question now, I realised that I wrote an answer on a similar question which covers both using the Design Support Library and prior to Google I/O.
I have included the essential parts below:
Using a TabLayout with the Toolbar has become much simpler since the announcement of the Android Design Support Library meaning we no longer need to download custom view classes.
From the Android Developers' Blogspot post on the Android Design Support Library:
Tabs:
Switching between different views in your app via tabs is not a new concept to material design and they are equally at home as a top level navigation pattern or for organizing different groupings of content within your app (say, different genres of music).
The Design library’s TabLayout implements both fixed tabs, where the view’s width is divided equally between all of the tabs, as well as scrollable tabs, where the tabs are not a uniform size and can scroll horizontally. Tabs can be added programmatically:
TabLayout tabLayout = ...;
tabLayout.addTab(tabLayout.newTab().setText("Tab 1"));
However, if you are using a ViewPager for horizontal paging between tabs, you can create tabs directly from your PagerAdapter’s getPageTitle() and then connect the two together using setupWithViewPager(). This ensures that tab selection events update the ViewPager and page changes update the selected tab.
If you are not using the Design Support library, you will instead need to do the following:
1. Download the SlidingTabLayout.java and SlidingTabStrip.java files from Google's I/O Conference app on GitHub. These would be the views that would be used in the tab layout, so I created a folder with my other Java activities called 'view' and placed them there.
2. Edit your layout to include the SlidingTabLayout:
<LinearLayout
android:orientation="vertical"
... >
<!-- This is the Toolbar with the tabs underneath -->
<LinearLayout
android:orientation="vertical"
... >
<include android:id="#+id/my_toolbar" layout="#layout/toolbar" />
<com.mycompany.myapp.SlidingTabLayout
android:id="#+id/sliding_tabs"
android:background="?attr/colorPrimary"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<!-- This is the ViewPager (which you already should have if you have
used tabs before) -->
<android.support.v4.view.ViewPager
android:id="#+id/view_pager"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
...
</LinearLayout>
The line which references the Toolbar (<include android:id="#+id/detail_toolbar" layout="#layout/toolbar" />), is referencing another XML file I had used to make the Toolbar.
3. Change the package names in SlidingTabLayout.java and SlidingTabStrip.java corresponding to where they were placed. In my case, I used something similar to: package com.mycompany.myapp.view; for both of these files. Organise imports and add in any necessary, as noted by the IDE you are using.
4. In your Activity (which was extending AppCompatActivity), setup the Toolbar in the onCreate method:
Toolbar toolbar = (Toolbar) findViewById(R.id.detail_toolbar);
setSupportActionBar(toolbar);
5. Setup the ViewPager and SlidingTabLayout parts:
mViewPager = (ViewPager) findViewById(R.id.view_pager);
mViewPager.setAdapter(new ViewPagerAdapter(getSupportFragmentManager()));
mSlidingTabLayout = (SlidingTabLayout) findViewById(R.id.sliding_tabs);
mSlidingTabLayout.setSelectedIndicatorColors(getResources().getColor(R.color.tab_line));
mSlidingTabLayout.setDistributeEvenly(true);
mSlidingTabLayout.setViewPager(mViewPager);
The colour 'tab_line' was a colour I had declared in color.xml which would be the colour of the tab line indicator. Also note that the variables above were global which I defined previously in this activity:
SlidingTabLayout mSlidingTabLayout;
ViewPager mViewPager;
6. Finally, setup the ViewPagerAdapter which I had called earlier. This would be responsible for changing the page depending on which tab was selected. I used the following code:
public class ViewPagerAdapter extends FragmentPagerAdapter {
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
// Returns the number of tabs
return 3;
}
#Override
public Fragment getItem(int position) {
// Returns a new instance of the fragment
switch (position) {
case 0:
return new FragmentOne();
case 1:
return new FragmentTwo();
case 2:
return new FragmentThree();
}
return null;
}
#Override
public CharSequence getPageTitle(int position) {
Locale l = Locale.getDefault();
switch (position) {
case 0:
return getString(R.string.title_section1).toUpperCase(l);
case 1:
return getString(R.string.title_section2).toUpperCase(l);
case 2:
return getString(R.string.title_section3).toUpperCase(l);
}
return null;
}
}
As I mentioned above, further details to these solutions are available on another question I answered, about using Sliding Tabs with the Toolbar.
I am using the google example called EffectiveNavigation to create a ViewPager with tabs. The problem is that in the manifest, for my main activity, I have set
android:theme="#android:style/Theme.Holo.NoActionBar.Fullscreen"
So that my app has no actionBar. Therefore I am getting a NullPointerException at
final ActionBar actionBar = getActionBar();//null from getActionBar()
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);//NullPointerException
Now, all I want is to be able to create a simple ViewPager with tabs. That's it. Nothing grand. I am using the google example because it is what I found.
Basically in the google example, they are using the statusBar to hold the tabs. How else might I hold the tabs? Anything less than a good example or instruction on how to modify the goggle example is not going to be much help as I don't know much about ViewPagers. The link to the google example is http://developer.android.com/training/implementing-navigation/lateral.html
A simple workaround the statusBar might be enough.
Using the ActionBar to hold the tabs is actually deprecated because, even if your app does have action bar, it may lead to NullPointerException. Good news are the TabLayout from the Design package brings you the possibility to easily create a ViewPager with tabs. Just write this in the XML of your Activity (or Fragment):
<android.support.v4.view.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<android.support.design.widget.TabLayout
android:id="#+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top" />
</android.support.v4.view.ViewPager>
And once you have your Adapter for the ViewPager, include this code in the onCreate method of your Activity (or Fragment) after the setContentView method:
ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
viewPager.setAdapter(new MyPagerAdapter(getSupportFragmentManager()));
TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
tabLayout.setupWithViewPager(viewPager);
Hope it helps ;)
I found a fix. Basically in onCreate, before setContentView, I call
requestWindowFeature(Window.FEATURE_ACTION_BAR);
Then everything works fine.
To anyone else arriving here finding that #learner's solution doesn't work for them, take a look at http://thepseudocoder.wordpress.com/2011/10/13/android-tabs-viewpager-swipe-able-tabs-ftw/
I have implemented a scrollable tab + swipe, everything works fine.
However I have some issues related to the current PagerTabStrip :
1. I can't make the current title Bold
2. Can't choose the default tab I want to display (e.g : the tab with position 2) when rendering the view, like the tabs in the Google Play app
3. The first and last tab have respectively a margin on the left and the right, can I set it to 0dp ?
This is what I could do for styling the tabs :
Java :
strip = (PagerTabStrip) findViewById(R.id.pager_tab_strip);
strip.setTabIndicatorColorResource(R.color.light_blue);
strip.setDrawFullUnderline(true);
Layout :
<android.support.v4.view.PagerTabStrip
android:id="#+id/pager_tab_strip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:background="#drawable/pagertitle_border_bottom"
android:paddingBottom="15dp"
android:paddingTop="15dp"
android:textColor="#color/light_blue" />
Can anyone help me with the 3 points ? Thank you.
Well, I actually found a proper solution for that. Forget about PagerTabStrip ;)
Apparently, Google released a sample app that features the exact source code they used in Google Play app. It's super easy to use. Download the sample app and copy two files from there: SlidingTabLayout.java and SlidingTabStrip.java.
Then in your layout XML, add the following above the ViewPager (change com.example to your package name):
<com.example.SlidingTabLayout
android:id="#+id/sliding_tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_marginTop="5dp"
android:layout_weight="1.5"
android:layout_width="match_parent"
android:background="#android:color/white"
android:layout_height="0dp"/>
And add the following to the activity after you initialize the ViewPager:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(new GridPagerAdapter(getSupportFragmentManager()));
// Initialize the SlidingTabLayout. Note that the order is important. First init ViewPager and Adapter and only then init SlidingTabLayout
mSlidingTabLayout = (SlidingTabLayout) findViewById(R.id.sliding_tabs);
mSlidingTabLayout.setViewPager(mViewPager);
}
SlidingTabLayout's API allows you to easily do basic styling such as, changing the tab color, the divider color, the indicator color, etc.
One more important thing, the SlidingTabLayout that Google provided works well when you have many tabs. But, if you have, for example, three tabs, you'll see that all the tabs are aligned to the left and there's a big gap between the last tab and the width of the view.
To overcome this, modify SlidingTabLayout.java after line 173 just below TextView textView = new TextView(context); add the following line:
textView.setLayoutParams(new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f));
Now the space among the tabs will be divided correctly:
Have a look at this. This guy explains very clearly how to use Sliding Tab Layout
Understanding Sliding Tab Layout
For any customization
Change the code in the class SlidingTabLayout.java