The gist of my problem:
I have multiple fragments that are added sequentially and not re-ordered. Almost all of them have SwipeRefreshLayouts as their root views. When the user triggers onBackPressed, the last fragment should be removed. If a new fragment is opened, the last fragment is hidden.
In the event that the SwipeRefreshLayout is active (ie refresh state set to true and onRefresh is called) and the user pops off the current fragment, the view remains stuck until an app restart. I've confirmed that onPause() and onDestroy() do get called. I have tested this across each type of my fragments, so I'm nearly sure the problem lies in how I remove fragments.
My code:
//I know this is a misnomer. I just changed the inside from
//addToBackStack which got me the same effect
protected void addToBackStack(BaseFragment fragment){
FragmentTransaction ft = fragMan.beginTransaction();
if(fragCount > 0)
ft.hide(peekBackStack());
ft.add(R.id.fragment_container, fragment, Integer.toString(fragCount++)).commit();
invalidateOptionsMenu();
}
protected BaseFragment peekBackStack(){
return (BaseFragment)(fragMan.findFragmentByTag(Integer.toString(fragCount - 1)));
}
#Override
public void onBackPressed(){
if (fragCount <= 1) {
//...do stuff & finish
} else {
BaseFragment fragment = peekBackStack();
if(fragment != null) {
if (fragment.isWeb() && ((WebFragment) fragment).canGoBack())
((WebFragment) fragment).goBack();
else
popActive();
}
else
finish();
}
}
public void popActive(){
BaseFragment remove = peekBackStack();
fragCount--;
BaseFragment show = peekBackStack();
fragMan.beginTransaction().remove(remove).commit();
Log.d(TAG, ""+remove.isVisible());
fragMan.beginTransaction().show(show).commitNow();
show.initAppBar();
}
Inside BaseFragment.java
#Override
#NonNull
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle state){
ViewGroup root = (ViewGroup) inflater.inflate(getLayoutResource(), container, false);
if(root instanceof SwipeRefreshLayout) {
swipeRefreshLayout = (SwipeRefreshLayout) root;
swipeRefreshLayout.setOnRefreshListener(this);
}
return root;
}
#Override
public void onDestroy(){
super.onDestroy();
if(swipeRefreshLayout != null)
swipeRefreshLayout.destroyDrawingCache();
}
...
WebFragment XML:
<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/swipe_refresh_submission_link"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.github.ksoichiro.android.observablescrollview.ObservableWebView
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:id="#+id/observable_web"
android:layout_centerHorizontal="true"
android:layout_below="#+id/toolbar"
android:scrollbarStyle="outsideOverlay"
android:fadeScrollbars="true">
</com.github.ksoichiro.android.observablescrollview.ObservableWebView>
Main activity XML:
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/coordinatorLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/parent"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="#+id/appbarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
...
</android.support.design.widget.AppBarLayout>
<RelativeLayout
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/appbarLayout">
</RelativeLayout>
</RelativeLayout>
</android.support.design.widget.CoordinatorLayout>
So I found a related thread after hours of searching. Both solutions worked for me, although I think I prefer the FrameLayout wrapper for now. This seems to be a bug in android itself
Related
I am currently trying to create an app having a splash screen, a login screen and a register screen.
The only way to achieve exactly what I need is through fragments. Though when I try to transition from the splash screen to the login fragment the text view which was part of the splash screen remains. The same happens after transitioning from the login fragment to the register fragment as well as from the register fragment back to the login fragment. It is important to note that there is no issue during transitioning between login and register or between register and login - except the fact that the edit text stays there.
My MainActivity.java
public class StartActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start);
}
#Override
public void onBackPressed(){
Toast.makeText(getApplicationContext(), "Back button is disabled", Toast.LENGTH_SHORT).show();
}
}
My layout for MainActivity
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".StartActivity"
android:background="#mipmap/background">
<fragment
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/viewpagerstart"
android:layout_centerInParent="true"
android:name="com.example.distributedsystemsui.SplashFragment">
</fragment>
</RelativeLayout>
The SplashScreen.java
public class SplashFragment extends Fragment {
LinearLayout linearLayout;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_splash, container, false);
linearLayout = (LinearLayout) view.findViewById(R.id.f_linearsplash);
Animation animation_fadein = AnimationUtils.loadAnimation((StartActivity)getActivity(), R.anim.fade_in);
linearLayout.startAnimation(animation_fadein);
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.setCustomAnimations(R.anim.slide_enter_left, R.anim.slide_exit_left,
R.anim.slide_enter_right, R.anim.slide_exit_right);
LoginFragment loginFragment = LoginFragment.newInstance();
ft.replace(R.id.viewpagerstart, loginFragment);
ft.commit();
}
}, 3000);
return view;
}
}
And the layout for the SplashScreen fragment.
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
tools:context=".LoginFragment"
android:background="#color/Tranparent"
android:id="#+id/frame_splash">
<LinearLayout
android:id="#+id/f_linearsplash"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="#+id/f_splashlogo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="70dp"
android:background="#FFFFFF"/>
<TextView
android:id="#+id/f_splashtext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/app_name"
android:layout_gravity="center_horizontal"
android:layout_marginTop="100dp"
android:background="#FFFFFF"/>
</LinearLayout>
</FrameLayout>
All my search led back to nothing.
I can only assume that either the below contained in the MainActivity.xml creates a confusion (which I tried to change but no luck):
android:name="com.example.distributedsystemsui.SplashFragment"
or that I am doing a mistake in the SplashScreen.java that I cannot locate, even though I tried for more than 5 hours to make it work.
The problem is that you cannot replace fragments added via the <fragment> tag.
Instead, you should either:
1) Switch to FragmentContainerView, which was added in Fragment 1.2.0. It does not suffer from the same issue as the <fragment> tag and lets you do replace operations without issues:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".StartActivity"
android:background="#mipmap/background">
<androidx.fragment.app.FragmentContainerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/viewpagerstart"
android:layout_centerInParent="true"
android:name="com.example.distributedsystemsui.SplashFragment">
</androidx.fragment.app.FragmentContainerView>
</RelativeLayout>
2) Use a FrameLayout in your layout and manually add the SplashFragment in your activity (this is essentially what FragmentContainerView does for you):
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".StartActivity"
android:background="#mipmap/background">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/viewpagerstart"
android:layout_centerInParent="true">
</FrameLayout>
</RelativeLayout>
which means you need to add the SplashFragment manually:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.viewpagerstart, new SplashFragment())
.commit()
}
}
I think it is better to do the fragment transactions from your activity instead of doing it from your fragment. You might consider having functions in your MainActivity as follows.
public class StartActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start);
}
public void goToLoginFragment() {
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.setCustomAnimations(R.anim.slide_enter_left, R.anim.slide_exit_left,
R.anim.slide_enter_right, R.anim.slide_exit_right);
LoginFragment loginFragment = LoginFragment.newInstance();
ft.replace(R.id.viewpagerstart, loginFragment);
ft.commit();
}
public void goToRegisterFragment() {
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.setCustomAnimations(R.anim.slide_enter_left, R.anim.slide_exit_left,
R.anim.slide_enter_right, R.anim.slide_exit_right);
LoginFragment registerFragment = RegisterFragment.newInstance();
ft.replace(R.id.viewpagerstart, registerFragment);
ft.commit();
}
#Override
public void onBackPressed(){
Toast.makeText(getApplicationContext(), "Back button is disabled", Toast.LENGTH_SHORT).show();
}
}
And then from your SplashFragment call the function that is defined in your activity.
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
((MainActivity) getActivity()).goToLoginFragment();
}
}, 3000);
I hope that solves the problem.
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.
In MainActivity's onCreate, I replaced the FrameLayout fragment container in its xml layout with a DragSelectRecyclerView in MainFragment's xml layout using FragmentManager. The following code shows this:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getFragmentManager().beginTransaction()
.replace(R.id.fragmentContainer, new MainFragment())
.commit();
}
MainActivity's xml:
<android.support.design.widget.CoordinatorLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.gra.app.activities.MainActivity">
<FrameLayout
android:id="#+id/fragmentContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingTop="?attr/actionBarSize" />
<android.support.design.widget.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior=".utilities.AppBarLayoutBackgroundAlphaBehavior">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="#style/ThemeOverlay.AppCompat.ActionBar" />
</android.support.design.widget.AppBarLayout>
</android.support.design.widget.CoordinatorLayout>
MainFragment's xml:
<com.afollestad.dragselectrecyclerview.DragSelectRecyclerView 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/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
app:dsrv_autoScrollEnabled="true"
app:dsrv_autoScrollHotspotHeight="56dp"
tools:context="com.gra.app.fragments.MainFragment" />
Is there any way to programmatically access an instance of DragSelectRecyclerView through FrameLayout?
The reason for this is I need to make the AppBarLayout inside MainActivity's xml dependent on MainFragment's DragSelectRecyclerView so I can fade the AppBarLayout in/out based on
DragSelectRecyclerView.getVerticalScrollbarPosition()
through
CoordinatorLayout.Behavior<AppBarLayout>
Is this possible or should I just move the DragSelectRecyclerView inside MainActivity's xml?
I would suggest you to have a public method in MainFragment -
public float getVerticalScrollbarPosition(){
return DragSelectRecyclerView.getVerticalScrollbarPosition();
}
Update MainActivity's onCreate method to this -
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MainFragment mainFragment = new MainFragment();
getFragmentManager().beginTransaction()
.replace(R.id.fragmentContainer, mainFragment)
.commit();
}
Now within the MainActivity, you can access the method anywhere to get the value and fade the AppBarLayout in/out accordingly -
mainFragment.getVerticalScrollbarPosition();
This is how the communication between fragments and activity should be handled - https://developer.android.com/guide/components/fragments.html#CommunicatingWithActivity
You could set the fragment directly in the xml layout of the main activity like
<RelativeLayout
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:layout_width="match_parent"
android:layout_height="match_parent">
<fragment android:id="#+id/fragment"
android:name="some.package.MainFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
And then get the instance of DragSelectRecyclerView in the onCreate method of your activity by
DragSelectRecyclerView recyclerView = (DragSelectRecyclerView) findViewById(R.id.recyclerView)
Or you just set the DragSelectRecyclerView from your Fragment into your Activity:
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
DragSelectRecyclerView dragSelectRecyclerView = (DragSelectRecyclerView) getView().findViewById(R.id.recyclerView);
Activity activity = getActivity();
if (activity != null && activity instanceof MainActivity) {
((MainActivity) activity).setDragSelectRecyclerView(dragSelectRecyclerView);
}
}
In both cases make sure you reset the variable after you fragment is gone.
#Override
public void onDestroyView() {
super.onDestroyView();
Activity activity = getActivity();
if (activity != null && activity instanceof MainActivity) {
((MainActivity) activity).setDragSelectRecyclerView(null);
}
}
you can try setting a tag in the fragment transaction and then retrieve the fragment and its view by the tag you set during the transaction.
you can do it like this:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getFragmentManager().beginTransaction()
.replace(R.id.fragmentContainer, new MainFragment(),"tag") //Adding tag here
.commit();
}
and then later you can retreive the recyclerview inside that fragment by:
View fragmentView = getFragmentManager().findFragmentByTag("tag").getView();
DragSelectRecyclerView recyclerView = (DragSelectRecyclerView)fragmentView.findViewById(R.id.recyclerView) //use the view
but View fragmentView = getFragmentManager().findFragmentByTag("tag").getView(); may return null based on the state of the fragment's state. so you need to look out for that.
I am unable to get a fragment to update after changing data. I call
getFragmentManager().beginTransaction().detach(this).attach(this).commit();
but onCreateView does not get called. I tried several different ways in refreshFragment(). I do see messages for "I/InjectionManager: dispatchCreateOptionsMenu" after I put in the above line of code so something is happening, but onCreateView() is not getting called.
public class MainActivityFragment extends Fragment {
private static Logger logger = Logger.getLogger(MainActivityFragment.class);
String poolWord = "ABCDE";
String newWord = "";
public MainActivityFragment() {
}
#Override
public void onCreate(Bundle savedInstanceState) {
logger.info("onCreate------------------------");
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
logger.info("onCreateView------------------------");
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_main, container, false);
GridView gridViewPool = (GridView) view.findViewById(R.id.gridviewPool);
gridViewPool.setAdapter(new LetterImageAdapter(getActivity(), poolWord));
gridViewPool.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
logger.error("po " + position);
updateWords(position);
refreshFragment();
}
});
GridView gridviewNewWord = (GridView) view.findViewById(R.id.gridviewNewWord);
gridviewNewWord.setAdapter(new LetterImageAdapter(getActivity(), ""));
return view;
}
private void refreshFragment() {
logger.info("refreshFragment------------------------");
getFragmentManager().beginTransaction().detach(this).attach(this).commit();
// getFragmentManager().beginTransaction().replace(R.id.fragment, this).attach(this).commit();
// Fragment fragment = getFragmentManager().findFragmentById(R.id.fragment);
//getFragmentManager().beginTransaction().detach(fragment).attach(fragment).commit();
getFragmentManager().executePendingTransactions();
}
After several hours, I got it to work. Not sure of all the details as this is my first android project. Posting an answer in case anybody else has the same problem.
The crux of the problem, I suspect, is the generated code from Intellij did not start a transaction in the Activity class. It did not include
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new MainActivityFragment())
.commit();
}
I'm not sure how my MainActivityFragment was created without this, but it was.I also replaced the generated activity-main.xml with one I had from a tutorial. The one from Intellij did not have an android:id that I could use for the container object. It also contained other stuff I did not need at this time.The new activity-main.xml looks like this:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:id="#+id/container"
android:layout_width="match_parent" android:layout_height="match_parent"
tools:context=".MainActivity" tools:ignore="MergeRootFrame"/>
The old generated one was:
<android.support.design.widget.AppBarLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:theme="#style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="#style/AppTheme.PopupOverlay"/>
</android.support.design.widget.AppBarLayout>
<include layout="#layout/content_main"/>
</android.support.design.widget.CoordinatorLayout>
There was also a content-main.xml which I got rid of. That may be what android used to generate my MainActivityFragment without explicit code. Someone more experienced with android development may be able to answer that.
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/fragment"
android:name="com.ultramondo.mywordandroid.MainActivityFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:layout="#layout/fragment_main">
</fragment>
I'm sure there's some useful stuff I can go back to but for now I want simplicity. I also had to make some changes to use android.support.v4.app packages for Fragment and Transaction classes. It's kind of like coming in the middle of a movie and figuring out what the plot is. Once I see the whole movie, I'm sure it will become clearer.
I get the following exception:
java.lang.IllegalArgumentException:
No view found for id 0x7f04001a (com.whatever:layout/fragment_main)
Looking at similar questions on SO:
One answer suggests using getChildFragmentManager() instead of getSupportFragmentManager().
This is not an option since MainActivity is not a Fragment.
Another answer suggests that fragment_main is not a child of activity_main, but I think I have that setup in activity_main.xml with tools:layout="#layout/fragment_main
Another suggests that setContent() has not been called, but I have that in onCreate()
What am I missing?
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public void onStart() {
super.onStart();
// create fragment immediately
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.layout.fragment_main, new MainActivityFragment());
fragmentTransaction.commit();
boolean wereThereWereAnyPendingTransactions = fragmentManager.executePendingTransactions();
}
}
MainActivityFragment.java
public class MainActivityFragment extends Fragment implements Observer<String> {
public MainActivityFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_main, container, false);
}
activity_main.xml
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/fragment"
android:name="com.whatever.MainActivityFragment"
tools:layout="#layout/fragment_main"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
fragment_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/fragment_main_table"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin"
tools:context=".MainActivityFragment">
<TextView
android:text="#string/fragment_main_xml"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
The answer with respect to code:
actvity_main.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_frame_layout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
tools:context=".MainActivity" >
</FrameLayout>
MainActivity.java:
...
fragmentTransaction.add(R.id.activity_frame_layout, new MainActivityFragment());
...
If you embed a Fragment inside of an Activity XML layout, you do not create it dynamically. The system does it for you. So there is no need to create the transaction and commit it, the Fragment will be created and added when you called setContentView().
Note that the tools:layout= line does not do anything at runtime. It is strictly for the tools to better link layouts and components together.