In my Android application I have created a simple Navigation Drawer which calls fragments when an item is clicked. From one of these fragments, I want to call a FragmentActivity (which will make scrollable tabs from within one of my fragments). Is this possible? Can someone please help me. A similar example to what I'm trying to achieve is in Play Music. It has a Navigation Drawer and upon selecting 'My Library' it creates a Fragment with scrollable tabs whilst still having the NavDrawer accessible from that page.
Regards,
import android.app.ActionBar;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import cgg.gov.in.apps.eoffice.source.R;
public class TestTabsinsideFragment extends Fragment
{
View rootView;
public TestTabsinsideFragment ()
{
// Empty constructor required for fragment subclasses
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
getActivity().getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
// Apply the layout for the fragment
rootView = inflater.inflate(R.layout.approve_leaves, container, false);
getActivity().setTitle("New tabbed layout inside Fragment :-) ");
ActionBar.TabListener tabListener = new ActionBar.TabListener() {
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
// show the given tab
}
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
// hide the given tab
}
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
// probably ignore this event
}
};
// Add 3 tabs, specifying the tab's text and TabListener
for (int i1 = 0; i1 < 3; i1++) {
getActivity().getActionBar().addTab(
getActivity().getActionBar().newTab()
.setText("Tab " + (i1 + 1))
.setTabListener(tabListener));
}
return rootView;
}
Did this answer your question ??
Include this code in your Fragment and call it from onSelectItem() of Nav-Drawer
Related
I am developing an Android app that has a little bit confusing navigation structure which leads me to my problem. There are two ways to navigate through the app. First is the BottomNav and second is a TabMenu. I thought about working with fragments that replace each other, so I came up with the following structure:
1. BottomNav#1
- TabMenu#1
- Tabmenu#2
- TabMenu#3
- ...
2. BottomNav#2
- TabMenu#4
- Tabmenu#5
- TabMenu#6
- ...
... and so on.
The problem I am facing is that when I navigate from BottomNav#1 to BottomNav#2 and back again there is a blank screen that doesn't show the content of the actual fragment:
When I open the app
After clicking on BottomNav#2 and then back to BottomNav#1 the fragment seems to be blank
My guess is that I somehow have a problem with my fragmentTransaction.replace(); as it seems like the fragment doesn't get loaded again? I am kind of new to this and really tried to find an answer online but this is a bit specific why I guess I didn't find anything.
This is my MainActivity:
package com.example.XXXX;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.BottomNavigationView;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.view.MenuItem;
import android.widget.FrameLayout;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private FrameLayout mMainFrame;
private AusweisFragment ausweisFragment;
private SpendenFragment spendenFragment;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ausweisFragment = new AusweisFragment();
spendenFragment = new SpendenFragment();
BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation);
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
}
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener()
{
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item)
{
switch (item.getItemId())
{
case R.id.navigation_ausweis:
setFragment(ausweisFragment);
return true;
case R.id.navigation_spenden:
setFragment(spendenFragment);
return true;
}
return false;
}
};
private void setFragment(Fragment fragment)
{
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.main_frame, fragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
}
This should lead to e.g. the fragment called "ausweis" by clicking on on BottomNav#1 (switch case):
package com.example.XXXXXXX;
import android.app.Activity;
import android.os.Bundle;
import android.support.design.widget.TabItem;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class AusweisFragment extends Fragment {
private View rootView;
private AusweisPageAdapter ausweisPageAdapter;
private TabLayout tabLayout;
private ViewPager viewPager;
public AusweisFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
rootView= inflater.inflate(R.layout.fragment_ausweis, container, false);
tabLayout = rootView.findViewById(R.id.tablayoutAusweis);
viewPager = rootView.findViewById(R.id.viewPagerAusweis);
ausweisPageAdapter = new AusweisPageAdapter(getActivity().getSupportFragmentManager(), tabLayout.getTabCount());
viewPager.setAdapter(ausweisPageAdapter);
viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener()
{
#Override
public void onTabSelected(TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
return rootView;
}
}
After that I would like to call another fragment called "spenden" which is exactly the same as the fragment called "ausweis" but with different named tabs, so I think it is not bother you with more code.
edit: I missed about writing where the "content" I wrote about gets from. For a first try and proof that the fragments change, I hardcoded a phrase like "Ausweis" into the XML which is connected to my fragment java class.
Maybe one of you has an idea to that problem. I think it has something to do with my onCreateView in one of the fragments, but I have no close clue.
Hopefuly I didn't miss an important detail. I am very grateful for any kind of help. Thanks a lot in advance.
The answer to my problem was to use a child-parent relation between the different fragments. With the click on BottomNav#1 I am inflating an fragment which inflates a new fragment inside itself. That was the key problem. In my code I handled this as a second "normal" getFragmentManager();.
The answer is to use getChildFragmentManager(); for the nested fragment instead. Like this:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
rootView= inflater.inflate(R.layout.fragment_ausweis, container, false);
tabLayout1 = rootView.findViewById(R.id.tablayoutAusweis);
viewPager = rootView.findViewById(R.id.viewPagerAusweis);
ausweisPageAdapter = new AusweisPageAdapter(getChildFragmentManager(), tabLayout1.getTabCount());
viewPager.setAdapter(ausweisPageAdapter);
viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout1));
...
...
Hopefuly this will help some people when they face the same problem with nested fragments.
I have a Main Activity which uses an AHBottomNavigationView for a menu at the bottom of the screen. When a different menu item is clicked, it creates a new fragment corresponding to that menu item with logic like so (condensed switch statement for the simplicity of this question):
fragmentManager.beginTransaction().replace(R.id.content_id, New TheFragmentForTheTabClicked).commit();
Where content_id is the ID of the Main Activity's ConstraintLayout.
Within the fragment for my first navigation menu item, there are two more tabs (using TabLayout), which replace the screen space with another fragment. This is done with a FragmentPagerAdapter, which is set onto a ViewPager, so tapping each tab changes the sub fragment. So at this point, there is a fragment nested in a fragment nested in a class. Here is what it generally is:
Main Activity
|
+-- Fragment 1 (selected from AHBottomNavigationView)
| |
| +-- Sub-Fragment 1 (selected by clicking the first tab in Fragment 1)
| |
| +-- Sub-Fragment 2 (selected by clicking the second tab in Fragment 1)
|
+-- Fragment 2 (selected from AHBottomNavigationView)
|
+-- Fragment 3 (selected from AHBottomNavigationView)
|
+-- Fragment 4 (selected from AHBottomNavigationView)
So my question is this:
Is the way I am doing this correct, and if not, what would a better way be?
Also, I'm finding that When I tab to Fragment 1 the first time, the swiping and tapping between the two tabs works fine, however if I tap a different bottom navigation menu item (i.e. Fragment 3) and then go back, I get the following 2 issues:
The content in either of the subfragments is not shown
Swiping between the two tabs no longer works. Instead of one motion moving to the different tab, I have to pull across the screen entirely because the indicator gets "stuck" part way between two tabs.
If there is any more information that I can provide, please let me know and I will.
Fragment1.java:
package com.mypackage.mypackage;
import android.app.Activity;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
/**
* A simple {#link Fragment} subclass.
*/
public class Fragment1 extends Fragment {
private FragmentActivity mContext;
public Fragment1() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_1, container, false);
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
// Find the view pager that will allow the user to swipe between fragments
ViewPager viewPager = (ViewPager) getView().findViewById(R.id.viewpager);
// Create an adapter that knows which fragment should be shown on each page
// using getFragmentManager() will work too
Fragment1PagerAdapter adapter = new Fragment1PagerAdapter(mContext.getSupportFragmentManager(), mContext);
// Set the adapter onto the view pager
viewPager.setAdapter(adapter);
TabLayout tabLayout = (TabLayout) getView().findViewById(R.id.sliding_tabs);
tabLayout.setupWithViewPager(viewPager);
}
/**
* Override to set context. This context is used for getSupportFragmentManager in onCreateView
* #param activity
*/
#Override
public void onAttach(Activity activity) {
mContext=(FragmentActivity) activity;
super.onAttach(activity);
}
}
fragment_1.xml
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.design.widget.TabLayout
android:id="#+id/sliding_tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabMode="fixed"
app:tabBackground="#color/fragment1TabBackground"
app:tabIndicatorColor="#color/fragment1TabIndicatorColor"/>
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="1"/>
</LinearLayout>
</android.support.constraint.ConstraintLayout>
Fragment1PagerAdapter.java
package com.mypackage.mypackage;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.content.Context;
public class Fragment1PagerAdapter extends FragmentPagerAdapter {
private Context context;
public Fragment1PagerAdapter(FragmentManager fm, Context mContext){
super(fm);
context = mContext;
}
#Override
public Fragment getItem(int position){
if (position == 0){
return new SubFragment1();
}
else{
return new SubFragment2();
}
}
#Override
public int getCount() {return 2;}
#Override
public CharSequence getPageTitle(int position) {
switch(position){
case 0:
return context.getResources().getString(R.string.sub_fragment_1_page_title);
case 1:
return context.getResources().getString(R.string.sub_fragment_2_page_title);
default:
return null;
}
}
}
When nesting Fragments inside Fragment with ViewPager and swipe feature as FragmentManager which needs to be provided to Adapter recommended is to use: getChildFragmentManager() instead of getSupportFragmentManager() or getFragmentManager(). Because both are actually related to Activities instead of getChildFragmentManager(), as documentation says, is related to Fragment:
Return a private FragmentManager for placing and managing Fragments
inside of this Fragment.
I have had some trouble finding an example I understand for using ActionBarSherlock. I can implement the TabNavigation.java example that comes with the download. I'm not sure how to extend this example so that it triggers a new activity but still keeps the tabs displayed. Here's the example that I've implemented:
package com.actionbarsherlock.sample.demos;
import android.os.Bundle;
import android.support.v4.app.FragmentTransaction;
import android.widget.TextView;
import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.app.ActionBar.Tab;
import com.actionbarsherlock.app.SherlockActivity;
public class TabNavigation extends SherlockActivity implements ActionBar.TabListener {
private TextView mSelected;
#Override
public void onCreate(Bundle savedInstanceState) {
setTheme(SampleList.THEME); //Used for theme switching in samples
super.onCreate(savedInstanceState);
setContentView(R.layout.tab_navigation);
mSelected = (TextView)findViewById(R.id.text);
getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
for (int i = 1; i <= 3; i++) {
ActionBar.Tab tab = getSupportActionBar().newTab();
tab.setText("Tab " + i);
tab.setTabListener(this);
getSupportActionBar().addTab(tab);
}
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction transaction) {
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction transaction) {
mSelected.setText("Selected: " + tab.getText());
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction transaction) {
}
}
I believe I need to create a new "Listener" class and pass that to the .setTabListener() method. I want to stick with Activities (not Fragments) as I have a pretty complex implementation of ORMLite and I'm not sure how to use ORMLite with Fragments yet.
I want to add a new tab labeled "Profile" which triggers ProfileActivity.class.
Thanks in advance!
I guess that TabListener is supposed to work with Fragments and FragmentTransaction. You will have to move your code from ProfileActivity to a new ProfileFragment class and add it with the FragmentTransaction you receive by the listener.
I would suggest you to add ViewPagerIndicator library on your project, just like this answer. A ViewPager is just like tabs plus the swype movement to navigate between them.
It sounds like the reason I'm not finding the example I am looking for is because you just can't use the action bar persistently with Activities. Comments or a better answer would be much appreciated. :)
It is maybe quite a newbie question but anyway. Since Tabhost is depreciated I tried to switch to the action bar tabs but I have my problems using fragments. Is there a possibility to use activities within the action bar tabs anyway?
I would appreciate any help.
Thanks.
If you're set on using Activities over Fragments you could just use an intent to launch your activity from your ActionBar.TabListener
startActivity(new Intent(thisActivity(), thatActivity.class));
You should also check out this comment about using Fragments over Activities
Is there a possibility to use activities within the action bar tabs anyway?
Fortunately, no.
That does not mean you have to use fragments, though. Your TabListener can do whatever it wants to affect the change in your UI. A brute-force solution would be to call setContentView() again, to dump all your old widgets and lay down a brand-new (presumably different) set.
It's possible to use an Activity with the ActionBar. Beware this is not intended behaviour though, but that doesn't mean it doesn't work perfectly.
import android.annotation.SuppressLint;
import android.app.ActionBar;
import android.app.ActionBar.Tab;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
//#SuppressLint("NewApi")
public class ActionBarActivity extends Activity {
private String TAG = getClass().getName();
private Intent i = null;
private ActionBar actionBar;
private Tab one;
private Tab two;
private Tab three;
// create a tab listener that is called when the user changes tabs
ActionBar.TabListener tabListener = new ActionBar.TabListener() {
#Override
public void onTabSelected(Tab tab, android.app.FragmentTransaction ft) {
if (tab.getTag().equals("one")){
Log.d(TAG, "tab one selected");
i = new Intent(getApplicationContext(), One.class);
determineRun();
}
if (tab.getTag().equals("two")){
Log.d(TAG, "tab two selected");
i = new Intent(getApplicationContext(), Two.class);
determineRun();
}
if (tab.getTag().equals("three")){
Log.d(TAG, "tab three selected");
i = new Intent(getApplicationContext(), Three.class);
determineRun();
}
}
#Override
public void onTabUnselected(Tab tab, android.app.FragmentTransaction ft) {
// TODO Auto-generated method stub
}
#Override
public void onTabReselected(Tab tab, android.app.FragmentTransaction ft) {
// TODO Auto-generated method stub
}
};
// we only need to start the Activity if it's not actually already the current Activity!
void determineRun(){
if (!TAG.equals(i.getComponent().getClassName())){
startActivity(i);
}
return;
}//end method
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
actionBar = getActionBar();
actionBar.setDisplayShowTitleEnabled(true);
actionBar.setSubtitle(getResources().getString("subtitle"));
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
one = actionBar.newTab();
one.setText("Tab 1").setTag("one");
two = actionBar.newTab();
two.setText("Tab 2").setTag("two");
three = actionBar.newTab();
three.setText("Tab 3").setTag("three");
one.setTabListener(tabListener);
two.setTabListener(tabListener);
three.setTabListener(tabListener);
// You will have to set the selected Tab manually
// A good idea would be to create a subclass for each Tab based on this code
// Then, just create a new Activity which extends ActionBarActivity
actionBar.addTab(one, 0, false);
actionBar.addTab(two, 1, true); // selected Tab
actionBar.addTab(three, 2, false);
}//end method
#Override
public void onResume(){
super.onResume();
Log.d(TAG, "onResume()");
Log.d(TAG, ""+i.getComponent().getClassName());
// again, here you need to select the Tab manually
if (!TAG.equals(i.getComponent().getClassName())){
actionBar.selectTab(two); // selected Tab
}
}//end method
#Override
public void onPause(){
super.onPause();
Log.d(TAG, "onPause()");
}//end method
#Override
public void onBackPressed() {
super.onBackPressed();
overridePendingTransition(android.R.anim.slide_in_left, android.R.anim.slide_out_right);
}//end method
}//end class
You probably want to override the animation in your Activity so the change of tabs is seemless.
To do so, modify the onCreate() method of your Activity which extends ActionBarActivity
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
overridePendingTransition(0, 0);
}//end method
if my understanding is correct, you want to use action bar to swap activity instead of fragment. in this case, please continue to read.
from the official document you can see, the actionbar feature defines a set of ui, position. if you want to implement actionbar with activity, the most important thing to do is
1. to associate your tab(position) with your activity.
2. add tablistener callback(instantiate your new activity, stop the current activity) every time the tab is click
the best design is to have the tablistener implemented a seperate class, so that each of your activity could use this class.
Ok, so I'm trying to use Sherlock to display multiple tabs, each for one fragment.
I've got only 4classes : one for my main activity, two for my fragments, and one for the TabListener.
Everything should be ok (I've got quite the same program without Sherlcock, working on 4.0 devices), so I can't understand why i get that NullPointerException.
Here's part of the error
05-18 17:46:57.197: E/AndroidRuntime(9312): FATAL EXCEPTION: main
05-18 17:46:57.197: E/AndroidRuntime(9312): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.micky.testing/com.micky.testing.SherlockTestActivity}: java.lang.NullPointerException
...
05-18 17:46:57.197: E/AndroidRuntime(9312): Caused by: java.lang.NullPointerException
05-18 17:46:57.197: E/AndroidRuntime(9312): at com.micky.testing.MyTabListener.onTabSelected(MyTabListener.java:21)
...
05-18 17:46:57.197: E/AndroidRuntime(9312): at com.micky.testing.SherlockTestActivity.onCreate(SherlockTestActivity.java:39)
Here is one of my fragment :
HomeFragment
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.actionbarsherlock.app.SherlockFragment;
public class HomeFragment extends SherlockFragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.homefragment, container, false);
}
}
Here is my tabListener :
MyTabListener
public class MyTabListener implements TabListener {
public SherlockFragment fragment;
MyTabListener(SherlockFragment fr) {
Log.d("MYTAG", "Creating a fragmentListener w/ " + fr);
this.fragment = fr;
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
// TODO Auto-generated method stub
Log.d("TAG", "" + fragment);
ft.replace(R.id.fragment_container, fragment);
}
}
And my main activity :
SherlockTestActivity
public class SherlockTestActivity extends SherlockActivity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//We take the support actionbar
ActionBar ab = getSupportActionBar();
//We set to navigationmode with tabs
ab.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
//we create the tabs
ActionBar.Tab homeTab = ab.newTab().setText("Home");
ActionBar.Tab tagsTab = ab.newTab().setText("Tags");
//We create the fragments
SherlockFragment homeFragment = new HomeFragment();
SherlockFragment tagsFragment = new TagFragments();
//And we set the tabsListener;
homeTab.setTabListener(new MyTabListener(homeFragment));
tagsTab.setTabListener(new MyTabListener(tagsFragment));
Log.d("","" + homeTab);
ab.addTab(homeTab);
ab.addTab(tagsTab);
}
Ok, so the error seems to be thrown when I add the tab to my actionbar. And when I don't add the TabListener to the tab, there's no error.
The code ft.replace(R.id.fragment_container, fragment); (MyTabListener) seems to be the problem, but i can't manage to understand why. fragment isn't null (initialized when instanciating a new tabListener), and there's no reason the fragment_container is wrong.
So if anyone can manage to help me around here ! Thank you !
You should be extending SherlockFragmentActivity instead of SherlockActivity. Use a fragment activity when you are managing fragments.