I would like to override the back button in an activity so that I can start a fragment, normally within activities I use an intent but as I understand I cannot use an intent to call fragments.
The reason I want to use the back button to start the intent is so it can reload new information from the database, otherwise, if I don't override the back button it will just load the previous fragment with old (cached) data.
My code below which does not work:
#Override
public void onBackPressed(){
handler.removeCallbacksAndMessages(null);
new leftRoom().execute();
Intent intent = new Intent(ActivityChatRoom.this,Fragment_01_Rooms.class);
startActivity(intent);
}
Update:
fragment_01_rooms.xml:
<?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:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- android:background="#color/gray_header"-->
<ListView
android:id="#+id/listView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:fadeScrollbars="false" >
</ListView>
</LinearLayout>
Update (activity_main.xml):
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/viewpager"
tools:context=".FragmentMain"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
Try this:
#Override
public void onBackPressed(){
handler.removeCallbacksAndMessages(null);
new leftRoom().execute();
getSupportFragmentManager().beginTransaction().replace(R.id.viewpager, new Fragment_01_Rooms()).commit();
}
You would have to add a FrameLayout as the container in the activity if you don't already have a container view in which to inflate the fragment. Let me know if you need more clarifications.
EDIT
Thanks a lot for the full activity code. Since you are using a ViewPager, replacing a fragment this way won't work. Now, you mentioned two lines in the onBackPressed you wanted to implement:
handler.removeCallbacksAndMessages(null);
new leftRoom().execute();
What is the role of these two methods and where are they implemented? If these methods update your db, and if Fragment_01_Rooms is the leftmost element in your viewPager, I would suggest you to do this:
In the onResume of your Fragment_01_Rooms, refresh the data that you are going to show the user (if the two lines above already refresh the data, you don't need to do anything).
In the Activity, override onBackPressed and call viewPager.setCurrentItem(0) instead of getSupportFragmentManager...commit().
Let me know if this solves the problem.
#Override
public void onBackPressed(){
handler.removeCallbacksAndMessages(null);
new leftRoom().execute();
}
In your leftRoom AsyncTask onPostExecute call this.
getSupportFragmentManager().beginTransaction().replace(R.id.container, new Fragment_01_Rooms()).commit();
Related
I have an SchoolActivity that has two buttons:
-Primary (adds the PrimaryFragment)
-Secondary (adds the SecondaryFragment)
<?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.yuv.mycollege.MainActivity">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Button
android:id="#+id/button_primary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Primary"/>
<Button
android:id="#+id/button_secondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Secondary"/>
</LinearLayout>
<!-- Main content area for fragments-->
<FrameLayout
android:background="#color/colorPrimary"
android:id="#+id/main_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:padding="4dp"/>
</LinearLayout>
Both the fragments has content and footer area, which are themselves are fragments (PrimaryContentFragment, PrimaryFooterFragment, SecondaryContentFragment, SecondaryFooterFragment)
I am adding the fragments from the activity using:
public void onClick(View view) {
Button button = (Button)view;
Toast.makeText(this, "Going to Add Children", Toast.LENGTH_SHORT).show();
switch(view.getId()) {
case R.id.button_primary:
getSupportFragmentManager().beginTransaction()
.replace(R.id.main_container, new PrimaryFragment())
.addToBackStack("primary")
.commit();
break;
case R.id.button_secondary:
getSupportFragmentManager().beginTransaction()
.replace(R.id.main_container, new SecondaryFragment())
.addToBackStack("secondary")
.commit();
break;
}
}
And, finally adding the each children fragments using:
The layout file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/content_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="#color/colorAccent"
android:layout_weight="8"
></FrameLayout>
<FrameLayout
android:id="#+id/footer_container"
android:background="#color/colorPrimaryDark"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="2"
></FrameLayout>
</LinearLayout>
The children fragments adding:
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.primary_fragment, container, false);
getChildFragmentManager()
.beginTransaction()
.add(R.id.content_container, new PrimaryContentFragment())
.add(R.id.footer_container, new PrimaryFooterFragment())
.addToBackStack("primarychildren")
.commit();
return view;
}
I am adding the similar logics for the another fragment also and so on for the rest which is working.
THE PROBLEM:
The solution as stated is working but seems as very raw naive approach. Could anybody suggest the better architecture I could follow such as:
all the fragments (Primary/Secondary/...) uses the same designs so
can I create some base class to inherit common features
all the footer are similar for all of fragments but with simple text change
(might be like breadcrumb) So, Can I use same footer for all
fragments with some settext method ...
how can I effectively communicate between activity and fragments
BEING A NEW ANDROID DEV, MY ONLY CONCERN IS AM I DOING THE RIGHT WAY !!!
Try this using bundle :-
ContentFragment content=new ContentFragment();
content.setArguments( ( new Bundle()).putString("value","primary"));
getChildFragmentManager()
.beginTransaction()
.add(R.id.content_container, content)
.add(R.id.footer_container, new PrimaryFooterFragment())
.addToBackStack("primarychildren")
.commit();
Similarly For secondary use :-
ContentFragment content=new ContentFragment();
content.setArguments( ( new Bundle()).putString("value","secondary"));
Use same content and footer fragments just set the texts by using bundle arguments
All the fragments (Primary/Secondary/...) uses the same designs so can
I create some base class to inherit common features
If they are using the same design and with slight changes in their content, then there's no need of create different Fragment classes. You can just pass necessary values from your calling Activity or Fragment to populate the contents in each of your child fragments.
All the footer are similar for all of fragments but with simple text
change (might be like breadcrumb) So, Can I use same footer for all
fragments with some settext method ...
If they are just simple text changes, then please use the setArguments and getArguments methods to pass values between Fragments rather creating different Fragment classes.
Here's how you can pass values between fragments. And here's how you can pass data from your Activity to Fragment.
How can I effectively communicate between activity and fragments
Please follow the two links above to communicate between Activity and Fragment.
Update
Following up the comment, as you have said that the PrimaryFragment and SecondaryFragment are mostly likely, I would suggest you to have one single Fragment having all these. Instead of having PrimaryFragment, SecondaryFragment and a CommonFragment, you might consider a single Fragment having the footer Fragment as well. When you are about to launch an instance of that Fragment, just pass necessary values to populate data as the contents of those Fragment.
Please let me know if I have not clarified enough.
I have a list view that is populated through a string-array in the xml, not run time and I'm trying to set the background color of specific items in the list using:
listView.getChildAt(x).setBackgroundColor(Color.BLACK);
I need this to happen before it's visible, but it gives an error when I use it in onCreate() or onStart(), but works if I run it on a button press. I've tried searching for an answer but can't seem to find any event that happens late enough for it to work.
getView() would be the best place to do this, but you don't have access with the way you are doing this with android:entries=.... Instead, you can post a runnable to the message queue to change the color after layout occurs like the following:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.layout).post(new Runnable() {
#Override
public void run() {
findViewById(R.id.view).setBackgroundResource(android.R.color.holo_red_dark);
}
});
}
Here I have used a simple View for demonstration but the same technique should work for your ListView.
Here is the XML I used if you want to work with it:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<View
android:id="#+id/view"
android:layout_width="100dp"
android:layout_height="100dp"
android:background="#android:color/holo_blue_light" />
</LinearLayout>
Here's a link to post. The runnable kicks off before onStart(). So, if that's a requirement, then this way may not work for you.
I have a two variants of activity_main.xml (for portrait/landscape orientation). In this activity, user can choose items and browse detailed information about selected item.
In portraint orientation, fragments added to flFragmentContainer dynamically, after item choosing, details fragment replaces list fragment:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/flFragmentContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"></FrameLayout>
</LinearLayout>
In landscape orientation, fragments described in XML file statically:
<?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:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<fragment
android:id="#+id/frgTaskList"
android:name="com.exprod.xchecklist.fragments.TaskListFragment"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
tools:layout="#layout/fragment_task_list" />
<fragment
android:id="#+id/frgTaskDetails"
android:name="com.exprod.xchecklist.fragments.TaskDetailsFragment"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
tools:layout="#layout/fragment_task_details">
</fragment>
</LinearLayout>
</LinearLayout>
Here is the code of onCreate() method (of MainActivity).
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fragmentManager = getSupportFragmentManager();
TaskDetailsFragment taskDetailsFragment = (TaskDetailsFragment)fragmentManager.findFragmentById(R.id.frgTaskDetails);
isDynamicMode = taskDetailsFragment == null || !taskDetailsFragment.isInLayout();
if(isDynamicMode){
FragmentTransaction transaction = fragmentManager.beginTransaction();
Fragment taskListFragment = new TaskListFragment();
if (savedInstanceState == null) {
transaction.add(R.id.flFragmentContainer, taskListFragment, TaskListFragment.FRAGMENT_TAG);
}else{
transaction.replace(R.id.flFragmentContainer, taskListFragment, TaskListFragment.FRAGMENT_TAG);
}
transaction.commit();
}
}
PROBLEM: When I rotate device to landscape orientation, I get twice calls of creation methods in first fragment (TaskListFragment) (onCreateView(), onActivityCreated(), ... ). This indicates that the old fragment remains in activity and recreated on orientation change.
How I can finally destroy old fragment?? I did not find the answer on the Internet.
P.S: Sorry for my bad English...
I see at least two approaches to do what you want.
Handle orientation change yourself:
As described in this document, you can prevent automatic Activity destroy and re-creation and handle orientation change yourself. In this case, you would remove the old Fragment before inflating a new layout into Activity.
Remove the old Fragment after Activity re-creation:
You could also add some logic that determines the orientation as described here (or use your current heuristic that sets isDynamicMode), and manually remove the unnecessary Fragments during onCreate().
The better way:
Although it is possible to do what you want, I can't undertand why would you like to do it this way - you use TaskListFragment in both portrait and landscape configurations, therefore the most logical thing to do is to reuse it. Except for simplifying things, it will also let you keep the state of that Fragment on orientation change (which, I believe, is desirable in your case).
Thanks for Vasiliy answer. I follow this link
and add
android:configChanges="oritentation"
to my manifest file:
<activity android:name=".MyActivity"
android:configChanges="orientation"
android:label="#string/app_name">
and the annoying behavior (fragment not attached to activity) when screen is rotated disappears
Just add to your activity
#Override
public void onSaveInstanceState(Bundle outState) {}
because implementation of this method in Android's Activity that is a parent of all Activities, saved Fragment.
I am using webview to create a basic web browser,
Currently I have one activity and multiple fragments :
-- Browser
-- something else
-- Something else
in Browser Fragment I have the webview :
<LinearLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:context=".fragments.BrowserFragment">
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/swipe_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:id="#+id/webview_holder"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true">
<include
android:id="#+id/button_layout"
layout="#layout/bottom_btn_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"/>
<LinearLayout
android:id="#+id/web_page_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#id/button_layout"
android:orientation="vertical">
</LinearLayout>
<WebView
android:id="#+id/web_page"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#id/button_layout"/>
</RelativeLayout>
</android.support.v4.widget.SwipeRefreshLayout>
</LinearLayout>
I am able to save the state of the webview on screen rotation , by using
#Override
public void onSaveInstanceState(Bundle outState) {
if(mWebView != null){
mWebView.saveState(outState);
}
super.onSaveInstanceState(outState);
}
and calling this OnActivityCreated :
mWebView = (WebView)getActivity().findViewById(R.id.web_page);
if (savedInstanceState != null){
mWebView.restoreState(savedInstanceState);
}else{
mWebView.loadUrl(makeUrl(url));
}
This works fine when screen rotates and activity is recreated.
But when I use navigation drawer and replace fragment in the same container and then try to come back to same fragment . The state of webview is lost.
I get the point that onSaveInstanceState() is not called when there is Fragment transaction. So I tried saving the webView state by calling onSaveInstanceState() from OnPause of the fragment. But in that case I get savedInstanceState as null in OnCreateView so I am not able to restore state.
I need to maintain state of multiple webviews and comeBack to them on click of a list.
I also tried saving webview, when onSave() is called in fragment, in a static HashMap in activity and try to assign it to webview of newFragment. But I get Blank screen in that case.
Could someone give me pointers how I can approach this.
In the following code, the fragments are being dynamically inflated from a layout xml. The issue i am facing is that I dont see the fragments when i rotate the device.
If I look at the fragmentManager, I do see fragments are there. They are added and attached, but no where on the screen.
MenuCard extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.content);
leftContainer = (FrameLayout) findViewById(R.id.leftContainer);
rightContainer = (FrameLayout) findViewById(R.id.rightContainer);
Fragment fragment = mFragmentManager.findFragmentById(R.id.leftFragment);
if (fragment == null) {
LayoutInflater.from(this).inflate(R.layout.left_fragment, leftContainer);
}
fragment = mFragmentManager.findFragmentById(R.id.rightFragment);
if (fragment == null) {
LayoutInflater.from(this).inflate(R.layout.right_fragment, rightContainer);
}
}
}
}
}
R.layout.left_fragment
<?xml version="1.0" encoding="utf-8"?>
<fragment 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:id="#+id/leftFragment"
android:name="com.example.LeftFragment"
tools:layout="#layout/left_fragment">
</fragment>
R.layout.right_fragment
<?xml version="1.0" encoding="utf-8"?>
<fragment 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:id="#+id/rightFragment"
android:name="com.example.LeftFragment"
tools:layout="#layout/left_fragment">
</fragment>
Adding the code for R.layout.content
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SlidingPaneLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/spl"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/leftContainer"
android:layout_width="320dp"
android:layout_height="match_parent"
android:background="#00AFF0"
/>
<FrameLayout
android:id="#+id/rightContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</android.support.v4.widget.SlidingPaneLayout>
you can try to add following lines in your manifest inside fragment Activity's declaration:
android:configChanges="screenSize|orientation"
This will prevent onCreate on screen orientation change.For handling of orientation change, you can override onConfigurationChanges.
So after a lot of reading of the fragment manager code i came across these magical lines:
// For fragments that are created from a layout, when restoring from
// state we don't want to allow them to be created until they are
// being reloaded from the layout.
which means the correct code for my case is
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.content);
leftContainer = (FrameLayout) findViewById(R.id.leftContainer);
rightContainer = (FrameLayout) findViewById(R.id.rightContainer);
LayoutInflater.from(this).inflate(R.layout.left_fragment, leftContainer);
LayoutInflater.from(this).inflate(R.layout.right_fragment, rightContainer);
}
}
SupportFragmentManager will reuse the fragments in case of orientation change and only inflate the layouts.
I never tried your design but I find it interesting. Although I don't recommend it.
The UI is not recreated because orientation is changed. This is because Activity is not recreated, hence onCreate is not triggered when orientation is changed. When the screen is changed due to different factors, onCreateView is triggered. This I never tried in the Activity subclass. It is normally done in a Fragment subclass. Actually, onCreate is only triggered when the App is stopped due to user action or a crash.
If/when I find a good sample of onCreateView method in Activity, I'll post it.