I am trying to replace the fragment in Volley's onResponse(). It's getting replaced, but in a weird way.
The old frag is still visible while the new one isn't.
The old frag is non interactive & click events are being handled by
the new frag.
It's like the new frag is beneath the old frag.
If I try to replace the frag outside Volley's onResponse(), then everything works the way it should: the old frag goes away showing the new one.
getNetworkManager().jsonGETRequest(new NetworkManagerRequestData(getActivity(), this,
orderListUrl,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject jsonObject) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.main_frame, IdleFragment.newInstance());
transaction.commit();
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError volleyError) {
..
}
}, NetworkManager.getDefaultPolicyForNonTransactions(), false));
The fragments are being added dynamically using a FrameLayout. I have an activity with 5-6 frags. Everything is working beautifully with animations, except this transaction.
Here's the R.id.main_frame which is an empty FrameLayout.
<FrameLayout
android:id="#+id/main_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
..
/>
UPDATE
So I tried implementing the fragment transaction inside an AsyncTask
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.main_frame, IdleFragment.newInstance());
transaction.commit();
}
}.execute();
This has same behaviour. Normally transacting outside any async callbacks is working fine though.
Here is the layout of the frag i am trying to replace
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/swipe_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/pitch_grey"
android:clickable="true"
android:fillViewport="true">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
.....
</RelativeLayout>
</ScrollView>
</android.support.v4.widget.SwipeRefreshLayout>
The problem is the Swipe Refresh Layout. When you are trying to replace the fragment while its refreshing, then the previous fragment will freeze. This is an issue in the SwipeRefreshLayout itself.
You can wrap your swipeRefreshLayout inside FrameLayout. This might work.
<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">
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/swipe_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/pitch_grey"
android:clickable="true"
android:fillViewport="true">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
.....
</RelativeLayout>
</ScrollView>
</android.support.v4.widget.SwipeRefreshLayout>
</FrameLayout>
Ref: When switch fragment with SwipeRefreshLayout during refreshing, fragment freezes but actually still work
You cannot replace a fragment that is statically placed in an xml layout file. Is the idle fragment statically placed? You should create a container (e.g. a FrameLayout) in the layout and then add the fragment programatically using FragmentTransaction.
Also use an empty FrameLayout as fragment container. Avoid, using LinearLayout or RelativeLayout. I faced this problem once using LinearLayout and replaced LinearLayout with FrameLayout and it worked fine.
Another suggestion, avoid doing fragment transactions in asynchronous calls. It may cause IllegalState Exception. If you still want to do this, use commitAllowStateLoss instead of commit.
Related
I have some sort of architecture issue:
I have Activity in which I need to show one of 3 views (one view at one moment in time):
1. One view with stable AppBarLayout + another part of the screen is
NestedScrollView that needs to be in separate fragment.
2. One view with stable AppBarLayout + another part of screen need to be a fragment with two tabs to switch between them.
3. Connection lost view - is a fullscreen view without AppBar - just to show that there is no connection.
The upper part of AppBarLayout - I made. Looks like good, but I need runtime to switch Fragments depending on the response from Server.
I am sending a request to the server in onCreate method in MainActivity, and then in onDataLoadedFromServer callback, depending on response - I am deciding which fragment to create. But my application crashes, because fragments fields are not initialized properly - the view is not attached to fragment. When I am adding fragment in onCreate method - everything is working good, but at that time I don't have a response from server.
Where to place TabLayout? And where ViewPager? Do I need to put
TabLayout in main_activity.xml, and ViewPager in separate
fragment_2_layout. And then add it to FrameLayout (container for
fragment which is located in main_activity)?
Is it good practice to load some data from the server in the fragment? Or it's better to load data in Activity and then set it through the method
call to Fragment?
Please, provide some sort of pseudo code to understand logic. Thanks!.
<?xml version="1.0" encoding="utf-8"?>
<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:id="#+id/shop_final_root_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.lampa.letyshops.view.activity.ShopFinalActivity">
<android.support.design.widget.AppBarLayout>
<android.support.design.widget.CollapsingToolbarLayout>
<include
layout="#layout/collapsing_part" />
<android.support.v7.widget.Toolbar>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.v7.widget.Toolbar>
</android.support.design.widget.CollapsingToolbarLayout>
<LinearLayout
android:id="#+id/content_layout_dual_tabs">
<android.support.design.widget.TabLayout />
<android.support.v4.view.ViewPager/>
</LinearLayout>
</android.support.design.widget.AppBarLayout>
<FrameLayout
android:id="#+id/fragment_holder"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/white"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
<include layout="#layout/shop_final_no_connection_layout" />
</android.support.design.widget.CoordinatorLayout>
public MainActivity {
protected void onCreate(Bundle savedInstanceState) {
loadDataFromServer();
}
private void onDataLoaded(Object dto) {
if (showOneViewLayout) {
showOneViewLayout();
} else {
showTabViewLayout();
}
}
}
private void showOneViewLayout() {
f1 = Fragment1.newInstance(params);
showFragmentWithoutBackStack(R.id.fragment_holder, f1);
}
private void showTabViewLayout() {
f2 = Fragment2.newInstance();
showFragmentWithoutBackStack(R.id.fragment_holder, f2);
}
protected void showFragmentWithoutBackStack(int containerViewId, Fragment fragment) {
Fragment previousFragment = currentFragment;
Fragment currentFragment = fragment;
String fragmentTag = fragment.getClass().getSimpleName();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
if (previousFragment != null) {
fragmentTransaction.hide(previousFragment);
}
fragmentTransaction.add(containerViewId, fragment, fragmentTag)
.commitAllowingStateLoss();
}
Basically this question has been asked many of times here and here.Solution given is not working for me. In some corrent answers were not posted by user.
I want to have nested fragments. My scenario is as below :
I have one MainActivity, ProductEnterFrag, and ProductListFrag,ProductDetailFrag.
I have add the ProductEnterFrag to MainActivity dynamically by the FragmentManager. Like this :
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.container, new ProductEnterFrag());
ft.commit();
ProductEnter layout is as below :
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragContainer"
android:layout_width="match_parent"
android:layout_height="match_parent" />
I have added the ProductListFrag to ProductEnterFrag like this :
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
ProductListFrag(new ProductListFrag());
}
public void ProductListFrag(SherlockFragment frag) {
getChildFragmentManager().beginTransaction().add(R.id.fragContainer, frag).commit();
getChildFragmentManager().executePendingTransactions();`
}
ProductListFrag whose layout is, as below :
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="product1" />
</LinearLayout>
Now on click of button, I want to add a another fragment say ProductDetailFrag,which should be another child fragment.
When I am trying to add ProductDetailFrag from ProductListFrag like this :
new ProductEnterFrag().ProductListFrag(new ProductDetailsFrag());
It is throwing me : java.lang.IllegalStateException: Activity has been destroyed
I am new in nested fragments. Please help me where I am doing wrong. I have stuck with this problem from last 2 days.
I am very new to Android programming. Following is what I am doing:
I have an EditTextBox. And when it is in focus I would want to display a custom number pad. To implement this, following are the code snippets:
layout file:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/activity_twopane"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<FrameLayout android:id="#+id/fragmentContainer"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="3"/>
<FrameLayout android:id="#+id/keyPadFragmentContainer"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
In the Fragment class, defined a Callbacks interface which the parent activity has to implement
public interface Callbacks {
void onAnswerEditTextSelected(Fragment fragment);
}
Added a listener to the EditText
mAnswerEditText.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(!mNumberPadShown) {
mCallbacks.onAnswerEditTextSelected(QuizFragment.this);
}
});
In the Parent activity, I have the following:
#Override
public void onAnswerEditTextSelected(Fragment fragment) {
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
Fragment numFragment = NumberPadFragment.newInstance(fragment);
ft.add(R.id.keyPadFragmentContainer, numFragment);
ft.commit();
}
At runtime I get No view found for id 0x7f ...(classname:id/keyPadFragmentContainer) for fragment NumberPadFragment. Can you please tell me where I am going wrong.
Thank you
if it's just numbers that you need, there is already a way to do it without any customization . it's called inputType.
it can be done either in XML or programmatically.
example :
<EditText android:inputType="number" android:digits="0123456789."
.../>
for more buttons, you can create a custom keyboard, as shown here:
http://www.fampennings.nl/maarten/android/09keyboard/index.htm
http://tutorials-android.blogspot.co.il/2011/06/create-your-own-custom-keyboard-for.html
https://stackoverflow.com/a/14676866/878126
i'm pretty sure the API demos have a sample for it too.
findViewById(R.id.team_detail_container) is failing to find the view. Is my problem with the xml or in the way I am constructing the FragmentActivity? How do I solve this?
To support a customised list for a fragment in the constructor of my fragment activity I have replaced
setContentView(R.layout.activity_team_list);
with
frag=(TeamListFragment)getSupportFragmentManager().findFragmentById(android.R.id.content);
if (frag==null) {
frag=new TeamListFragment();
getSupportFragmentManager().beginTransaction().add(android.R.id.content, frag).commit();
So that my class now looks like this
public class TeamListActivity extends SherlockFragmentActivity implements
TeamListFragment.Callbacks {
private boolean mTwoPane;
private TeamListFragment frag=null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
frag=(TeamListFragment)getSupportFragmentManager().findFragmentById(android.R.id.content);
if (frag==null) {
frag=new TeamListFragment();
getSupportFragmentManager().beginTransaction().add(android.R.id.content, frag).commit();
}
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
if (findViewById(R.id.team_detail_container) != null) {
Log.i("####", "Team Detail Container has been found! Yaay!");
mTwoPane = true;
((TeamListFragment) getSupportFragmentManager().findFragmentById(
R.id.team_list)).setActivateOnItemClick(true);
}
}
The if (findViewById(R.id.team_detail_container) != null) { condition is never met despite the fact that team_detail_container exists in the xml file that is used
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/team_list_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:orientation="horizontal"
android:showDividers="middle"
tools:context=".TeamListActivity" >
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/team_list"
android:orientation="vertical"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:paddingLeft="8dp"
android:paddingRight="8dp">
<ListView
android:id="#android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:drawSelectorOnTop="false" />
<TextView android:id="#id/android:empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="No data"/>
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="1dp"
android:background="?android:attr/listDivider"
/>
<FrameLayout
android:id="#+id/team_detail_container"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="3" />
</LinearLayout>
I know that this particular layout xml is being used as changes I make to the xml file are reflected in the app.
I also know that the condition is never met as I never get the log message Log.i("####", "Team Detail Container has been found! Yaay!"); and the behaviour get is that when an item is selected the list view is replaced with the detail fragments instead of the detail fragments being displayed next to the list view because the item selected conditions are not being met
#Override
public void onItemSelected(int id) {
if (mTwoPane) {
// mTwoPane is never set! Why?
Bundle arguments = new Bundle();
arguments.putInt(TeamDetailFragment.ARG_ITEM_ID, id);
TeamDetailFragment fragment = new TeamDetailFragment();
fragment.setArguments(arguments);
getSupportFragmentManager().beginTransaction()
.replace(R.id.team_detail_container, fragment).commit();
} else {
// In single-pane mode, simply start the detail activity
// for the selected item ID.
Intent detailIntent = new Intent(this, TeamDetailSwipeActivity.class);
detailIntent.putExtra(TeamDetailFragment.ARG_ITEM_ID, id);
startActivity(detailIntent);
}
}
The fragment doesn't get added to your layout tree immediately after committing the transaction. You'll have to wait till onViewCreated on the fragment was called. You could probably access the view within the onStart-Method of your Activity, but it would probably be a better idea to keep that logic within the fragment itself. The Activity shouldn't be concerned with what views are contained in a fragment.
I think the issue is you're looking for the Fragment from the Activity, but since you're not using setContentView, the Activity doesn't actually have a View to look through. Try getting the view from the Fragment instead using the getView() method.
Ok, whenever I try to replace a fragment in my application, it only adds the fragment inside of the container the other fragment is, and leaves the current fragment. I've tried calling replace and referencing the view the contains the fragment, and by referencing the fragment itself. Neither of these work. I can add a fragment to a view with the fragment transaction manager, but even if I try to remove it after its been added, it doesn't work. Any help would be appreciated. Here are my files.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//Setup the actionabar. Use setDrawableBackground to set a background image for the actionbar.
final ActionBar actionbar = getActionBar();
actionbar.setDisplayShowTitleEnabled(false);
actionbar.setDisplayUseLogoEnabled(true);
actionbar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
actionbar.addTab(actionbar.newTab().setText(R.string.home_tab_text).setTabListener(this),true);
actionbar.addTab(actionbar.newTab().setText(R.string.insert_tab_text).setTabListener(this));
Fragment fragment = new insert_button_frag();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.button_fragment, fragment);
transaction.addToBackStack(null);
transaction.commit();
}
Here is the layout
<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:orientation="vertical"
android:id="#+id/button_fragment_container"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<fragment
android:name="com.bv.silveredittab.home_button_frag"
android:id="#+id/button_fragment"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<fragment
android:name="com.bv.silveredittab.quick_insert_frag"
android:id="#+id/quick_insert_frag"
android:layout_width="350dip"
android:layout_height="fill_parent" />
<fragment
android:name="com.bv.silveredittab.editor_frag"
android:id="#+id/editor_frag"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>
</LinearLayout>
And here is the fragment code
public class insert_button_frag extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
return inflater.inflate(R.layout.insert_buttons,container, false);
}
}
Like I have said. I have tried referencing the fragments parent view to replace it, and the fragment itself (by id) and still, it only adds the new fragment, inside the containing view the original fragment is in.
I solved this by using a placeholder in my Layout and then attaching my Fragment to it at runtime.
Like you, if I instantiated my Fragment within my xml layout then the contents would remain visible after replacing it with another Fragment at runtime.
The problem is this line:
transaction.replace(R.id.button_fragment, fragment);
replace has two overloaded forms. In both of them, the first argument is the container of the Fragment to be replaced, not the Fragment itself. So, in your case, you need to call
transaction.replace(R.id.button_fragment_container, fragment);
edit: I see in your question that you have tried both. I have tested and verified the behavior. This appears to be a bug in the FragmentTransaction API.
Edit2: not a bug after all. You simply cannot replace fragments added statically in a layout file. You can only replace those you have added programmatically ( Android: can't replace one fragment with another )
I had the same issue. Take a look at this link: Android Fragment Duplication
I wonder if the fact that you're passing the container into the inflater.inflate() method causes it to create the new fragment inside of the old one instead of a wholesale replace. I've been providing 'null' to my inflaters in the working version.
Here's the essentials from the version I have working...
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
View newFrag = new View(getActivity().getApplicationContext());
newFrag = inflater.inflate(R.id.frag, null);
return newFrag;
}
The google developer guide is quite confusing because it shows first to implement hard coded fragment in you xml. But if you really want to replace existing fragment with the new one, then you should add it(the first) at runtime.
What official site state is
FragmentTransaction replace (int containerViewId, Fragment fragment, String tag)
Replace an existing fragment that was added to a container. This is
essentially the same as calling remove(Fragment) for all currently
added fragments that were added with the same containerViewId and then
add(int, Fragment, String) with the same arguments given here.
You should noticed that it says that Replace an existing fragment that was added to container
So, your xml should look like
someActivity.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
...
tools:context="someActivity">
<ImageView
.../>
<LinearLayout
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="bottom"/>
</RelativeLayout>
And than in your activity do in OnCreate()
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
[...]
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, new FirstFragment).commit();
}
Than you may easily replace fragment with your animations and event add it to back stack in order to save its state.
private SecondFrag getSecondFrag(){
if(secondFrag == null)
secondFrag = new SecondFrag()
return secondFrag;
}
private void openRechargeFragment(){
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.setCustomAnimations(R.anim.show_frag, R.anim.hide_frag,
R.anim.show_frag, R.anim.hide_frag);
ft.replace(R.id.fragment_container, getSecondFrag(), "myTAG");
ft.addToBackStack(null);
ft.commit();
}