So let me try to explain this: I have a Bottom navigation view with three buttons, each one when pressed will load a fragment. However, when pressing the back button, i have programmed the back stack to go back to the fragment it was previously. However, the colour of the navigation button does not change after the back button is changed. I know this has something to do with the state checked thing but i do not know how to implement it on my codes. Here are the codes
This is the menu page, it sets the bottomnavigation view only in which the main_frame is where the fragments are going to be:
public class menuPage extends AppCompatActivity {
BottomNavigationView mainNav;
FrameLayout mainFrame;
private MoviesFragment moviesFragment;
private HomeFragment homeFragment;
private ProfileFragment profileFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_menu_page);
mainFrame= (FrameLayout) findViewById(R.id.main_frame);
mainNav = (BottomNavigationView) findViewById(R.id.main_nav);
moviesFragment= new MoviesFragment ();
homeFragment = new HomeFragment();
profileFragment = new ProfileFragment ();
removeFragment(homeFragment);
mainNav.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.nav_movies :
setFragment(moviesFragment);
return true;
case R.id.nav_home :
setFragment(homeFragment);
return true;
case R.id.nav_profile:
setFragment(profileFragment);
return true;
default:
return false;
}
}
});
}
private void setFragment(Fragment fragment) {
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.main_frame, fragment);
fragmentTransaction.addToBackStack("detail");
fragmentTransaction.commit();
}
private void removeFragment(Fragment fragment){
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.main_frame, fragment);
fragmentTransaction.disallowAddToBackStack();
fragmentTransaction.commit();
}
}
Here is the codes for the Home Fragment, Movies Fragment and Profile Fragment respectively. You can ignore the codes written inside cos i know it correctly goes to another activity when launched and that has nothing to do with this issue
Home Fragment
public class HomeFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view= inflater.inflate(R.layout.fragment_agenda, container, false);
return view;
}
}
Movies Fragment
public class MoviesFragment extends Fragment {
ListView listofmovies;
ArrayList<String> genres;
ArrayAdapter<String> listview;
NowShowing nowShowing;
ComingSoon comingsoon;
#Override//inflate this
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.fragment_movies, container, false);
listofmovies = (ListView) view.findViewById(R.id.movielist);
genres = new ArrayList<String>();
listview = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_activated_1, genres);
listofmovies.setAdapter(listview);
//The types of options
genres.add("Now Showing");
genres.add("Coming Soon");
genres.add("July");
genres.add("June");
nowShowing = new NowShowing();
comingsoon = new ComingSoon();
listofmovies.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (position == 0) {
Intent nextPage = new Intent(view.getContext(), NowShowing.class);
startActivityForResult(nextPage,0);
}
if (position == 1){
Intent nextPage = new Intent(view.getContext(),ComingSoon.class);
startActivityForResult(nextPage,1);
}
}
});
return view;
}
}
Profile Fragment
public class ProfileFragment extends Fragment {
ListView management;
ArrayList<String> mis;
ArrayAdapter<String> Adapter;
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_profile,container, false );
management= (ListView) view.findViewById(R.id.management);
mis= new ArrayList<String>();
Adapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_activated_1, mis);
management.setAdapter(Adapter);
//add management settings
mis.add("Settings");
mis.add("Favourites");
mis.add("Log Out");
userprofilefragment userprofilefragment = new userprofilefragment();
FragmentManager manager = getFragmentManager();
manager.beginTransaction()
.replace(R.id.profilelayout, userprofilefragment, userprofilefragment.getTag())
.commit();
return view;
}
Help is much appreciated. The context is that I'm just making a simple movie app for school assignment and i am still kinda beginner at Java..
Thanks
Related
I have an Activity with a ViewPager, each page of the ViewPager has a Fragment.
Inside my Screen3Fragment I have a LinearLayout (lly_fragments) where I am showing some other fragments. I start by showing the fragment Screen3_1
public class Screen3Fragment extends Fragment {
private FragmentManager manager;
private FragmentTransaction transaction;
public static Screen3Fragment newInstance() {
final Screen3Fragment mf = new Screen3Fragment();
return mf;
}
public Screen3Fragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_screen3, container, false);
Screen3_1Fragment frag31 = new Screen3_1Fragment();
manager = getChildFragmentManager();
transaction = manager.beginTransaction();
transaction.add(R.id.lly_fragments,frag31,"frag31");
transaction.addToBackStack("frag31");
transaction.commit();
return v;
}
}
This works fine without problems. Problem comes when, from within frag31 (which is inside Screen3Fragment), I want to call fragt32, for that I do the following.
public class Screen3_1Fragment extends Fragment {
private ImageButton imgbt_timer;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_screen3_1,container,false);
imgbt_timer = (ImageButton) v.findViewById(R.id.bT_scr31_timer);
imgbt_timer.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getChildFragmentManager().beginTransaction()
.replace(R.id.lly_fragments, new Screen3_2Fragment(), "frag32")
.commit();
}
});
return v;
}
}
As I read in other answers, the line transaction.replaceshould do the trick and replace the existing frag31 by the given frag32 inside the same given container lly_fragments.
However, I get java.lang.IllegalArgumentException: No view found for id..... I am not sure why.
getFragmentManager() will return always attributes from parent, in most cases the activities. getChildFragmentManager() wil return parent attributes (in your case, Screen3Fragment attributes). It should be used when you add a fragment inside a fragment.
In your case, Screen3Fragment should be added using getFragmentManager() and Screen3_1Fragment should be added using getChildFragmentManager() because Screen3_1Fragment is Screen3Fragment child. Screen3Fragment is the parent.
I recomand you to use always getFragmentManager() with add method, not replace because your parent will be the same.
getChildFragmentManager() can be used when you add a ViewPager inside a fragment.
You can use the callback, as following :
that worked for me, I hope my answers helps you
1) create a interface
public interface ChangeFragmentListener {
void onChangeFragmentLicked(int fragment);
}
2) implement the interface and transaction methods in your activity:
public class MainActivity extends AppCompatActivity implements ChangeFragmentListener {
FragmentTransaction fragmentTransaction;
Fragment1 fragment1;
Fragment2 fragment2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
fragment1 = new Fragment1();
fragment1.setChangeFragmentListener(this);
fragment2 = new Fragment2();
fragment2.setChangeFragmentListener(this);
initListeners();
}
void changeToFrag1() {
fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.activity_main_fragment_container,fragment1, "");
fragmentTransaction.commit();
}
void changeToFrag2() {
fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.activity_main_fragment_container, fragment2, "");
fragmentTransaction.commit();
}
#Override
public void onChangeFragmentLicked(int fragment) {
switch (fragment){
case 1:
changeToFrag1();
break;
case 2:
changeToFrag2();
break;
}
}
3) Create object from the interface to handle the callback:
public class Fragment1 extends Fragment {
private ChangeFragmentListener changeFragmentListener;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_fragment1, container, false);
view.findViewById(R.id.fragment1_textView).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
changeFragmentListener.onChangeFragmentLicked(2);
}
});
return view;
}
public Fragment1 setChangeFragmentListener(ChangeFragmentListener changeFragmentListener) {
this.changeFragmentListener = changeFragmentListener;
return this;
}
}
I created a tabbed app where the main acitvity contains two layouts. The top layout contains the actual tabbar (which is a fragment). Underneth the content layout contains another fragments depending on which tabbar button the user has clicked. In order to stay compatible with older android versions I use android.support.v4.app.Fragment.
So, in the tabbar menu I added two buttons and each button triggers another fragment in the content. This is what the code looks like:
public class TabbarMenuFragment extends Fragment implements View.OnClickListener {
LiveFragment liveFragment;
OddsFragment oddsFragment;
FragmentTransaction fragmentTransaction;
Button liveButton;
Button oddsButton;
public TabbarMenuFragment() {}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
liveFragment = new LiveFragment();
oddsFragment = new OddsFragment();
fragmentTransaction = getFragmentManager().beginTransaction().add(R.id.content_view, oddsFragment);
fragmentTransaction.commit();
View rootView = (View) inflater.inflate(R.layout.tabbar_menu, container, false);
liveButton = (Button) rootView.findViewById(R.id.liveButton);
liveButton.setOnClickListener(this);
oddsButton = (Button) rootView.findViewById(R.id.oddsButton);
oddsButton.setOnClickListener(this);
return rootView;
}
public void updateUIInTabs() {
liveFragment.updateUI();
}
#Override
public void onClick(View v) {
if(v == liveButton) {
fragmentTransaction = getFragmentManager().beginTransaction().replace(R.id.content_view, liveFragment);
fragmentTransaction.commit();
}
if(v == oddsButton) {
fragmentTransaction = getFragmentManager().beginTransaction().replace(R.id.content_view, oddsFragment);
fragmentTransaction.commit();
}
updateUIInTabs();
}
}
The important thing is that the liveFragment is a Fragment that contains a ListView:
public class LiveFragment extends Fragment {
LiveMainAdapter adapter;
ListView list;
LayoutInflater inflater;
View rootView;
public LiveFragment() {}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
this.inflater = inflater;
rootView = (View) inflater.inflate(R.layout.live_fragment, container, false);
adapter = new LiveMainAdapter(inflater);
list = (ListView) rootView.findViewById(R.id.live_fragment_ListView);
list.setAdapter(adapter);
list.setDivider(null);
list.setDividerHeight(0);
return rootView;
}
public void updateUI() {
if(list != null) {
list.invalidate();
adapter.createRowObjects();
adapter.notifyDataSetChanged();
}
}
}
The oddsFragment is so far just a dummy with a TextView:
public class OddsFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = (View) inflater.inflate(R.layout.odds_fragment, container, false);
return rootView;
}
}
Now there are two settings which are important:
First:
As described above: I start the app and the oddsFragment is initally shown. Now, when I click on the liveFragment button the liveFragment's ListView is not updated.
Clicking again on the button for oddsFragment the oddsFragment is properly shown. Clicking back to liveFragment I get nothing - no ListView is shown.
The second setting:
Is exactly the same as the first, but instead I set the liveFragment as the intial view that is in TabbarMenuFragment I replace:
fragmentTransaction = getFragmentManager().beginTransaction().add(R.id.content_view, oddsFragment);
with
fragmentTransaction = getFragmentManager().beginTransaction().add(R.id.content_view, liveFragment);
Then the ListView is properly shown and the data in the list view is updated and displayed correctly. However, if I click on oddsFragement and then again on liveFragement the ListView has disappeared.
I already tried different things for over two hours now and I'm pretty desperated because I have not clue what might be the reason for this wired behaviour.
Anybody got an idea what is going on here?
Give this a try...
In your LiveFragment add a Handler and a Runnable and update the UI from your Runnable.
Handler handler = new Handler();
Runnable updater = new Runnable()
{
public void run()
{
if(list != null) {
list.invalidate();
adapter.createRowObjects();
adapter.notifyDataSetChanged();
}
}
};
and then change your updateUI() method in LiveFragment
public void updateUI()
{
handler.post(updater);
}
I created an application with navigation drawer using this tutorial: http://www.androidhive.info/2013/11/android-sliding-menu-using-navigation-drawer/
And I have a button on the first fragment. So, I want to change fragment by clicking the button. This code can change fragment but not change navigation draver state (title, selected item):
public class FirstFragment extends Fragment {
public FirstFragment(){}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_today, container, false);
return rootView;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
ImageButton addButton = (ImageButton) getView().findViewById(R.id.add_button);
addButton.setOnClickListener(new addNewListener());
super.onActivityCreated(savedInstanceState);
}
private class addNewListener implements View.OnClickListener {
#Override
public void onClick(View v) {
final FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.frame_container, new NewFragment(), "NewFragmentTag");
ft.commit();
}
}
}
How can I fix it?
Thanks a lot!
I'm new to Android programming.
I have a list of items, when the user clicks on an item it takes them to a screen with that item details.
I want the user to have the ability to swipe right and left to view other items' details in the list, instead of going back to the list and choosing another item.
I read that I need to use ViewPager for the ability to swipe right and left, so I did that.
ViewPager works fine, but my problem when I click any item on the list I always get to the first page in the ViewPager.
I don't want that, what I want is if I click on item 4 on the list it takes me to page 4 in the view pager and still have the ability to swipe right and left to view the details of other items.
I know how to set the page in viewpager by using mPager.setCurrentItem(0)
But I don't know how to set it according to which item was selected from the list (i.e which item launched the activity).
Here is my code:
Main activity which contains the listview:
public class Main extends SherlockListActivity implements OnItemClickListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ListView mylist = (ListView) findViewById(android.R.id.list);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.simple_list_reda_1, R.id.list_content, getResources().getStringArray(R.array.items_list_array) );
mylist.setAdapter(adapter);
mylist.setOnItemClickListener(new OnItemClickListener()
{
public void onItemClick(AdapterView<?> arg0,View arg1, int position, long arg3)
{
Intent n = null;
switch (position){
case 0:
n = new Intent(getApplicationContext(), ViewPagerClass.class);
break;
case 1:
n = new Intent(getApplicationContext(), ViewPagerClass.class);
break;
case 2:
n = new Intent(getApplicationContext(), ViewPagerClass.class);
break;
case 3:
n = new Intent(getApplicationContext(), ViewPagerClass.class);
break;
}
if(null!=n)
startActivity(n);
}
});
}
}
ViewPagerClass
public class ViewPagerClass extends SherlockFragmentActivity{
static final int NUM_ITEMS = 4;
MyAdapter mAdapter;
ViewPager mPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.viewpager_layout);
mAdapter = new MyAdapter(getSupportFragmentManager());
mPager = (ViewPager) findViewById(R.id.viewpager);
mPager.setAdapter(mAdapter);
//mPager.setCurrentItem(2);
final ActionBar ab = getSupportActionBar();
ab.setDisplayHomeAsUpEnabled(true);
ab.setDisplayUseLogoEnabled(false);
ab.setDisplayShowHomeEnabled(false);
}
public static class MyAdapter extends FragmentPagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
#Override
public int getCount() {
return NUM_ITEMS;
}
#Override
public Fragment getItem(int position) {
switch(position){
case 0: return FirstPageFragment.newInstance();
case 1: return SecondPageFragment.newInstance();
case 2: return ThirdPageFragment.newInstance();
case 3: return FourthPageFragment.newInstance();
}
return null;
}
}
public static class FirstPageFragment extends Fragment {
public static FirstPageFragment newInstance() {
FirstPageFragment f = new FirstPageFragment();
return f;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View V = inflater.inflate(R.layout.fragment1, container, false);
return V;
}
}
public static class SecondPageFragment extends Fragment {
public static SecondPageFragment newInstance() {
SecondPageFragment f = new SecondPageFragment();
return f;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View V = inflater.inflate(R.layout.fragment2, container, false);
return V;
}
}
public static class ThirdPageFragment extends Fragment {
public static ThirdPageFragment newInstance() {
ThirdPageFragment f = new ThirdPageFragment();
return f;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View V = inflater.inflate(R.layout.fragment3, container, false);
return V;
}
}
public static class FourthPageFragment extends Fragment {
public static ThirdPageFragment newInstance() {
ThirdPageFragment f = new ThirdPageFragment();
return f;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View V = inflater.inflate(R.layout.fragment4, container, false);
return V;
}
}
Finally viewpager_layout.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
>
<android.support.v4.view.ViewPager
android:id="#+android:id/viewpager"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
So in short What I want is:
If I click on item 3 in the list I go to screen with the details on item 3, and if I swipe to the right I get to item 4, and if I swipe to the left I get to item 2.
In the onItemClickListener() you need to add an extra to the intent so you can get it when that activity launches.
n.putExtra("POSITION_KEY", position);
In the ViewPagerClass onCreate() using the
int position = getIntent().getIntExtra("POSITION_KEY", 0);
mPager.setCurrentItem(position);
That should do what you are wanting to do.
The Android 4.1 ActionBar provides a useful navigation mode as a list or tab. I am using a SpinnerAdapter to select from three fragments to be displayed in view android.R.id.content.
The onNavigationItemSelected() listener then inflates each fragment to the view and adds it to the back stack using FragmentTransaction.addToBackStack(null).
This all works as advertised, but I don't know how to update the ActionBar to reflect the current back stack. Using the ActionBar.setSelectedNavigationItem(position) works but also triggers a new OnNavigationListener() which also creates another FragmentTransaction (not the effect I want). The code is shown below for clarification. Any help with a solution is appreciated.
public class CalcActivity extends Activity {
private String[] mTag = {"calc", "timer", "usage"};
private ActionBar actionBar;
/** An array of strings to populate dropdown list */
String[] actions = new String[] {
"Calculator",
"Timer",
"Usage"
};
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.actionBar = getActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
// may not have room for Title in actionbar
actionBar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE);
actionBar.setListNavigationCallbacks(
// Specify a SpinnerAdapter to populate the dropdown list.
new ArrayAdapter<String>(
actionBar.getThemedContext(),
android.R.layout.simple_list_item_1,
android.R.id.text1,
actions),
// Provide a listener to be called when an item is selected.
new NavigationListener()
);
}
public class NavigationListener implements ActionBar.OnNavigationListener {
private Fragment mFragment;
private int firstTime = 0;
public boolean onNavigationItemSelected(int itemPos, long itemId) {
mFragment = getFragmentManager().findFragmentByTag(mTag[itemPos]);
if (mFragment == null) {
switch (itemPos) {
case 0:
mFragment = new CalcFragment();
break;
case 1:
mFragment = new TimerFragment();
break;
case 2:
mFragment = new UsageFragment();
break;
default:
return false;
}
mFragment.setRetainInstance(true);
}
FragmentTransaction ft = getFragmentManager().beginTransaction();
if (firstTime == 0) {
firstTime++;
ft.add(android.R.id.content, mFragment, mTag[itemPos]);
} else {
ft.replace(android.R.id.content, mFragment, mTag[itemPos]);
ft.addToBackStack(null);
}
ft.commit();
Toast.makeText(getBaseContext(), "You selected : " +
actions[itemPos], Toast.LENGTH_SHORT).show();
return true;
}
}
public static class CalcFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_calc, container, false);
return v;
}
}
public static class TimerFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_timer, container, false);
return v;
}
}
public static class UsageFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_usage, container, false);
return v;
}
}
You could do something like this:
Create a boolean to track when you are selecting a navigation item based on the back button:
private boolean mListIsNavigatingBack = false;
Override onBackPressed, in the override, check if the backstack has items, if so handle yourself, if not call the superclass:
public void onBackPressed() {
if(getFragmentManager().getBackStackEntryCount() > 0){
mListIsNavigatingBack = true;
//You need to get the previous index in the backstack through some means
//possibly by storing it in a stack
int previousNavigationItem = ???;
getActionBar().setSelectedNavigationItem(previousNavigationItem);
}
else{
super.onBackPressed();
}
}
Inside NavigationListener, handle the mListIsNavigatingBack state, manually pop the back stack and unset the state:
if(mListIsNavigatingBack){
if(fm.getBackStackEntryCount() > 0){
fm.popBackStack();
}
mListIsNavigatingBack = false;
}