having a condition to fragment adding - android

for the last 4 hours ive been trying to understand how to do it.
to be clear, i need to add a fragment under a certain condition and for my purpose the condition can be either:
a. if the parent's id matches to what i seek.
b. if the fragment's id matches to what i seek.
i tried to put the condition inside the fragment itself:
public class BlockFrag extends Fragment{
SharedPreferences sp;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
sp = getActivity().getSharedPreferences("myOptions", Context.MODE_PRIVATE);
int id = container.getId();//didn't work(i think the container is null for some reason
switch (id)
{
default: return inflater.inflate(R.layout.nothing, container, false);
case (R.id.redFrame): {
if (sp.getBoolean("redBlock", true)) {
return inflater.inflate(R.layout.block_frag, container, false);
}
}
case (R.id.greenFrame): {
if (sp.getBoolean("greenBlock", true)) {
return inflater.inflate(R.layout.block_frag, container, false);
}
i also tried to get the condition programatically but i cant understand how to prevent the fragment to automatically generate when i call the layout it's placed.
this is the layout it's placed for example
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/redFrame"
android:layout_weight="0.33333">
<fragment
android:name="com.example.dor.myapplication.BlockFrag"
android:id="#+id/blockRed"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:layout="#layout/block_frag"/>
this is the fragment's layout if it matters...
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true">
<ImageView
android:layout_width="118dp"
android:layout_height="match_parent"
android:id="#+id/blockFrag"
android:src="#drawable/block"
android:layout_weight="0.33333"
android:layout_above="#+id/linearLayout"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_gravity="left"
android:scaleType="fitXY"/>
thank you for helping, any kind of way to do this will help me.

I did something similar earlier, I had to decide based on a value from PickHeaderFragment which Fragment to show inside my root_frame_body, here's how I achieved it:
My Parent or Host Fragment:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:background="#000"
android:layout_height="wrap_content"
android:id="#+id/root_frame_header" />
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:background="#000"
android:layout_weight="1"
android:layout_height="match_parent"
android:id="#+id/root_frame_body" />
</LinearLayout>
Inside my Host Fragment:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View rootView = inflater.inflate(R.layout.fragment_pick, container, false);
headerFragment = PickHeaderFragment.newInstance("");
getChildFragmentManager().beginTransaction().replace(R.id.root_frame_header, headerFragment).commit();
}
My PickHeaderFragment has an OnPickHeaderFragmentInteractionListener Interface implemented which looks like this:
public interface OnPickHeaderFragmentInteractionListener {
public void onFragmentInteraction(int res);
}
I ensure my Host Fragment will implement this interface by adding it during onAttach (inside PickHeaderFragment) like so:
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
Fragment f = getParentFragment();
mListener = (OnPickHeaderFragmentInteractionListener) getParentFragment();
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnFragmentInteractionListener");
}
}
Now, when the enter Button inside my PickHeaderFragment is clicked I call this event to let my Host Fragment know to change the root_frame_body:
Inside my Host Fragment I have:
#Override
public void onFragmentInteraction(int res) {
if (res == R.id.btnEnter) {
getChildFragmentManager().beginTransaction().replace(R.id.root_frame_body, new PickByItemBodyFragment()).commit();;
}
}
I am now able to change the layout (or which Fragment is displayed) in my Host Fragment after an event occurs inside a child Fragment.
Note: In my example I have nested Fragments, but you could just as easily have several Fragments within an Activity.
In summary I would recommend you follow a similar pattern of having a host Activity or Fragment and ensuring you have a FrameLayout added to your view to allow you to inflate whatever Fragment you need.
An added benefit to doing it this way is that you can segregate your code into separate Fragments also.

Leigh thank you for your answer but i don't know if you didn't understand my question or i didn't understand your answer...
anyway, i solved my problem through programatically adding the fragment in r=the onCreate of the activity and inserting the adding to an "if"
BlockFrag rb = new BlockFrag();
FragmentManager fragmentManager = getFragmentManager ();//declaring Fragment Manager
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction ();// declaring Fragment Transaction
if(condition)
{
editor.putBoolean("redBlock", true);
fragmentTransaction.add(R.id.redFrame, rb);//adding the fragment
}
needless to say i didn't declare the fragment in my activity layout and didn't perform any changes in the fragment class or fragment xml layout.

Related

Android views in fragments overlapping

So I've read several tutorials and many of the posts on SO but I still can't get this to work. I'm trying to use dynamically added fragments but when loading a fragment TextViews and ImageViews in the fragment keep overlapping instead of updating their values, like so:
and fragments overlap each other when replaced.
I got a main activity (extending AppCompatActivity) split into 2 layouts:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/root_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/background_white"
android:orientation="vertical"
tools:context=".MainActivity">
<FrameLayout
android:id="#+id/upperFrameLayout"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/lowerLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
...
The upper FrameLayout contains my fragments. The fragments are replaced in this method in the MainActivity-class:
void setMode() {
// Show "find opponent"- or "fight"-fragment
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
if (!debugFight && (btCoordinator == null || !btCoordinator.isConnected())) {
fragmentTransaction.replace(R.id.upperFrameLayout, new FindOpponentFragment());
} else {
fragmentTransaction.replace(R.id.upperFrameLayout, new FightFragment());
}
fragmentTransaction.commitNow();
}
What am I missing?
EDIT: Added FightFragment onCreateView()-method:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
((TextView) inflater.inflate(R.layout.fragment_fight, container, false).findViewById(R.id.score_textView)).setText(getString(R.string.score, 0, 0));
((TextView) inflater.inflate(R.layout.fragment_fight, container, false).findViewById(R.id.opponent_textView)).setText(MainActivity.opponentName.toUpperCase());
final AnimatedVectorDrawableCompat animated = AnimatedVectorDrawableCompat.create(container.getContext(), R.drawable.loading_animated_vector);
animated.registerAnimationCallback(new Animatable2Compat.AnimationCallback() {
#Override
public void onAnimationEnd(Drawable drawable) {
animated.start();
}
});
((ImageView) inflater.inflate(R.layout.fragment_fight, container, false).findViewById(R.id.opponent_hand_imageview)).setImageDrawable(animated);
animated.start();
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_fight, container, false);
}
...and as I pasted this piece of code I just realised that those inflated views are all different objects. I replaced them with a single object which I modify and then let the method return and it works. I guess I have to read up on views and the inflate-method.
use fragmentTransaction. add() instead of replace()
Use this above the fragment commit
fragmentTransaction.addToBackStack(null);
I didn't understand how the inflate-method. I modified my fragments onCreateView-method as seen in the OP and now it works.

id attribute in fragments

I am working on a app and it runs just fine but there is an problem that it crashes when I dont write id in my fragment code but runs fine after using id attribute and the problem is that i have not used it's id anywhere in my app
Here is the xml file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
>
<fragment
class="com.hfad.workout.WorkoutList"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2"
android:id="#+id/listfrag"
/>
<FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:text="second text view"
android:layout_marginTop="20dp"
android:id="#+id/fragcont"
android:layout_weight="3"
/>
</LinearLayout>
workoutlist.jav
public class WorkoutList extends ListFragment {
MainActivity ma;
static interface WorkoutListListener{
void clickme(long at);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
ArrayAdapter<Workout> arrayAdapter = new ArrayAdapter<Workout>(
inflater.getContext(), android.R.layout.simple_list_item_1,
Workout.workout);
setListAdapter(arrayAdapter);
return super.onCreateView(inflater, container, savedInstanceState);
}
#Override
public void onAttach(Context ac){
super.onAttach(ac);
this.ma =(MainActivity) getActivity();
}
public void onListItemClick(ListView v, View vi, int position, long id){
ma.clickme(id);
}
}
Before an activity can talk to its fragment, the activity first needs
to get a reference to it. To get a reference to the fragment, you first
get a reference to the activity’s fragment manager using the
activity’s getFragmentManager() method. You then use its
findFragmentById() method to get a reference to the fragment:
findFragmentById() is a
bit like findViewById()
except you use it to get a
reference to a fragment
getFragmentManager().findFragmentById(R.id.fragment_id)
It's because if you do not specify fragment id, then FragmentManager cannot restore it after recreating.
You have to specify fragment id or tag.

java.lang.IllegalArgumentException: No view found for id 0x1020002 (android:id/content) for fragment

I am trying to move from one fragment to another.. It shows following error during fragment transaction-
java.lang.IllegalArgumentException: No view found for id 0x1020002 (android:id/content) for fragment PhotosFragment2{41a57218 #3 id=0x1020002}
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:930)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1115)
at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:682)
at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1478)
at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:446)
at android.os.Handler.handleCallback(Handler.java:615)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:153)
at android.app.ActivityThread.main(ActivityThread.java:5086)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:821)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:584)
at dalvik.system.NativeStart.main(Native Method)
Below are the classes.I have used following code for fragment transaction
Fragment fragment = new PhotosFragment2();
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.replace(android.R.id.content, fragment);
fragmentTransaction.commit();
PhotosFragment.java
public class PhotosFragment extends Fragment {
private FragmentActivity myContext;
#Override
public void onAttach(Activity activity) {
myContext = (FragmentActivity) activity;
super.onAttach(activity);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.photos, container, false);
rootView.setVerticalScrollBarEnabled(false);
int[] mThumbIds = {
R.drawable.album8, R.drawable.album3,
R.drawable.album4, R.drawable.album8,
R.drawable.album6, R.drawable.album7,
R.drawable.album12, R.drawable.album10,
};
int[] mThumbIds2 = {
R.drawable.album8, R.drawable.album3,
R.drawable.album4,
R.drawable.album6, R.drawable.album7,
R.drawable.album9, R.drawable.album10,
R.drawable.album11, R.drawable.album12, R.drawable.album8,
R.drawable.album8, R.drawable.album3,
R.drawable.album4,
R.drawable.album6, R.drawable.album7,
R.drawable.album9, R.drawable.album10,
R.drawable.album11, R.drawable.album12, R.drawable.album8,
};
CustomGridSingle2 adapter = new CustomGridSingle2(myContext, mThumbIds);
GridView grid = (GridView)rootView.findViewById(R.id.gridView);
final ImageView img= (ImageView)rootView.findViewById(R.id.imageView7);
grid.setFocusable(false);
grid.setAdapter(adapter);
grid.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Fragment fragment = new PhotosFragment2();
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.replace(android.R.id.content, fragment);
fragmentTransaction.commit();
}
});
CustomGridSingle2 adapter2 = new CustomGridSingle2(myContext, mThumbIds2);
GridView grid2 = (GridView)rootView.findViewById(R.id.gridView2);
grid2.setFocusable(false);
grid2.setAdapter(adapter2);
grid2.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Fragment fragment = new PhotosFragment2();
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.replace(android.R.id.content, fragment);
fragmentTransaction.commit();
}
});
return rootView;
}
}
PhotosFragment2.java
public class PhotosFragment2 extends Fragment {
private FragmentActivity myContext;
#Override
public void onAttach(Activity activity) {
myContext = (FragmentActivity) activity;
super.onAttach(activity);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View rootView = inflater.inflate(R.layout.photos2, container, false);
myContext.getActionBar().hide();
return rootView;
}
}
Activity xml file
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/frame_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<LinearLayout
android:id="#+id/left_drawer_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="#drawable/bgmenu"
android:orientation="vertical">
<RelativeLayout
android:id="#+id/profilelayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0"
android:paddingTop="10dp">
<ImageView
android:id="#+id/drawer_profile_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="#+id/drawer_profile_background"
android:layout_alignLeft="#+id/drawer_profile_background"
android:layout_alignRight="#+id/drawer_profile_background"
android:layout_alignTop="#+id/drawer_profile_background"
android:layout_marginBottom="7.667dp"
android:layout_marginLeft="6.5dp"
android:layout_marginRight="8.3dp"
android:layout_marginTop="7.667dp"
android:scaleType="centerCrop"></ImageView>
<ImageView
android:id="#+id/drawer_profile_background"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:scaleType="centerCrop"
android:src="#drawable/profileblock">
</ImageView>
<ImageView
android:id="#+id/settingicon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="#+id/drawer_profile_background"
android:layout_marginLeft="-15dp"
android:layout_toRightOf="#+id/drawer_profile_background"
android:background="#drawable/settings" />
<textview
android:id="#+id/username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/drawer_profile_background"
android:layout_centerHorizontal="true"
android:layout_marginTop="5dp"
android:text="Name"
android:textAppearance="?android:attr/textAppearanceListItemSmall"
android:textColor="#android:color/white" />
</RelativeLayout>
<ListView
android:id="#+id/list_slidermenu"
style="#style/buttonStyle"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="30dp"
android:layout_weight="2"
android:cacheColorHint="#android:color/transparent"
android:choiceMode="singleChoice"
android:divider="#android:color/transparent"
android:dividerHeight="1dp"
android:listSelector="#android:color/transparent"
android:scrollbars="none" />
</LinearLayout>
</android.support.v4.widget.DrawerLayout>
When you use fragmentTransaction.replace(R.id.container,fragment) it will remove any fragments that are already in the container and add your new one to the same container.
Now i can suggest you 2 things.First, if you want to use fragmentTransaction.replace(android.R.id.content, fragment); which you are doing right now,then don't set content for your Activity using setContentView.This should work fine then.To know what exactly android.R.id.content is you can refer this stackoverflow question and answer
Or Secondly, In the layout of your Activity have a FrameLayout whose id is content. And then use
fragmentTransaction.replace(R.id.content, fragment);
fragmentTransaction.addToBackStack(null);//add the transaction to the back stack so the user can navigate back
// Commit the transaction
fragmentTransaction.commit();
Hope this helps.
More Info:
From your comments it seems that you are having some problem in getting the idea of using FrameLayout in your Activity's layout(Not of any of the Fragment's layout).From the Documents
FrameLayout is designed to block out an area on the screen to display a single item. Generally, FrameLayout should be used to hold a single child view, because it can be difficult to organize child views in a way that's scalable to different screen sizes without the children overlapping each other.
So the main purpose of FrameLayout is to block the area required to fit the largest child view. If you use a FrameLayout as Fragment Container you can ensure that you always have the space available to accommodate the largest Fragment's layout.
So you can have your FrameLayout something like this in your Activity's layout xml file
<FrameLayout
android:id="#+id/content"
android:layout_height="match_parent"
android:layout_width="match_parent">
<!--you can put your existing views of your current xml here, so yes your entire xml is now inside this FrameLayout -->
</FrameLayout>
I don't use getSupportFragmentManager().
I use getChildFragmentManager() and it worked for me.
You can try it.
If you are trying to put Fragment into Fragment, use getChildFragmentManager()
If you are using ViewPager, use getChildFragmentManager() for pager adapter
viewPager.setAdapter(new ItemsAdapter(getChildFragmentManager()));
You have to set a content view in your Activity that your PhotosFragment is attached.
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.activity_main);
//...
}
i know this is very late. But it may work for other's.
I declared the id for each layout, like (3rd line bellow)
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/container"
Since Android 4.2 (API 17) nested fragments become avaliable.
See this
To place fragment inside other fragment use getChildFragmentManager()
Also available in support library.
I had the same error and after trying a lot of things I found that the problem was with the "id" and "getActivity().getSupportFragmentManager()".
Before i implement i like these
txt_more.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
getChildFragmentManager()
.beginTransaction()
.replace(R.id.content_frame, new OpportunitiesFragment())
.commit(); }
});
Then i changed to these
txt_more.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
## Heading ##
getActivity().getSupportFragmentManager()
.beginTransaction()
.replace(android.R.id.content, new OpportunitiesFragment())
.commit(); }
});
Put this block of code in PhotosFragment2 onCreateView method:
View rootView = inflater.inflate(R.layout.photos2, null);
instead of:
View rootView = inflater.inflate(R.layout.photos2, container, false);
in my case I had to change
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.sample,
container);
to
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.sample,
container, false);
This answer highlights a silly mistake that may occur, that arises when nesting fragments or you are converting activities to fragments.
If you are trying to replace a fragment within a fragment with the fragmentManager but you are not inflating the parent fragment that can cause an issue.
In BaseFragment.java OnCreateView:
if (savedInstanceState == null) {
getFragmentManager().beginTransaction()
.replace(R.id.container, new DifferentFragment())
.commit();
}
return super.onCreateView(inflater, container, savedInstanceState);
Replace super.onCreateView(inflater, container, savedInstanceState);
with inflating the correct layout for the fragment:
return inflater.inflate(R.layout.base_fragment, container, false);
The problem may be that you add a fragment and beginTransaction in some fragment like
getFragmentManager().beginTransaction().replace(android.R.id.content, ImageBrowseFragment.newInstance(bundle),"ImageBrowseFragment")
.addToBackStack(null).commit();
I just change getFragmentManager() to getActivity().getSupportFragmentManager()
Then everything goes fine
When you add a fragment in a fragment use android.R.id.content Or Window.ID_ANDROID_CONTENT,I guess it represents the root container of your fragment not the activity. So change to getActivity().getSupportFragmentManager() works it out
If you use ViewPager with fragments (pages) and try to show a new fragment from a page, you will get this error. I think, you cannot reference to a container of an Activity from a ViewPager's page.
In this case you should call getActivity(). Then in Activity write a method that will open a new fragment.
class YourActivity : AppCompatActivity() {
fun showNewFragment(title: String) {
val fm = supportFragmentManager
fm?.beginTransaction()?.apply {
val fragment = NewFragment.newInstance(title)
replace(R.id.container, fragment, NewFragment.TAG)
addToBackStack(null)
}?.commit()
}
}
class PageFragment : Fragment() {
private fun show() {
(activity as YourActivity).showNewFragment("New fragment")
}
}
If you call getParentFragment() and add a new fragment, then you will place a new fragment inside ViewPager:
class ViewPagerFragment : Fragment() {
fun showNewFragment(title: String) {
val fm = childFragmentManager
fm?.beginTransaction()?.apply {
val fragment = NewFragment.newInstance(title)
replace(R.id.container, fragment, NewFragment.TAG)
addToBackStack(null)
}?.commit()
}
}
class PageFragment : Fragment() {
private fun show() {
(parentFragment as? ViewPagerFragment)?.showNewFragment("New fragment")
}
}
Make sure the fragment id you are calling is in the class listed in setcontent in onCreate method. For my case, I was editing the layout from linear to constraint. So as not to mess with the working code, I made another layout but messed an id in the process so that what was on oncreate was the edited layout but the fragment being called was from the old layout. Apparently, in the new layout I gave it a different id name. Search the id of your fragment using ctrl+shift+f and see if the search results point to the layout you specified in the oncreate method. Goodluck.
The most common mistake that can be happened is, If you have more than one version of XML layout (let say v21 and v23) and you added <fragment> tag in lower version of XML only (or vice-versa).
So, please check you added <fragment> tag (or pageGroup, etc) in both of the versions.
You are trying to open fragment which fragmentContainer(the view in which you are trying to open fragment) is one or more level above from your current fragmentContainer.
Try to change your fragment hierarchy architecture.

Nested Fragments. How to know when all of them are attached to the activity?

My application uses multiple nested fragments. Some of these fragments are placeholders. When I try to replace placeholders with initial set of fragments I get
java.lang.IllegalStateException: Activity has been destroyed
I am sure that activity is fine and the issue is caused by fragments not being attached to the activity. So how do know when all nested fragments are attached to the activity?
Additional information about my setup:
I have an Activity that has fragment declaration in it's layout file:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/mainLayout"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="#+id/rightPane"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.company.PagerFragment"/>
</LinearLayout>
The PagerFragment is the host for view pager.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal">
<ViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
I put two Fragments into the pager. They serve as placeholders.
I want to replace placeholders with initial fragments but when I initiate the replacement in activity's onCreate method I get the aforementioned exception.
Everything works fine if I use new Handler().postDelayed(..) in activity's onCreate method. I would rather to not use the delay approach for obvious reasons.
I tried to replace placeholders in other activity's methods like onStart(), onResume(), onResumeFragments(), onPostResume() but that didn't help.
EDIT:
public class PagerFragment extends Fragment
{
..
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
return inflater.inflate(R.layout.fragment_right_pane, container, false);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState)
{
super.onViewCreated(view, savedInstanceState);
if (savedInstanceState == null)
{
m_primary = new PlaceholderFragment();
m_secondary = new PlaceholderFragment();
}
else
{
FragmentManager fm = getChildFragmentManager();
m_primary = (PlaceholderFragment)fm.getFragment(savedInstanceState, FRAGMENT_TAG_PRIMARY);
m_secondary = (PlaceholderFragment)fm.getFragment(savedInstanceState, FRAGMENT_TAG_SECONDARY);
}
Fragment[] fragments = new Fragment[] { m_primary, m_secondary };
PagerAdapter pagerAdapter = new FragmentPagerAdapter(getChildFragmentManager(), fragments);
m_viewPager = (ViewPager)view.findViewById(R.id.pager);
m_viewPager.setAdapter(pagerAdapter);
m_viewPager.setOnPageChangeListener(this);
m_viewPager.setCurrentItem(0, true);
}
..
}

Fragment Inside Fragment

I need help regarding working on fragment inside fragment, actually I
am facing a problem on pressing back button. Application Main screen
has buttons and pressing on each button view replace with new
fragment(and that fragment contain inside another fragment),
dynamically adding/replacing fragment is working fine, by pressing
button1 fragment replaced, same happens when pressing button, but if
I press the button again, got an exception:
"Duplicate id 0x7f05000a, tag null, or parent id 0x7f050009 with
another fragment for com........ fragmentname"
means fragment or inner fragments are already added and I am trying
to add them again, anybody has idea how to work with fragment inside
fragment and moving back and forth without any problem, thanks for the
support.
MainActivity, where fragments are dynamical added and
replaced.
public class FragmentInsideFragmentTestActivity extends Activity {
private Button button1;
private Button button2;
private Button button3;
private Button button4;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
button1 =(Button) this.findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
onButtonClick(view);
}
});
button2 =(Button) this.findViewById(R.id.button2);
button2.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
onButtonClick(view);
}
});
button3 =(Button) this.findViewById(R.id.button3);
button3.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
onButtonClick(view);
}
});
button4 =(Button) this.findViewById(R.id.button4);
button4.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
onButtonClick(view);
}
});
}
public void onButtonClick(View v) {
Fragment fg;
switch (v.getId()) {
case R.id.button1:
fg=FirstFragment.newInstance();
replaceFragment(fg);
break;
case R.id.button2:
fg=SecondFragment.newInstance();
replaceFragment(fg);
break;
case R.id.button3:
fg=FirstFragment.newInstance();
replaceFragment(fg);
break;
case R.id.button4:
fg=SecondFragment.newInstance();
replaceFragment(fg);
break;
}
}
private void replaceFragment(Fragment newFragment) {
FragmentTransaction trasection = getFragmentManager().beginTransaction();
if(!newFragment.isAdded()) {
try {
//FragmentTransaction trasection =
getFragmentManager().beginTransaction();
trasection.replace(R.id.linearLayout2, newFragment);
trasection.addToBackStack(null);
trasection.commit();
} catch (Exception e) {
// TODO: handle exception
// AppConstants.printLog(e.getMessage());
} else {
trasection.show(newFragment);
}
}
}
Here is Layout: main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<LinearLayout
android:id="#+id/linearLayout1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button1" />
<Button
android:id="#+id/button2"
android:text="Button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="#+id/button3"
android:text="Button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="#+id/button4"
android:text="Button4"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:id="#+id/linearLayout2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" />
</LinearLayout>
Hope I tried to clear my problem.
AFAIK, fragments cannot hold other fragments.
UPDATE
With current versions of the Android Support package -- or native fragments on API Level 17 and higher -- you can nest fragments, by means of getChildFragmentManager(). Note that this means that you need to use the Android Support package version of fragments on API Levels 11-16, because even though there is a native version of fragments on those devices, that version does not have getChildFragmentManager().
I needed some more context, so I made an example to show how this is done. The most helpful thing I read while preparing was this:
Creating and Using Fragments
Activity
activity_main.xml
Add a FrameLayout to your activity to hold the parent fragment.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Activity"/>
<FrameLayout
android:id="#+id/parent_fragment_container"
android:layout_width="match_parent"
android:layout_height="200dp"/>
</LinearLayout>
MainActivity.java
Load the parent fragment and implement the fragment listeners. (See fragment communication.)
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity implements ParentFragment.OnFragmentInteractionListener, ChildFragment.OnFragmentInteractionListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Begin the transaction
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.parent_fragment_container, new ParentFragment());
ft.commit();
}
#Override
public void messageFromParentFragment(Uri uri) {
Log.i("TAG", "received communication from parent fragment");
}
#Override
public void messageFromChildFragment(Uri uri) {
Log.i("TAG", "received communication from child fragment");
}
}
Parent Fragment
fragment_parent.xml
Add another FrameLayout container for the child fragment.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="20dp"
android:background="#91d0c2">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Parent fragment"/>
<FrameLayout
android:id="#+id/child_fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>
</LinearLayout>
ParentFragment.java
Use getChildFragmentManager in onViewCreated to set up the child fragment.
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
public class ParentFragment extends Fragment {
private OnFragmentInteractionListener mListener;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_parent, container, false);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
Fragment childFragment = new ChildFragment();
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
transaction.replace(R.id.child_fragment_container, childFragment).commit();
}
#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");
}
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
void messageFromParentFragment(Uri uri);
}
}
Child Fragment
fragment_child.xml
There is nothing special here.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="20dp"
android:background="#f1ff91">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Child fragment"/>
</LinearLayout>
ChildFragment.java
There is nothing too special here, either.
import android.support.v4.app.Fragment;
public class ChildFragment extends Fragment {
private OnFragmentInteractionListener mListener;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_child, container, false);
}
#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");
}
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
void messageFromChildFragment(Uri uri);
}
}
Notes
The support library is being used so that nested fragments can be used before Android 4.2.
Since Android 4.2 (API 17) nested fragments become available http://developer.android.com/about/versions/android-4.2.html#NestedFragments
To place fragment inside other fragment use getChildFragmentManager()
It also available in support library!
you can use getChildFragmentManager() function.
example:
Parent fragment :
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.parent_fragment, container,
false);
}
//child fragment
FragmentManager childFragMan = getChildFragmentManager();
FragmentTransaction childFragTrans = childFragMan.beginTransaction();
ChildFragment fragB = new ChildFragment ();
childFragTrans.add(R.id.FRAGMENT_PLACEHOLDER, fragB);
childFragTrans.addToBackStack("B");
childFragTrans.commit();
return rootView;
}
Parent layout (parent_fragment.xml):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/white">
<FrameLayout
android:id="#+id/FRAGMENT_PLACEHOLDER"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
Child Fragment:
public class ChildFragment extends Fragment implements View.OnClickListener{
View v ;
#Override
public View onCreateView(LayoutInflater inflater,
#Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
// TODO Auto-generated method stub
View rootView = inflater.inflate(R.layout.child_fragment, container, false);
v = rootView;
return rootView;
}
#Override
public void onClick(View view) {
}
}
Fragments can be added inside other fragments but then you will need to remove it from parent Fragment each time when onDestroyView() method of parent fragment is called. And again add it in Parent Fragment's onCreateView() method.
Just do like this :
#Override
public void onDestroyView()
{
FragmentManager mFragmentMgr= getFragmentManager();
FragmentTransaction mTransaction = mFragmentMgr.beginTransaction();
Fragment childFragment =mFragmentMgr.findFragmentByTag("qa_fragment")
mTransaction.remove(childFragment);
mTransaction.commit();
super.onDestroyView();
}
I solved this problem. You can use Support library and ViewPager. If you don't need swiping by gesture you can disable swiping. So here is some code to improve my solution:
public class TestFragment extends Fragment{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.frag, container, false);
final ArrayList<Fragment> list = new ArrayList<Fragment>();
list.add(new TrFrag());
list.add(new TrFrag());
list.add(new TrFrag());
ViewPager pager = (ViewPager) v.findViewById(R.id.pager);
pager.setAdapter(new FragmentPagerAdapter(getChildFragmentManager()) {
#Override
public Fragment getItem(int i) {
return list.get(i);
}
#Override
public int getCount() {
return list.size();
}
});
return v;
}
}
P.S.It is ugly code for test, but it improves that it is possible.
P.P.S Inside fragment ChildFragmentManager should be passed to ViewPagerAdapter
It's nothing complicated. We cannot use getFragmentManager() here. For using Fragments inside a Fragment, we use getChildFragmentManager(). Rest will be the same.
Use getChildFragmentManager(), follow the link :
Nested Fragment
You can add FrameLayout to the fragment and replace it with another fragment when it initializes.
This way , you could consider the other fragment to be inside the first fragment.
Curently in nested fragment, the nested one(s) are only supported if they are generated programmatically! So at this time no nested fragment layout are supported in xml layout scheme!
That may help those who works on Kotlin you can use extension function
so create a kotlin file let's say "util.kt" and add this piece of code
fun Fragment.addChildFragment(fragment: Fragment, frameId: Int) {
val transaction = childFragmentManager.beginTransaction()
transaction.replace(frameId, fragment).commit()
}
Let's say this is the class of the child
class InputFieldPresentation: Fragment()
{
var views: View? = null
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
views = inflater!!.inflate(R.layout.input_field_frag, container, false)
return views
}
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
...
}
...
}
Now you can add the children to the father fragment like this
FatherPresentation:Fragment()
{
...
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val fieldFragment= InputFieldPresentation()
addChildFragment(fieldFragment,R.id.fragmet_field)
}
...
}
where R.id.fragmet_field is the id of the layout which will contain the fragment.This lyout is inside the father fragment of course.
Here is an example
father_fragment.xml:
<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android"
>
...
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:id="#+id/fragmet_field"
android:orientation="vertical"
>
</LinearLayout>
...
</LinearLayout>
There is no support for MapFragment, Android team says is working on it since Android 3.0. Here more information about the issue But what you can do is by creating a Fragment that returns a MapActivity. Here is a code example. Thanks to inazaruk:
How it works:
MainFragmentActivity is the activity that extends FragmentActivity and hosts two MapFragments.
MyMapActivity extends MapActivity and has MapView.
LocalActivityManagerFragment hosts LocalActivityManager.
MyMapFragment extends LocalActivityManagerFragment and with help of TabHost creates internal instance of MyMapActivity.
If you have any doubt please let me know
Hi I solved this problem by putting per Fragment into distinct layout.And I made just related Layout visible and made the others visibilities gone.
I mean:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout android:id="#+id/linearLayout1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button android:layout_width="wrap_content"
android:id="#+id/button1"
android:layout_height="wrap_content"
android:text="Button1"></Button>
<Button android:text="Button2"
android:id="#+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"></Button>
<Button android:text="Button3"
android:id="#+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"></Button>
<Button android:text="Button4"
android:id="#+id/button4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"></Button>
</LinearLayout>
<LinearLayout android:layout_width="full_screen"
android:layout_height="0dp"
android:layout_weight="1"
android:id="action_For_Button1"
android:visibility="visible">
<Fragment android:layout_width="full_screen"
android:layout_height="full_screen"
android:id="fragment1"
.
.
.
/ >
</LinearLayout>
<LinearLayout android:layout_width="full_screen"
android:layout_height="0dp"
android:id="action_For_Button1"
android:layout_weight="1"
android:visibility="gone">
<Fragment android:layout_width="full_screen"
android:layout_height="full_screen"
android:id="fragment2"
.
.
.
/ >
</LinearLayout>
.
.
.
</LinearLayout>
I assumed that you will open your page as button 1 is clicked.You can control your fragment's visibilities on click action.You can make related Layout visible and the others gone and by Fragment Manager you can take your fragment.This approach worked for me.And since view that has visibility:gone is invisible, and it doesn't take any space for layout purposes I think this approach does not cause any space problem.
P.S:I just tried to explain my solution code may have syntax mistakes or uncompleted structure.

Categories

Resources