I have an activity with two fragments like this image :
status A
Fragment B is a ListFragment. When user clicks on one row in fragment B. I have show detail. And the UI should like this:
status B
How can I move and resize fragment B?
I tried this way but don't know whether it is a correct way or hacky way.
I set this layout for the activity.
<LinearLayout
android:id="#+id/linearLayoutMainPart"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#+id/scrollViewBottomMenu"
android:layout_toRightOf="#+id/scrollViewLeftMenu"
android:background="#android:color/white"
android:orientation="horizontal" >
<FrameLayout
android:id="#+id/leftContainer"
android:layout_width="wrap_content"
android:layout_height="match_parent" >
</FrameLayout>
<FrameLayout
android:id="#+id/rightContainer"
android:layout_width="0dip"
android:layout_height="match_parent">
</FrameLayout>
</LinearLayout>
I create 3 fragment A, B, C class. When the activity create I add two fragment A, B to leftContainer and rightContainer.
(fragment A layout width is fixed). When user click on one row in fragment B. I replace fragment A by another instance of fragment B (with different layout, and using the same data with the old fragment B instance). And replace fragment B in rightContainer by an instance of fragment C.
In fragment B class. I have flag to choose layout when onCreateView method is called, something like :
if(rightPosition)
return B_right_pos_layout;
else
return B_left_pos_layout;
I think I can use the same instance of Fragment B. By doing like this :
FragmentB fragmentB = (FragmentB) getSupportFragmentManager().findFragmentByTag(FRAGMENT_B_TAG);
fragmentB.setRightPos(false);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(rightContainer, fragmentC);
transaction.replace(leftContainer, fragmentB);
transaction.commit();
I think this way is more like a hack way. What is the best way?
Related
I am beginner in Android development. I am creating a small application in which i call two fragment from single activity.
Activity -> fragment 1->fragment 2.
Activity to fragment 1, fragment 1 to fragment 2.
I want to know how i directly call fragment 2 to Activity directly.
I give button in Fragment 2, on click of that button I want go in Activity.
The activity already exists. Your activity is the one hosting the fragments i.e assuming all are full screen fragments, when you call fragment1, your activity removes the present fragment (if there is any) and replaces it with fragment1, when you call fragment2, fragment1 is replaced with fragment2 and so on.
If you want to see only the layout of the activity (which in most cases would be just a white screen), you have to remove all fragments, to do that, add this to your button's onClick:
getActivity().getFragmentManager().popBackStack(1, FragmentManager.POP_BACK_STACK_INCLUSIVE);
By your question I notice that you still need to read about fragments.
you can't go to an activity from a fragment because the fragments
are a section of an activity, which has its own lifecycle, receives its own input events, you can add or remove them while the activity is running (sort of like a "sub activity" that you can reuse in different activities).
Although you can replace a fragment and use a different one on the same activity.
you can do so like this:
First in main xml use a layout that your going to inflate:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="#+id/frgContainer"
android:layout_margin="20dp"
android:background="#00e6ff">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/btn"
android:text="btn"
/>
</LinearLayout>
Create 2 new activities which will be our fragments with an xml files you can add anything you wish to it:
<?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="vertical"
tools:context="com.example.hackeru.mydynamicfragment.Login">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="User Name"
android:id="#+id/txtLoginUser"
android:layout_marginLeft="20sp"
android:layout_marginRight="20sp"
android:layout_marginTop="80dp"
/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Password"
android:id="#+id/txtLoginPass"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/btnLogin"
android:text="Login"
/>
</LinearLayout>
Override onCreate method on the fragment
public class Login extends Fragment {
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.activity_login,container,false);
}
4.use fragmentTransaction in the onClick method in main to replace or add current layout with the fragment you created:
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
FragmentManager fragmentManager =getFragmentManager();
// we must handle the callback fragment process
FragmentTransaction fragmentTransaction =
fragmentManager.beginTransaction();
Login loginFragment = new Login();
fragmentTransaction.add(R.id.frgContainer,loginFragment);
// fragmentTransaction.replace if there is another fragment you
// wish to replace
fragmentTransaction.commit();
}
read this:
https://developer.android.com/guide/components/fragments.html
There is one Activity, called MainActivity, with layout file below (only two FrameLayout as containers).
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/tabletux_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<FrameLayout
android:id="#+id/tabletux_container1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"></FrameLayout>
<FrameLayout
android:id="#+id/tabletux_container2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"></FrameLayout>
</LinearLayout>
And two fragments: A and B.
If I commit a transaction (say, transactionA) to "start" fragment A,
getFragmentManager().beginTransaction().
replace(R.id.tabletux_container1, displayFragment,
GeneralConstants.DISPLAY_FRAGMENT_FRAGMENTTRANSACTION_TAG).commit();
And then in fragment A (after transactionA), I commited another transaction (say, transactionB) to "start" fragment B.
// This is committed in DisplayFragment, not in MainActivity
getFragmentManager().beginTransaction()
.replace(R.id.tabletux_container2, detailFragment,
GeneralConstants.DETAIL_FRAGMENT_FRAGMENTTRANSACTION_TAG).commit();
How am I supposed to retrieve fragment B when trying to save the InstanceState of fragment B in MainActivity, especially after twice or more times of rotations of the tablet?
Further Questions:
1. What's the relationship between transactionA and transactionB?
2. Let me make a wild deduction: transactionA and transactionB are in a "transactions pool", so that all fragments committed by any of them can be retrieved from the "pool" by id or tag. If I am wrong, please help correct.
3. If transactions are separate and have no relations with each other, how am I able to find the fragment in the specific transaction? How can I locate the transaction?
What I have tried on method onSaveInstanceState(Bundle outState) in MainActivity:
detailFragment = (DetailFragment) getFragmentManager()
.findFragmentByTag(GeneralConstants.DETAIL_FRAGMENT_FRAGMENTTRANSACTION_TAG);
But this returns null. But the fragment "started" in the transaction committed in the MainActivity can be located via its tag.
For your questions, Please refer below link :
http://developer.android.com/guide/components/fragments.html
According to this guide :
Each fragment transaction is a set of changes that you want to perform at the same time. You can set up all the changes you want to perform for a given transaction using methods such as add(), remove(), and replace().
Then, to apply the transaction to the activity, you must call commit().
So you need to change your code as below :
getFragmentManager().beginTransaction().
replace(R.id.tabletux_container1, displayFragment,
GeneralConstants.DISPLAY_FRAGMENT_FRAGMENTTRANSACTION_TAG)
.replace(R.id.tabletux_container2, detailFragment,
GeneralConstants.DETAIL_FRAGMENT_FRAGMENTTRANSACTION_TAG).commit();
Thank you..!!
I found a tricky solution: in AndroidManifest.xml file, add the code below to the activity where the 2 fragments were committed
android:configChanges="orientation|screenSize"
The info provided by the 2 fragments will remain the same, but layout will be tuned according to screen size (For example: GridView will display more columns in horizontal view than in vertical view), which is exactly what I expect.
I could not explain how it works, nor do I know the limitation of such solution.
My android app has a registration flow setup with an Activity and I load the steps as fragments into the activity layout.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal"
tools:context="com.example.android.ui.RegisterActivity">
<LinearLayout
android:id="#+id/register_container"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"></LinearLayout>
</RelativeLayout>
Now each fragment class implements a public interface implemented in the RegisterActivity so that I know to load the next step fragment and I add the new fragment to the backstack
mFragmentTransaction.addToBackStack(mStepOne.TAG);
now it all works fine all the way through 4 steps and I can navigate back through the steps while retaining the inputted data in each fragment IF it stays in the same orientation (portait)
BUT once i change the orientation, the fragment views disappear
if I am up to step 3, I can still hit the back button and it will go back showing me the fragment that is meant to be there by viewing the backstack changed listener
mFragmentManager.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
if(mFragmentManager.getBackStackEntryCount() > 0) {
Log.d("BACK STACK", "TAG (name): " + mFragmentManager.getBackStackEntryAt( (mFragmentManager.getBackStackEntryCount() - 1)).getName());
}
}
});
The count and tags of the fragments is retained but not the views. The RegisterActivity onCreateView loads the terms fragment into the view and it is what remains in view when navigating back through the steps.
mFragmentManager = getSupportFragmentManager();
mFragmentTransaction = mFragmentManager.beginTransaction();
mTerms = new RegisterFragmentTerms();
mFragmentTransaction.add(R.id.register_container, mTerms);
mFragmentTransaction.commit();
It seems to me the views are loaded into the RegisterActivity on top of each other and cleared on orientation change. Is it possible to solve what I'm trying to do? or have I implemented it wrong? It's my first android app :)
Cheers
It seems to be caused by Activity recreation. Have you ever add android:configChanges="orientation|keyboardHidden|screenSize" in your AndroidManifest.xml to your Activity? , if you add that flag to you activity, it will not be destroyed when orientation changes and the fragments stack will not disappear.
I have the necessity to replace one starting fragment (I'll call it A) of an activity with two other fragments (B and C, in the "usual" list+viewer configuration). Currently I have a relative layout with two frame layouts acting as a placeholder for B and C:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<RadioGroup
android:id="#+id/radiogroup_navigation"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<!-- Some radiobuttons (not displayed for the sake of brevity) -->
</RadioGroup>
<FrameLayout
android:id="#+id/frame_list"
android:layout_width="100dp"
android:layout_height="fill_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentBottom="true"
android:layout_below="#id/radiogroup_navigation">
</FrameLayout>
<FrameLayout
android:id="#+id/frame_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_below="#id/radiogroup_navigation"
android:layout_toRightOf="#id/frame_list">
</FrameLayout>
When I need to display A, I just hide frame_list and add A to frame_view, and when I need to display B and C I set frame_list visible again and add the two fragments to each frame, in the same fragment transaction.
FragmentTransaction t = getSupportFragmentManager().beginTransaction();
t.remove(fragmentA);
t.add(R.id.frame_list, fragmentB);
t.add(R.id.frame_view, fragmentC);
t.addToBackStack(null);
t.commit();
In this way, when i press the back button, both C and B go away and I'm back to A fragment, but now frame_list is visible (and empty).
I am thinking to solve the problem in two possible ways:
overriding onBackPressed to hide the left frame if needed;
nesting B and C in another fragment;
But I also feel I'm probably looking at the problem in the wrong way, and maybe there's a cleaner design solution. Do you have any advice?
If I understand correctly, here is one solution:
Create two activities ActivityA and ActivityBC
Create another fragment with the radiogroup
Embed FragmentRadio into both ActivityA and ActivityBC
Have that fragment start new activities based on selection whilst finishing current activity
Make fields like this:
private static final String FRAGMENT_B_TAG = "fragmentB";
When you add the fragments, use the static Strings like tags:
t.add(R.id.frame_list, fragmentB, FRAGMENT_B_TAG);
t.add(R.id.frame_view, fragmentC, FRAGMENT_C_TAG);
In your activity, set up a listener, which will get triggered every time after you call addToBackStack(String). It will find out which fragment is currently visible and hide/show needed containers.
getSupportFragmentManager().addOnBackStackChangedListener(new OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
FragmentA fa = getSupportFragmentManager().findFragmentByTag(FRAGMENT_A_TAG);
FragmentB fb = getSupportFragmentManager().findFragmentByTag(FRAGMENT_B_TAG);
if (fa != null && fa.isVisible()) {
// Fragment A is visible, so hide the second container which is now empty
}
if (fb != null && fb.isVisible()) {
// Fragment B is visible, so show the second container
}
}
});
Notice that checking whether Fragment C is visible or not is not needed since when Fragment B is visible, Fragment C is always visible too.
This is an untested code, but I think it should work. Also, if you need any explanation, don't hesitate to ask.
Hope it helps.
I am implementing fragments for the first time so please help me.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment android:name="com.example.news.ArticleListFragment"
android:id="#+id/list"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
<fragment android:name="com.example.news.ArticleReaderFragment"
android:id="#+id/viewer"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="match_parent" />
</LinearLayout>
I want that fragment having the id as 'list' should remain constant but the the fragment having id 'viewer' should be able to call different classes.
(Note that the classes extend Activity.)
My question is simple: I have four classes(Extending ACTIVITY). I want to divide the screen into two parts. The left Side remains constant which contains the listview. On list view's click I want to open my Class(Extending ACTIVITY), but only in the right portion(remaining screen).
It is a basic question. You should start from here. And this topic can help you also.
Fragments are like seperate acticities, so unless u make the changes the action on one fragment will not affect the other fragments.
Assuming u have a listview on the left fragment, in its activity place a onItemClickListener.
For each itemclick switch the activity on the right fragment.
Sample Code for the OnItemClick Event
Fragment fragment=new activity1();
fragmentManager fm=getFragmentManager();
FragmentTransaction ft=fm.beginTransaction();
ft.replace(R.id.frame2,fragment);
ft.commit();
In the above code segment activity1 is the new class want to attach to the right fragment. R.id.frame2 is the id of the framelayout that is used with the right fragment.
According to the android documentation of fragments:
A Fragment represents a behavior or a portion of user interface in an Activity. You can combine multiple fragments in a single activity to build a multi-pane UI and reuse a fragment in multiple activities. You can think of a fragment as a modular section of an activity, which has its own lifecycle, receives its own input events, and which you can add or remove while the activity is running (sort of like a "sub activity" that you can reuse in different activities).
http://developer.android.com/guide/components/fragments.html
what i understood from your question is that you want to the content of your fragement viewer at run-time. a possible solution which i can suggest for this is:
Instead of your four classes extending Activity, extend Fragment, each having its own layout. Modify the main layout file to look something like this:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment android:name="com.example.news.ArticleListFragment"
android:id="#+id/list"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/viewer"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="match_parent" >
</FrameLayout>
</LinearLayout>
the FrameLayout will basically act as a container for your fragments, which you can dynamically load at run-time(by clicking the ListView). This tutorial will help you out with it:
http://developer.android.com/training/basics/fragments/fragment-ui.html
Hope my answer helps you in some way.
The Fragment class can be used many ways to achieve a wide variety of results. In its core, it represents a particular operation or interface that is running within a larger Activity. A Fragment is closely tied to the Activity it is in, and can not be used apart from one. Though Fragment defines its own lifecycle, that lifecycle is dependent on its activity: if the activity is stopped, no fragments inside of it can be started; when the activity is destroyed, all fragments will be destroyed.
MyFragment newFragment = new MyFragment();// MyFragment is a Fragment class
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.add(R.id.fra,newFragment, tag);
transaction.addToBackStack(null);
transaction.commit();
sample code
in sample code change
ft.add(android.R.id.content,fragTwo, "tag");
to
ft.add(R.id.fra,fragTwo, "tag");
and add some code in detail.java
public void onStart() {
// TODO Auto-generated method stub
tv.setText(data);
b.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
fm.beginTransaction();
Fragment fragTwo = new MyFragment();
//String tag = getActivity().GetFragmentID();
Fragment f= fm.findFragmentById(getId());
ft.replace(R.id.fra,fragTwo, "tag");
ft.hide(f);
ft.commit();
}
});
super.onStart();
}