The fragments are a part of the bottom navigation bar, so far so good on the navigation part. But when i try to pass data from fragment1 to fragment2 the app is crashing. Also I am using the example given by google at this following link
When I use the following code android studio gives me deprecated warning!
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
So I am using CONTEXT instead of ACTIVITY below is my code that crashes :
MainActivity
import android.support.annotation.NonNull;
import android.support.design.widget.BottomNavigationView;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import com.br.tron.bottombar.RadioFragment;
import com.br.tron.bottombar.StreamFragment;
import com.br.tron.bottombar.InfoFragment;
public class MainActivity extends AppCompatActivity implements RadioFragment.OnNameSetListener {
BottomNavigationView bottomNavigationView;
private Fragment fragment;
private FragmentManager fragmentManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fragmentManager = getSupportFragmentManager();
fragment = new RadioFragment();
final FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.add(R.id.main_container, fragment).commit();
bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottomNavigationBar);
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener(){
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.nav_button_one:
fragment = new RadioFragment();
break;
case R.id.nav_button_two:
fragment = new StreamFragment();
break;
case R.id.nav_button_three:
fragment = new InfoFragment();
break;
}
final FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.main_container, fragment).commit();
return true;
}
});
}
public void performStreamClick(){
View view = bottomNavigationView.findViewById(R.id.nav_button_two);
view.performClick();
}
#Override
public void setUrl(String url) {
StreamFragment frag=(StreamFragment) getSupportFragmentManager().findFragmentByTag("frag");
frag.getUrl(url);
}
}
RadioFragment(Fragment1 where the data exist)
import android.app.Activity;
import android.content.Context;
import android.support.v4.app.Fragment;
import android.os.Bundle;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
public class RadioFragment extends Fragment implements Button.OnClickListener {
Button buttonman;
View rootView;
String url;
Activity a;
OnNameSetListener onNameSetListener;
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof Activity) {
a = (Activity) context;
try{
onNameSetListener=(OnNameSetListener) context;
}
catch (Exception e){}
}
}
public RadioFragment(){
};
#Override
public View onCreateView(LayoutInflater inflater,ViewGroup container, Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_player, container, false);
buttonman = (Button)rootView.findViewById(R.id.buttonman);
buttonman.setOnClickListener(this);
return rootView;
}
#Override
public void onClick(View v) {
/*Fragment fragment = new StreamFragment();
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.main_container, fragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();*/
url="www.idontknow.com";
onNameSetListener.setUrl(url);
((MainActivity)a).performStreamClick();
}
public interface OnNameSetListener
{
public void setUrl(String url);
}
}
StreamFragment(fragment2 where I want to send the data -from fragment1)
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class StreamFragment extends Fragment {
TextView textViewStream;
public StreamFragment(){};
#Override
public View onCreateView(final LayoutInflater inflater,final ViewGroup container,final Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_stream, container, false);
}
public void getUrl(String url)
{
textViewStream.setText(url);
}
}
LOGCAT
01-06 20:10:23.023 3744-3744/com.br.tron.bottombar E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.br.tron.bottombar, PID: 3744
java.lang.NullPointerException: Attempt to invoke virtual method 'void com.br.tron.bottombar.StreamFragment.getUrl(java.lang.String)' on a null object reference
at com.br.tron.bottombar.MainActivity.setUrl(MainActivity.java:64)
at com.br.tron.bottombar.RadioFragment.onClick(RadioFragment.java:61)
at android.view.View.performClick(View.java:5201)
at android.view.View$PerformClick.run(View.java:21209)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5525)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:730)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:620)
01-06 20:10:24.663 3744-3744/? I/Process: Sending signal. PID: 3744 SIG: 9
You never register the frag tag you later search for.
In your MainActivity modify:
final FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.add(R.id.main_container, fragment, "frag").commit();
...
final FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.main_container, fragment, "frag").commit();
...
In addition to that within your StreamFragment class you never set the TextViewStream variable after you inflate your layout.
#Override
public View onCreateView(final LayoutInflater inflater,final ViewGroup container,final Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_stream, container, false);
textViewStream = (TextView) view.findViewById(R.id.ID_GOES_HERE);
return view;
}
Edit: Further Errors
You're getting a ClassCastException because you're not checking to see whether the fragment you get in MainActivity.setUrl is a StreamFragment. And it makes sense that it's failing because you register the frag tag for all three types of custom Fragments. Here's a further solution:
fragment = new RadioFragment();
final FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.add(R.id.main_container, fragment, "radio_fragment").commit();
...
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
String tag = "";
case R.id.nav_button_one:
fragment = new RadioFragment();
tag = "radio_fragment";
break;
case R.id.nav_button_two:
fragment = new StreamFragment();
tag = "stream_fragment";
break;
case R.id.nav_button_three:
fragment = new InfoFragment();
tag = "info_fragment";
break;
}
final FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.main_container, fragment, tag).commit();
return true;
}
Make the appropriate change in the Tag string in setUrl:
StreamFragment frag=(StreamFragment) getSupportFragmentManager().findFragmentByTag("stream_fragment");
if (frag != null) frag.getUrl(url);
Both of the current answers key in on your crash problem. I expect that will take care of your primary question. #asadmshah seems to have taken care of your next problem. Make sure to up vote his answer & accept it.
Regarding the warning from using Google's example code, here is what AS will currently give you if you tell it to create a new fragment.
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener) {
mListener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
The point of this is to make sure that the Activity you're attaching to implements the interface you've defined in your fragment.
And on the subject of the interface, all fragment to activity comms should be done through it. It would be better to find a way to call performStreamClick() through the interface as well. (I just realized I made this same mistake on an app I started several months ago but am just finishing up now :).
Lastly, just an FYI, you can get the attached activity of a fragment with getActivity().
EDIT:
1st, it might help to see your current code. I'm going to assume you have what Asad has put in his answer, more or less. Also, what is the expected behavior? I believe that you make a selection in RadioFragment then replace it with StreamFragment. That is, both fragments take the whole nav bar, they don't exist at the same time. If this is wrong, let me know.
If you're still calling setUrl() and performStreamClick() in the same order as before, you're getting null because you've never created a fragment by that name. You can't find a fragment you haven't already created.
Without actually setting this up to test it myself, I believe what you need to do would be to save the url in a global variable in setUrl(), then in your click listener, pass that to the StreamFragment when you create it.
See the "Deliver a Message to a Fragment" section of the link you've sited for doing that
Bundle args = new Bundle();
args.putInt(ArticleFragment.ARG_POSITION, position);
newFragment.setArguments(args);
(obviously, handling your url string instead of the int in the example)
Also, it's not a bad idea to handle null when searching for a fragment that should exist already
StreamFragment frag=(StreamFragment) getSupportFragmentManager().findFragmentByTag("stream_frag");
if(frag == null) {
final FragmentTransaction transaction = fragmentManager.beginTransaction();
StreamFragment fragment = new StreamFragment();
transaction.replace(R.id.main_container, fragment, "stream_frag").commit();
}
You should initialize your textViewStream in the StreamFragment fragment.
Related
Hello im new to the Android scene,
I have a problem with chaning Fragment using the FragmentTransaction, what is wrong with the code?
As far as I know the commit is the problem because its getting called twice but it does not seem like this is possbile, and I cant see this behavouir in the debugger.
package com.example.foodgent.Logic;
import android.annotation.SuppressLint;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;
import androidx.viewpager.widget.ViewPager;
import com.example.foodgent.UserInterface.Fragment2;
import com.example.foodgent.UserInterface.Fragment3;
import com.example.foodgent.UserInterface.MainActivity;
import com.example.fragment.R;
public class FragmentChanger<mainActivity> extends AppCompatActivity {
private Button btnNavFrag1;
private Button btnNavFrag2;
private Button btnNavFrag3;
private MainActivity mainActivity;
private int currentPage;
public FragmentChanger(Button btnNavFrag1, Button btnNavFrag2, Button btnNavFrag3, MainActivity activity) {
this.btnNavFrag1 = btnNavFrag1;
this.btnNavFrag2 = btnNavFrag2;
this.btnNavFrag3 = btnNavFrag3;
this.mainActivity= mainActivity;
currentPage=0;
}
#SuppressLint("RestrictedApi")
public void change(int number, ViewPager viewPager){
final FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
switch (number) {
case 0:
if (currentPage != 0) {
btnNavFrag1.setBackgroundResource(R.drawable.fragment_active_button);
btnNavFrag2.setBackgroundResource(R.drawable.fragment_buttons);
btnNavFrag3.setBackgroundResource(R.drawable.fragment_buttons);
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack if needed
transaction.replace(R.id.fragment, new Fragment());
transaction.addToBackStack(null);
transaction.commit();
}
break;
case 1:
if (currentPage != 1) {
btnNavFrag1.setBackgroundResource(R.drawable.fragment_buttons);
btnNavFrag2.setBackgroundResource(R.drawable.fragment_active_button);
btnNavFrag3.setBackgroundResource(R.drawable.fragment_buttons);
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack if needed
transaction.replace(R.id.fragment, new Fragment2());
transaction.addToBackStack(null);
transaction.commit();
}
break;
case 2:
if (currentPage != 2) {
btnNavFrag1.setBackgroundResource(R.drawable.fragment_buttons);
btnNavFrag2.setBackgroundResource(R.drawable.fragment_buttons);
btnNavFrag3.setBackgroundResource(R.drawable.fragment_active_button);
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack if needed
transaction.replace(R.id.fragment, new Fragment3());
transaction.addToBackStack(null);
transaction.commit();
}
break;
}
currentPage=number;
//set the new Page
viewPager.setCurrentItem(number);
}
public int getCurrentPage(){
return currentPage;
}
}
The Error:
AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.fragment, PID: 24603
java.lang.IllegalStateException: Activity has been destroyed
at androidx.fragment.app.FragmentManagerImpl.enqueueAction(FragmentManagerImpl.java:1566)
at androidx.fragment.app.BackStackRecord.commitInternal(BackStackRecord.java:317)
at androidx.fragment.app.BackStackRecord.commit(BackStackRecord.java:282)
at com.example.foodgent.Logic.FragmentChanger.change(FragmentChanger.java:82)
at com.example.foodgent.UserInterface.MainActivity.setViewPager(MainActivity.java:111)
at com.example.foodgent.UserInterface.MainActivity$6.onClick(MainActivity.java:366)
at android.view.View.performClick(View.java:6669)
at android.view.View.performClickInternal(View.java:6638)
at android.view.View.access$3100(View.java:789)
at android.view.View$PerformClick.run(View.java:26145)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6898)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
W/OPDiagnose: getService:OPDiagnoseService NULL
D/OSTracker: OS Event: crash
I/Process: Sending signal. PID: 24603 SIG: 9
Process 24603 terminated.
The Fragment Class :
package com.example.foodgent.UserInterface;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.example.fragment.R;
public class Fragment1 extends Fragment {
private static final String TAG = "Fragment1";
private Button btnNavFrag1;
private Button btnNavFrag2;
private Button btnNavFrag3;
private Button btnNavSecondActivity;
Activity mainActivity;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment1_layout, container, false);
return view;
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (activity != null) {
try {
mainActivity = activity;
}
catch (ClassCastException e) {
Log.v(TAG, "Exception: " + e.getLocalizedMessage());
}
}
}
}
Are you holding a reference to the activity in that Fragments? try saving/updating the activity in Fragment's onAttach:
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (activity != null) {
try {
mainActivity = activity;
}
catch (ClassCastException e) {
Log.v(TAG, "Exception: " + e.getLocalizedMessage());
}
}
}
Android saves, destroys and recreates your Activity and Fragments on device rotation, it could be reusing your fragments that hold a reference to the destroyed activity.
Saving the reference to the new activity in onAttach will update that reference.
If the error is occurring at the time of transaction.commit() ? then you can enclose the call inside by checking whether the activity is not finishing.
if (!this.isFinishing()){
transaction.commit();
}
I removed the Fragment Changer Class and changed the Fragment inside the Mainactivity
The problem was the transaction.commit(), due to the fact that I cant call this more than once in the FragmentChanger, the error ocurred after pressing a Tab button.
I am creating a simple application which will have a bottom navigation view.
This will take the user between various categories. This has been setup with Fragments and a case system
I then want buttons on these category pages which take you to other pages with the navigation at bottom of each of the pages.
When I put my button code I used on the previous version of this application it fails saying
error: cannot find symbol method findViewById(int)
After some reading I can see this doesnt work with fragments but I cant find anything clear or easy to understand as to how to make these work.
In short, i would like the nav panel to stay constant on any page i load not just the category pages i have listed in the panel.
Any of the letter pages and other links from buttons need to open in the panel above the nav bar but i have no clue how to implement this.
Any help would be ... helpful. I have spent about 12 hours trying various things with no avail.
Code to handle navigation fragments:
package breed.sheep.british.com.britishsheepbreeds;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.BottomNavigationView;
import android.support.design.widget.CoordinatorLayout;
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 breed.sheep.british.com.britishsheepbreeds.fragment.BreedsFragment;
import breed.sheep.british.com.britishsheepbreeds.fragment.ColoursFragment;
import breed.sheep.british.com.britishsheepbreeds.fragment.DefaultFragment;
import breed.sheep.british.com.britishsheepbreeds.fragment.TerminologyFragment;
import breed.sheep.british.com.britishsheepbreeds.fragment.TypeFragment;
import breed.sheep.british.com.britishsheepbreeds.helper.BottomNavigationBehavior;
public class MainActivity extends AppCompatActivity {
private ActionBar toolbar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toolbar = getSupportActionBar();
BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation);
BottomNavigationBehavior.disableShiftMode(navigation);
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
// attaching bottom sheet behaviour - hide / show on scroll
CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) navigation.getLayoutParams();
layoutParams.setBehavior(new BottomNavigationBehavior());
// load the default fragment
toolbar.setTitle("British Sheep Breeds");
loadFragment(new DefaultFragment());
}
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Fragment fragment;
switch (item.getItemId()) {
case R.id.home:
toolbar.setTitle("British Sheep Breeds");
fragment = new DefaultFragment();
loadFragment(fragment);
return true;
case R.id.breeds:
toolbar.setTitle("Breeds A-Z");
fragment = new BreedsFragment();
loadFragment(fragment);
return true;
case R.id.colour:
toolbar.setTitle("Colours");
fragment = new ColoursFragment();
loadFragment(fragment);
return true;
case R.id.type:
toolbar.setTitle("Type ");
fragment = new TypeFragment();
loadFragment(fragment);
return true;
case R.id.term:
toolbar.setTitle("Terminology ");
fragment = new TerminologyFragment();
loadFragment(fragment);
return true;
}
return false;
}
};
/**
* loading fragment into FrameLayout
*
* #param fragment
*/
private void loadFragment(Fragment fragment) {
// load fragment
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.frame_container, fragment);
transaction.addToBackStack(null);
transaction.commit();
} }
Fragment page
package breed.sheep.british.com.britishsheepbreeds.fragment;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import breed.sheep.british.com.britishsheepbreeds.R;
public class DefaultFragment extends Fragment {
public DefaultFragment() {
// Required empty public constructor
}
public static DefaultFragment newInstance(String param1, String param2) {
DefaultFragment fragment = new DefaultFragment();
Bundle args = new Bundle();
fragment.setArguments(args);
return fragment;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_main, container, false);
}
}
previous button code
Button balwen = (Button) findViewById(R.id.balwen);
balwen.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
Intent myIntent = new Intent(view.getContext(), balwen.class);
startActivityForResult(myIntent, 0);
}
});
Button balwenLeft, balwenRight;
FragmentManager fragmentManager;
Fragment fragment;
public Timeline() {
// Required empty public constructor
}
public static Timeline newInstance(String param1, String param2) {
Timeline fragment = new Timeline();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
fragmentManager = this.getActivity().getSupportFragmentManager();
}
#Override
public View onCreateView(LayoutInflater inflater, final ViewGroup container,
Bundle savedInstanceState) {
final LinearLayout l = (LinearLayout) inflater.inflate(R.layout.fragment_colours, container, false);
fragment = new FirstPageOnBreedFragment();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.channelactivitycontainer, fragment).commit();
balwenLeft = (Button) l.findViewById(R.id.btnLeft);
balwenLeft.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
fragment = new AnotherFragmentWithInBreedFrag();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.frame_container, fragment).commit();
}
});
balwenRight = (Button) l.findViewById(R.id.btnRight);
balwenRight.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
fragment = new AnotherFragmentWithInBreedFrag();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.frame_container, fragment).commit();
}
});
return l;
So this looks like what you might want to do, what's actually happening is that from each fragment as a category, you will create the custom navigations buttons on the layouts and when calling it from the fragment,
Don't forget that you will need to inflate the layouts of the fragment like i did, the parent layout of the fragment layout is LinearLayout in my case, so if you're using Relating layout or Coor... just change them respectively, and back to the navigations, what's happening is that I'm hard coding it but you can replace it with your switch case function that's actually simpler, so if you're on BreedsFragment, You will have two Buttons that will help you navigate between the pages in BreedFrag... In general we're just replacing whatever is present in frame_container just like the bottom nav does, same thing happens when you click the buttons but in this case you'r still on the same page breedFrag, and note that i'm assuming the those pages are also fragements. I hope you understand, else if you can share your code on gitHub I can help contribute to it.
I want to go to Fragment when Click button event. but I have issues about it.
Here's my code.
#OnClick(R.id.Main_Bottom_Bar_Summary)
public void onBottomBarClicked()
{
loadFragment(new AddItemFragment());
}
private void loadFragment(android.support.v4.app.Fragment fragment) {
FragmentManager fm = getFragmentManager();
FragmentTransaction fragmentTransaction = fm.beginTransaction();
fragmentTransaction.replace(R.id.frameLayout, fragment);
fragmentTransaction.commit(); // save the changes
}
and top of the acitivity. import files.
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
And My Fragment:
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.and.dmt.R;
public class AddItemFragment extends Fragment
{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_additemdialog,container, false);}
}
And I can see this issue.
What's this issue? How can I solve it?
If I change loadFragment parameter "Fragment" to "android.support.v4.app.Fragment"
then This issue appear.
loadFragment() takes android.app.Fragment as argument whereas your fragment extends android.support.v4.app.Fragment.
Change the argument for loadFragment() to take v4 Fragment and use getSupportFragmentManager().
if you use support fragment, try this code :
private void loadFragment(android.support.v4.app.Fragment fragment) {
android.support.v4.app.FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.frameLayout, fragment);
fragmentTransaction.commit(); // save the changes
}
Add below code to calling Activity.
addFragment(R.id.fragment_container, NewFragment.newInstance());
and add below method in the fragment class which will return new Instance of that very fragment.
public static NewFragment newInstance() {
return new NewFragment();
}
I am trying to move to another fragment by clicking on a button. the current fragment named as FirstFragment and the target fragment named as AttendanceFragment. When I run the app it just crashed.
This is the code of the current fragment
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
/**
* A simple {#link Fragment} subclass.
*/
public class FirstFragment extends Fragment {
public FirstFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_first, container, false);
Button btn1 = (Button) v.findViewById(R.id.btn1);
btn1.setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View v) {
AttendanceFragment fragment = new AttendanceFragment();
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.register_attendance, fragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
}
);
return v;
}
}
Make an interface.
public interface FragmentChangeListener{
public void replaceFragment(Fragment fragment);
}
Implements your parent activity with this interface.
public class MainActivity extends Activity implements FragmentChangeListener {
#Override
public void replaceFragment(Fragment fragment) {
FragmentManager fragmentManager = getSupportFragmentManager();;
FragmentTransaction fragmentTransaction =
fragmentManager.beginTransaction();
fragmentTransaction.replace(mContainerId, fragment);
fragmentTransaction.addToBackStack(fragment.toString());
fragmentTransaction.commit();
}
}
Call this method from Fragments like this.
//In your fragment - call this method from onClickListener
public void showOtherFragment(){
Fragment fragment=new NewFragmentName();
FragmentChangeListener listener = (FragmentChangeListener)getActivity();
listener.replaceFragment(fragment);
}
Please read Communicating with other Fragments
There you can find elegant version of solution posted by #Sanjog Shrestha. You should register you callback in onAttach(Activity activity) and unregister it in onDetach(). You better be sure that Activity implements interface defined by yourself.
Here is the code of MainActivity which I have written.
I am loading the list of fragment in the first screen. When the user taps on any of the list items, the planet name will be shown to the user in the detail fragment which I have defined in a separate class.
I am adding the transaction of "Fragment Planet List" -> "Fragment Planet Detail" to back stack. So what I expect is when I press the back button from the fragment of planet detail, it should load planet list in the phone. But it is not happening this way.
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.FrameLayout;
import com.meditab.fragments.fragment.FragmentPlanetDetail;
import com.meditab.fragments.fragment.FragmentPlanetList;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends ActionBarActivity implements Callback {
private FrameLayout frmPlanetList;
private FrameLayout frmPlanetDetail;
private boolean isPhone;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
frmPlanetList = (FrameLayout) findViewById(R.id.frmPlanetList);
frmPlanetDetail = (FrameLayout) findViewById(R.id.frmPlanetDetail);
if (null != frmPlanetDetail) {
isPhone = false;
} else {
isPhone = true;
}
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.frmPlanetList, new FragmentPlanetList());
fragmentTransaction.commit();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public void onListItemClicked(int intPosition) {
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
List<String> lstPlanetArray = getPlanetArray();
String strPlanetName = lstPlanetArray.get(intPosition);
FragmentPlanetDetail fragmentPlanetDetail = FragmentPlanetDetail.newInstance(strPlanetName);
if (isPhone) {
fragmentTransaction.addToBackStack(null);
fragmentTransaction.replace(R.id.frmPlanetList, fragmentPlanetDetail);
} else {
fragmentTransaction.replace(R.id.frmPlanetDetail, fragmentPlanetDetail);
}
fragmentTransaction.commit();
}
private List<String> getPlanetArray() {
List<String> lstPlanets = new ArrayList<>(10);
lstPlanets.add("Mercury");
lstPlanets.add("Venus");
lstPlanets.add("Earth");
lstPlanets.add("Mars");
lstPlanets.add("Jupiter");
lstPlanets.add("Saturn");
lstPlanets.add("Uranus");
lstPlanets.add("Neptune");
lstPlanets.add("Saturn");
return lstPlanets;
}
}
However if I override the backPress method and pop the back stack programatically, it works just fine.
#Override
public void onBackPressed() {
if (getFragmentManager().getBackStackEntryCount() != 0) {
getFragmentManager().popBackStack();
} else {
super.onBackPressed();
}
}
Do I need to override the onBackPressed() method this way if I want to achieve this behavour?
It is not documented that you need to override this method in this link.Android Back Press Fragment Documentation
Fragment Planet Detail Class
import android.app.Fragment;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.meditab.fragments.R;
/**
* Created by BHARGAV on 25-Dec-14.
*/
public class FragmentPlanetDetail extends Fragment {
private String strPlanetName;
public FragmentPlanetDetail() {
}
public static FragmentPlanetDetail newInstance(String strPlanetName) {
FragmentPlanetDetail fragmentPlanetDetail = new FragmentPlanetDetail();
Bundle bundle = new Bundle();
bundle.putString("Planet_Name", strPlanetName);
fragmentPlanetDetail.setArguments(bundle);
return fragmentPlanetDetail;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle bundle = getArguments();
if (null != bundle) {
strPlanetName = bundle.getString("Planet_Name");
}
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.planet_name, container, false);
TextView txtPlanetName = (TextView) rootView.findViewById(R.id.txtPlanetName);
txtPlanetName.setText(strPlanetName);
return rootView;
}
}
Fragment Planet List Class:
import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import com.meditab.fragments.Callback;
import com.meditab.fragments.R;
import java.util.ArrayList;
import java.util.List;
/**
* Created by BHARGAV on 25-Dec-14.
*/
public class FragmentPlanetList extends Fragment {
private ListView listView;
private ArrayAdapter<String> stringArrayAdapter;
private Callback callback;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View rootView = inflater.inflate(R.layout.listview, container, false);
listView = (ListView) rootView.findViewById(R.id.listView);
stringArrayAdapter = new ArrayAdapter<>(getActivity(),
android.R.layout.simple_list_item_1, getPlanetArray());
listView.setAdapter(stringArrayAdapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
callback.onListItemClicked(position);
}
});
return rootView;
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
callback = (Callback) activity;
}
private List<String> getPlanetArray() {
List<String> lstPlanets = new ArrayList<>(5);
lstPlanets.add("Mercury");
lstPlanets.add("Venus");
lstPlanets.add("Earth");
lstPlanets.add("Mars");
lstPlanets.add("Jupiter");
lstPlanets.add("Saturn");
lstPlanets.add("Uranus");
lstPlanets.add("Neptune");
lstPlanets.add("Saturn");
return lstPlanets;
}
}
Callback Interface:
/**
* Created by BHARGAV on 26-Dec-14.
*/
public interface Callback {
public void onListItemClicked(int intPosition);
}
Thanks. Please let me know if any additional detail or code is needed.
Ok. So the reason why it is not working is the conflict of ActionBarActivty and Activity class differences.
And the differnce between getSupportFragmentManager() and getFragmentManager() methods of FragmentTransaction.
ActionBarActivity:
I was using ActionBarActivity which is android.support.v7.app.ActionBarActivity. This means I was using v7 compat library
FragmentManager:
Fragment Manager were not from the compat library. It was directly
import android.app.FragmentManager;
import android.app.FragmentTransaction;
as you can see in the MainActivity class.
Fragment:
android.app.Fragment; is the class which is imported in the separate fragment classes.
So once I changed from ActionBarActivity to Activity class, things were working fine.
The same holds true when I changed the FragmentManager,FragmentTransaction and Fragment classes to support library classes.
So after any of the modification, things started working normally.
Thanks.
Of course you don't need to override the onBackPressed() method. It's just a hack.
Your this condition is messing it up.
if (isPhone) {
fragmentTransaction.addToBackStack(null);
why don't you use this statement without condition once to see if it works. You can keep rest as it is. Just move this statement outside of the if condition.
Wierd, but I think
addToBackStack(null)
is the problem
Try replcing it with
addToBackStack("planetDetail");
You are using actionbar activity which extends fragment activity. Now, to have perfect behaviour, you need support fragments to work with actionbar activity.
Here you are using actionbar activity with normal fragments (not support v4 fragments) and that's what is causing the issue. So to have perfect behaviour, change your fragments to support fragments and it should work fine.
Let me know if that doesn't work.
Well I was having the same problem with you. What worked for me and I think its the solution for you too is to change the :
1.fragment with the fragment from the support library :
import android.support.v4.app.Fragment;
and
getFragmentManager() with the getSupportFragmentManager() :