My MainActivity has a TabLayout that I'm showing(View.VISIBILE) when certain fragments(say type A) are loaded and hiding(View.GONE) when other fragments(say type B) are loaded.
Now while transitioning between type A and type B fragments - the TabLayout and the fragment don't load together. First the TabLayout becomes visible and the previous fragment shifts down, then the new fragment replaces the old fragment. Of course this happens in milliseconds but some visible effect is there on looking carefully.
How can I make sure both TabLayout visibility change and fragment replace happen together?
Here is the code to load fragments in MainActivity.java:
...
private View mTitleWithTabs = findViewById(R.id.title_and_hometabs);
...
#UiThread
public void loadFragment(Fragment fragment) {
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction
.add(R.id.frameLayout, fragment)
.addToBackStack(null)
.commit();
if (/* type A fragment */) {
setTabsHomeButtonSelected(true); // setting an ImageView as selected or not
scrollAndSelectTabAtPositionOnlyUI(0);
mTitleWithTabs.setVisibility(View.VISIBLE);
} else {
setTabsHomeButtonSelected(false);
mTitleWithTabs.setVisibility(View.GONE);
}
}
Here is the relevant code from activity_main.xml
...
<include
android:id="#+id/title_and_hometabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
layout="#layout/title_with_hometabs"/>
<FrameLayout
android:id="#+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#id/exo_mini"
android:layout_below="#id/title_and_hometabs"/>
...
Here is title_with_hometabs.xml
<?xml version="1.0" encoding="utf-8"?>
<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/title_with_hometabs"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:showIn="#layout/activity_main">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
android:orientation="horizontal"
android:weightSum="1">
<TextView
android:id="#+id/title"
android:paddingStart="35dp"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight=".90"
android:gravity="center"
android:text="#string/app_name"
android:textColor="#color/colorViewAll"
android:textSize="#dimen/text_size_22" />
<ImageView
android:id="#+id/search_browser"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="center_vertical"
android:layout_weight=".10"
android:src="#drawable/ic_search_black_14dp" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#color/colorDivider" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="42dp"
android:orientation="horizontal">
<RelativeLayout
android:layout_width="?actionBarSize"
android:layout_height="42dp"
android:gravity="center">
<ImageView
android:id="#+id/home_button_tabs"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:src="#mipmap/home" />
</RelativeLayout>
<View
android:layout_width="0.5dp"
android:layout_height="match_parent"
android:background="#color/colorDivider" />
<com.google.android.material.tabs.TabLayout
android:id="#+id/tabs_list"
android:layout_height="42dp"
android:layout_width="match_parent"
app:tabBackground="#android:color/transparent"
app:tabRippleColor="#null"
app:tabMode="scrollable"
app:tabIndicatorColor="#android:color/holo_red_dark"
app:tabSelectedTextColor="#android:color/holo_blue_dark"
app:tabTextAppearance="#style/CustomTabLanguages"/>
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="#color/colorDivider" />
</LinearLayout>
</LinearLayout>
The thing that worked for me was to use getSupportFragmentManager().executePendingTransactions() after calling commit() inside the loadFragment() method in MainActivity.java.
This is because the delay between additional UI element in MainActivity and fragment-load was coming because of FragmentManager.commit() being asynchronous.
Other ways that I explored and didn't work:
Using FragmentManager.commitNow() doesn't work because I need the backstack.
Using FragmentManager.runOnCommit(additionUIElementChangeRunnable) doesn't also allow the fragment transaction to be added the backstack.
Note: FragmentManager.commitNow() doesn't allow using backstack because ordering guarantee in backstack can't be provided when commitNow is used with commit.
Related
I have 3 different fragments in the same activity, and the Activity contains a toolbar with a settings button. When I press the settings button, the Settings Fragment should open. How can I implement this using Navigation? Should I create an action from every single fragment to Settings fragment or there is a simple way? (considering that the settings button is in activity layout).
I also created a diagram for better understanding. If you have any questions let me know. Thanks
This is how I set the navigation graph in my activity:
private fun setDefaultNavGraph(bundle: Bundle?){
fragmentContainer = my_nav_host_fragment as NavHostFragment
val inflater = fragmentContainer.findNavController().navInflater
val graph = inflater.inflate(R.navigation.prb_navigation)
fragmentContainer.findNavController().setGraph(graph, bundle)
}
And this is my activity xml file:
<?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="com.eschbachit.citylinemobile.activities.PrbModuleActivity">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="#dimen/toolbar_height"
android:background="#color/toolbarDarkGrey"
app:theme="#style/ToolbarColoredWhiteIcon"
app:contentInsetLeft="0dp"
app:contentInsetStart="0dp"
app:contentInsetRight="0dp"
app:contentInsetEnd="0dp">
<androidx.appcompat.widget.AppCompatImageView
android:id="#+id/back_btn"
android:layout_width="24dp"
android:layout_height="match_parent"
android:paddingTop="16dp"
android:paddingBottom="16dp"
android:layout_marginStart="4dp"
app:srcCompat="#drawable/back_button_icon"
android:tint="#color/white"
android:visibility="gone"
android:contentDescription="#null"/>
<TextView
android:id="#+id/prb_activity_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="1"
android:ellipsize="end"
android:text="#string/title_activity_main"
android:textSize="#dimen/font_size_default"
android:lineSpacingExtra="4sp"
android:textStyle="bold"
android:textColor="#color/white"
android:layout_gravity="center"/>
<androidx.appcompat.widget.AppCompatImageView
android:id="#+id/settings_nav_tv"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:textColor="#color/white"
app:srcCompat="#drawable/ic_settings"
android:layout_gravity="end"
android:textSize="#dimen/font_size_default"
android:paddingTop="12dp"
android:paddingBottom="12dp"
android:gravity="center" />
</androidx.appcompat.widget.Toolbar>
<fragment
android:id="#+id/my_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
android:layout_below="#+id/toolbar"/>
</RelativeLayout>
Expecting all your fragments lie on a single activity, you can do it in following ways :-
Adding action (as you said)
Navigation.findNavController(...).navigate(destination.id);
You can also popBackStack if the parent of Fragment A,B,C is Settings Fragment.
Navigation.findNavController(...).popBackStack();
SOLUTION:
The thing I was looking for was a Generic action in my nav_graph.
I found the solution here.
I added this action to my navigation file and I used it in my activity in setOnClickListener method.
<action android:id="#+id/openOnlineSettingsFragment"
app:destination="#id/onlineSettingsFragment"
app:enterAnim="#anim/pull_in_right"
app:exitAnim="#anim/push_out_left"
app:popEnterAnim="#anim/pull_in_left"
app:popExitAnim="#anim/push_out_right" />
Yes. I know that there are a lot of the questions like mine, BUT I watched them all (I guess)
I think that I am doing everything right, but it still does not work
I have a frameLayout. I tried to use add() instead of replace() and that does not work.
I try to add a fragment in OnLoadFinished() - method.
My code:
Bundle fragBundle = new Bundle();
fragmentManager = getFragmentManager();
fragmentTransaction = fragmentManager.beginTransaction();
ContactInfoFragment contactInfoFragment = (ContactInfoFragment) fragmentManager.findFragmentByTag("contactInfo");
if (contactInfoFragment == null){
switch(type) {
case "44": {
//passing some data to bundle
break;
}
case "223":{
//passing some data to bundle
break;
}
}
contactInfoFragment = new ContactInfoFragment();
contactInfoFragment.setArguments(fragBundle);
fragmentTransaction.replace(R.id.contact_layout, contactInfoFragment, "contactInfo");
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
fragmentManager.executePendingTransactions();
}
Please, help me
My Layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="0dp"
android:paddingRight="0dp"
android:paddingTop="0dp"
android:paddingBottom="0dp"
android:background="#ffffff"
tools:context="com.example.tenderplan.activities.ElementActivity">
<ScrollView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/scrollView"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="0dp"
android:paddingRight="0dp"
android:paddingTop="0dp"
android:paddingBottom="0dp"
android:background="#ffffff"
tools:context="com.example.tenderplan.activities.ElementActivity"
android:id="#+id/element_layout"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/contact_layout">
</FrameLayout>
<fragment
android:name="com.example.tenderplan.fragments.DocumentsFragment"
android:id="#+id/documents_fragment"
android:layout_height="wrap_content"
android:layout_width="match_parent"
tools:layout="#layout/documents_fragment"
android:layout_below="#+id/contact_info_fragment"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="16dp" />
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/attachmentslayout2"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="#+id/documents_fragment">
</LinearLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id = "#+id/comments_layout">
</FrameLayout>
</LinearLayout>
</ScrollView>
Actually, I need the contact_layout to be replaced.
You should not commit fragment transactions in onLoadFinish, you can read:
Called when a previously created loader has finished its load. Note that normally an application is not allowed to commit fragment transactions while in this call, since it can happen after an activity's state is saved. See FragmentManager.openTransaction() for further discussion on this.
https://developer.android.com/reference/android/app/LoaderManager.LoaderCallbacks.html#onLoadFinished%28android.content.Loader%3CD%3E,%20D%29
http://developer.android.com/reference/android/app/FragmentManager.html#beginTransaction()
so what you should do is to use commitAllowingStateLoss() instead of commit().
I'm having a problem using SlidingUpPanel (https://github.com/umano/AndroidSlidingUpPanel) and FragmentStatePagerAdapter.
I have 3 fragments in the pager: the first two with a sliding panel (one a fragment and one a hardcoded layout) and the third without it.
When I had a pager with just two fragments the panel were working perfectly fine. Now that I've added a third they are not working anymore.
At first the fragment are created correctly, but when I swipe all the way to the third and then come back the panel get messed up.
Basically the panel that used to be on the first fragment is now on the panel of the second one, the hardcoded layout of the second panel is now gone and the panel of the first fragment is empty.
The main content of all fragment is still correct and behaves as intended.
I've drawn a schema of the situation to explain better:
My code to select the fragment is:
public Fragment getItem(int i) {
Fragment fragment = null;
switch (i) {
case 0:
fragment = new MatchListFragment();
break;
case 1:
fragment = new StreamFragment();
break;
case 2:
fragment = new PendingMatches();
break;
default:
break;
}
return fragment;
}
Layout of first fragment (MatchListFragment):
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/container_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/white"
android:gravity="center"
android:orientation="vertical"
android:visibility="visible">
CONTENT
</RelativeLayout>
<FrameLayout
android:id="#+id/scrollable_panel"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Layout of second fragment (StreamFragment)
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ListView
android:id="#+id/log_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="false"
android:divider="#drawable/material_divider"
android:dividerHeight="1dp"
android:visibility="visible" />
</LinearLayout>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/scrollable_panel"
android:layout_width="match_parent"
android:layout_height="88dp"
android:background="#color/white"
android:gravity="center">
<ImageView
android:id="#+id/icon"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_alignParentBottom="true"
android:background="#color/light_gray" />
<ImageView
android:layout_width="25dp"
android:layout_height="10dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_margin="5dp"
android:src="#drawable/green_pill" />
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#id/icon"
android:layout_alignParentTop="true"
android:orientation="horizontal">
<com.coffeestrap.android.Widgets.IconAndText
android:id="#+id/profile_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:layout_weight="1"
app:resource="#drawable/ic_profile_icon"
app:undertext="New" />
<com.coffeestrap.android.Widgets.IconAndText
android:id="#+id/talk_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:layout_weight="1"
app:resource="#drawable/ic_talk_icon"
app:undertext="Learning" />
<com.coffeestrap.android.Widgets.IconAndText
android:id="#+id/conversation_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:layout_weight="1"
app:resource="#drawable/ic_conversation_icon"
app:undertext="Teaching" />
<com.coffeestrap.android.Widgets.IconAndText
android:id="#+id/all_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:layout_weight="1"
app:resource="#drawable/ic_all_icon"
app:undertext="All" />
</LinearLayout>
</RelativeLayout>
I cant really understand how to solve this especially because on the second fragment (StreamFragment()) the sliding panel layout is hardcoded and I can't understand why it loads everything else correctly and the replace the panel with the one from the first fragment(MatchListFragment).
In the end I strongly believe it's an issue with the library I'm using and not one caused by me.
I've opened an issue on their github page
here
I'm using supportlib v4 to reach master-detail flow.
Problem:
New instance of "details" fragment overlays the first one (xml created) instead replace it.
My Activity layout is:
<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"
tools:context=".TrackListActivity" >
<fragment
android:id="#+id/fragmentList"
android:name="pl.com.digita.BikeComputerUi.TrackList.TrackListFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<fragment
android:id="#+id/fragmentTrack"
android:name="pl.com.digita.BikeComputerUi.TrackList.TrackInfoFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2" />
</LinearLayout>
Method called after click:
private void showDetails(long trackId){
View fragmentContainer = getActivity().findViewById(R.id.fragmentTrack);
TrackInfoFragment trackInfoFragment = TrackInfoFragment.newInstance(trackId);
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction().replace(fragmentContainer.getId(), trackInfoFragment).commit();
}
Note: When you add a fragment to an activity layout by defining the fragment in the layout XML file, you cannot remove the fragment at runtime. If you plan to swap your fragments in and out during user interaction, you must add the fragment to the activity when the activity first starts, as shown in the next lesson.
Which is very last thing on http://developer.android.com/training/basics/fragments/creating.html
Here is solution - just need to change fragment to FrameLayout without loading it at activity creation:
<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"
tools:context=".TrackListActivity" >
<fragment
android:id="#+id/fragmentList"
android:name="pl.com.digita.BikeComputerUi.TrackList.TrackListFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<FrameLayout
android:id="#+id/details"
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="2" />
</LinearLayout>
Check following code it will work.
View fragmentContainer = ClassName.this.findViewById(R.id.fragmentTrack);
TrackInfoFragment fragment = new TrackInfoFragment();
getSupportFragmentManager().beginTransaction()
.replace(fragmentContainer.getId(), fragment).commit();
I have a simple layout, which contains two fragments. One of the two fragments periodically will replace itself, the other one will remain consistent for the whole time I am using it. For some reason, removing/adding it works just fine, but replacing it causes the other fragment to disappear. I know from debug code that the Fragment simply gets detached, for no apparent reason. Why might this be? If I use the latter, I have to be extremely careful to ensure that the QuestionArea is gone first...
These lines of code come from the in the FragmentActivity class.
fragManager.beginTransaction()
.replace(R.id.QuestionArea, getQuestionPostView(), "QuestionArea")
.commit();
fragManager.beginTransaction()
.remove(fragManager.findFragmentById(R.id.QuestionArea))
.add(R.id.QuestionArea, getQuestionPostView(), "QuestionArea")
.commit();
Here is what I hope is the other relevant code:
XML of the main display:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:orientation="vertical" >
<com.pearsonartphoto.FontFitTextView
android:id="#+id/Name"
style="#style/bigText"
android:background="#color/blue"
android:gravity="center"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"/>
<TextView android:id="#+id/Instructions"
style="#style/medText"
android:layout_width="fill_parent"
android:layout_below="#id/PowerName"
/>
<fragment
android:name="com.pearsonartphoto.NumberDisplay"
android:gravity="center"
android:id="#+id/QuestionArea"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="#id/Instructions"/>
<fragment
android:name="com.pearsonartphoto.AbstractAnswerFragment"
android:id="#+id/AnswerArea"
style="#style/bigText"
android:gravity="center"
android:layout_below="#+id/QuestionArea"
android:layout_alignParentBottom="true"
android:layout_width="fill_parent"
android:layout_height="530sp"/>
</RelativeLayout>
Part of FragmentActivity which sets up the AnswerArea
protected void setAnswerPad(AbstractAnswerFragment pad)
{
fragManager.beginTransaction()
.replace(R.id.AnswerArea, pad, "AnswerArea")
.commit();
fragManager.executePendingTransactions();
answerPadToAnswer=pad.getAnswerKey();
}
First time (From FragmentActivity) that the QuestionArea is set.
fragManager.beginTransaction()
.replace(R.id.QuestionArea, getQuestionPreView(), "QuestionArea")
.commit();
The functions getPostView() and getPreView()
#Override
Fragment getQuestionPreView() {
NumberDisplay display=(NumberDisplay)manager.findFragmentById(R.id.QuestionArea);
display.setNumber(-1);
return display;
}
#Override
Fragment getQuestionPostView() {
NumberDisplay display=(NumberDisplay)manager.findFragmentById(R.id.QuestionArea);
display.setNumber(answer);
return display;
}
Your problems, most likely, come from trying to change static fragments(fragments declared in the xml layout) at runtime, which you shouldn't do. If you plan to replace, remove etc those fragments in your FragmentActivity you should put instead a wrapper layout(like a FrameLayout) in their places in the xml layout and use that container for future fragment transactions. Check this answer from one of the Android engineers.
There were a few things that needed to be done to make this work:
The key thing was to change my main XML code to FrameLayout.
I had to create some of the smaller fragments, when I wasn't having to create them before.
New main XML code:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:orientation="vertical" >
<com.pearsonartphoto.FontFitTextView
android:id="#+id/PowerName"
style="#style/bigText"
android:background="#color/blue"
android:gravity="center"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"/>
<TextView android:id="#+id/Instructions"
style="#style/medText"
android:layout_width="fill_parent"
android:layout_below="#id/PowerName"
/>
<FrameLayout
android:gravity="center"
android:id="#+id/QuestionArea"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="#id/Instructions"/>
<FrameLayout
android:id="#+id/AnswerArea"
style="#style/bigText"
android:gravity="center"
android:layout_below="#+id/QuestionArea"
android:layout_alignParentBottom="true"
android:layout_width="fill_parent"
android:layout_height="530sp"/>
</RelativeLayout>