Android - Recycler view resizes when app is on background - android

I have this very weird bug on my app. Every time I open my app my view works perfectly, but when I try to put my app in background and open my app again, my recycler view resizes.
Here's an image to describe it properly:
This is the correct image:
This is the image that messes up my recyclerview when the app is on background, then I open it again.
There are also times that all images in my recyclerview are gone. Here's a screenshot:
This is my onResume() code:
public void onResume()
{
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
if (fragment instanceof MenuFragment)
{
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.remove(fragment);
transaction.commit();
transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, fragment);
transaction.addToBackStack(null);
transaction.commit();
}
}
This is my view pager layout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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"
android:background="#drawable/app_background">
<android.support.constraint.ConstraintLayout
android:id="#+id/action_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<include
android:id="#+id/nav_bar"
layout="#layout/home_screen_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
<com.yotadevices.widget.RtlViewPager
android:id="#+id/menuFragment_viewPager"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#android:color/transparent"
app:layout_constraintBottom_toTopOf="#+id/buttons"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/nav_bar"/>
<include
android:id="#+id/buttons"
layout="#layout/navigation_layout"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/menuFragment_viewPager" />
</android.support.constraint.ConstraintLayout>
Here's my list layout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
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/linearLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/transparent"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerview"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</android.support.constraint.ConstraintLayout>
What do you think is the problem here?
PS: The bug occurs only on selected phones. Not all. We've tested this on Samsung Galaxy s8+ and it works well.

I have solved the problem by breaking part the included layouts. So instead of adding an included layout, I just transferred all the included in to the main XML layout.
Even though this fixed the problem, i still do not know why the bug occurred in the first place.

If you are using only single child view, it's better to use FrameLayout. Then in RecyclerView use match_parent parameters in layout attribute, so your code should look like this:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
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/linearLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/transparent">
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
By the way my best method to debug scenarios like this is to write android:background="#0000FF" in every view, to see how it inflates.

You don't need to first remove the old fragment if you are going to call replace(R.id.container_id, newFragment);
You don't need to call commit after every change (remove(), replace(), etc.), call all your changes then end it with a commit()
If your activity did not lose/change its state (e.g. rotate), then you are just removing and adding the same fragment for no reason
You are checking if it is a MenuFragment in the if statement, then, oddly enough, if the condition holds, you go and re-add the MenuFragment, when you should probably replace it if it's not a MenuFragment
if (!(fragment instanceof MenuFragment)) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
Fragment menuFragment = new MenuFragment();
// pass any data...
transaction.replace(R.id.fragment_container, menuFragment);
transaction.addToBackStack(null);
transaction.commit();
}
Adding Log.d(TAG, "method name") in your onResume(), onPause() might help you know what's happening.
Also, checking the view hierarchy in Tools > Layout Inspector might also help.
Enabling Settings > Developer Options > Show Layout Bounds on your emulator might also help.
Let me know if I had anything wrong. Also let me know if any of this works for you.

To start off, declare your layouts in a more specific way rather than using root elements that are certainly not used.
Here's the list layout:
<android.support.v7.widget.RecyclerView
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"/>
Second, the onPause is mainly responsible for saving the app state automatically if you don't have much to implement. It is best to remove the onResume and let it take care of itself.

keep width of recyclerview as match_parent and height as wrap_content,
after that ,for your item layout,put height and width as a fixed value,such as 50dp or 70dp, which can keep the item's height and width same in any mode u want.
i hope this helps,if this answer is not helpful,please ignore this answer.

You must use a subclass of android.support.v4.app.FragmentStatePagerAdapter to load Fragments rather then putting code in on resume.
public class PagerAdapter extends FragmentStatePagerAdapter {
private static final int ITEMS = 4;
PagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int i) {
switch (i) {
case 0:
return Fragment1.newInstance();
case 1:
return Fragment2.newInstance();
case 2:
return Fragment3.newInstance();
case 3:
return Fragment4.newInstance();
}
return null;
}
#Override
public int getCount() {
return ITEMS;
}
}
The new instance method of each fragment must be:
public static Fragment1 newInstance() {
return new Fragment1 ();
}
In your activity write following in onCreate method:
PagerAdapter pagerAdapter = new PagerAdapter(getSupportFragmentManager());
//rtlViewPager -- object of RtlViewPager
rtlViewPager.setAdapter(pagerAdapter);
rtlViewPager.setOffscreenPageLimit(4);

Related

Change content_main XML in Basic Navigation Drawer Template

i am really new to android studio and struggle with a very basic problem.
I have seen a few threads regarding that issue but none really tacles my problem exactly.
Problem:
How am i able to change a fragment in the NavigationDrawer template? The template provides a environment in which the so-called content_main.xml should be changed to another fragment. I do not know how to accomblish that
What I tried: Since I was not able to change the content_main.xml I changed the whole xml, which includes the content_main.xml. It is easier understandble if you just have a short look at my code.
Template app_bar_main.xml that sets up how the fragments are suppose to look and includes content_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
android:id="#+id/content_main_frame"
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=".MainActivity"
tools:showIn="#layout/app_bar_main"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</FrameLayout>
In the Navigation Drawer main_activity I change my fragment like this:
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
int id = item.getItemId();
Fragment newFragment;
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
if (id == ..) {
} else if (id == R.id.nav_emergencies) {
transaction.replace(R.id.content_main_frame, new Drawer_Emergencies());
transaction.addToBackStack(null);
transaction.commit();
}}
As you might see the transaction replaces the whole app_bar_main but i think i should just change the content_main.xml that is included in app_bar_main.xml. How do I accomblish that?
As I implemented the suggestion from Jude I got a updated result. The Fragment is at the right place, but somehow the content_main_frame.xml (hello world) is still shown.
maybe you should try to make a framelayout inside your content_main xml file.
then load the new fragment into the framelayout.this is like creating a frame for the new fragment.
then load it like:
transaction.add(R.id.yourframelayoutid, new Drawer_Emergencies()).commit();
From a Discussion with Mike.M I was able to solve my problem, whereas he provided the way. To make this thread closable I want to post the solution in here, citing Mike.M :
If you want to replace some Views with a FragmentTransaction, those Views should be in their own Fragment, one that you could load at startup, in the Activity's onCreate(). Your subsequent replace() transactions will then work as expected, provided that you're passing the same R.id for all of them; i.e., provided that you're swapping them out of the same ViewGroup. (Btw, please put # in front of my username when you'd like to ping me. When there's more than one other user in comments, we don't get notified unless you address it directly to a user.)
Which yields the following step-by-step solution:
I have to make empty Framelayout, e.g. make my content_main.xml empty.
Make a seperate Fragment for the inital content and load that in content_main.xml at the start..
After opening a new drawer I the fragment container, which is my Framelayout with a new fragment..
You can do something like this:
content_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
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"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
android:id="#+id/content_frame">
</FrameLayout>
and then
transaction.replace(R.id.content_frame, new Drawer_Emergencies());

Determine Which Layout Application is Currently Using

I have 2 Fragments and a Main Activity. An Item from a List in Fragment A is Clicked, the id is retrieved in the Main Activity and the 2nd Fragment replaces the First. This happens in the following interface method within Main Activity:
#Override
public void onCLicked(int id) {
//Launch Fragment B/Pass id to it
}
That works well on a phone screen.
I now made a 2nd layout for a Tablet where the Fragments are side by side. My new layout is in my resource layout-sw720-land folder and looks like this:
<android.support.constraint.ConstraintLayout
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"
xmlns:app="http://schemas.android.com/apk/res-auto">
<FrameLayout
android:id="#+id/container"
android:layout_width="600dp"
android:layout_height="0dp"
tools:context="com.markf.popularmovies.activities.MainActivity"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent">
</FrameLayout>
<FrameLayout
android:id="#+id/containerDetail"
android:layout_width="600dp"
android:layout_height="0dp"
tools:context="com.markf.popularmovies.activities.MainActivity"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="#id/container"
app:layout_constraintRight_toRightOf="parent">
</FrameLayout>
</android.support.constraint.ConstraintLayout>
This layout launches when I'm using my tablet but when I click on an item, Fragment A is replaced with Fragment B as if it was still on the phone. Is there a way to do determine which layout Im in programmatically so I could code this appropriately? I tried reading this solution:Android Developer:Communicating with Other Fragments
but no luck
A simple way to do this would be to check for the existence of a View that's unique to one of the layouts. In this case, since the FrameLayout with ID containerDetail is only in the tablet layout, if findViewById(R.id.containerDetail) returns null, then you're in the phone layout.
For example:
if (findViewById(R.id.containerDetail) == null) {
// Phone layout
}
else {
// Tablet layout
}

How can I declare a Fragment as hidden in an XML layout

My activity declares all of its GUI fragments in a single XML layout. It only needs to display a few of the fragments at launch time; the rest get shown as the user interacts with the app. A portion of the layout is as follows:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<fragment
android:id="#+id/map_panel"
android:name="com.example.MapPanel"
android:layout_width="match_parent"
android:layout_height="#dimen/map_panel_height" />
<fragment
android:id="#+id/list_panel"
android:name="com.example.ListPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/map_panel" />
<fragment
android:id="#+id/detail_panel"
android:name="com.example.DetailPanel"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/map_panel"
android:visibility="gone" />
My intention is that the list_panel fragment is visible at startup, and the detail_panel fragment is hidden until the user selects something from the list.
By default, a fragment starts out with the isHidden attribute as false. That means my activity has to iterate through the loaded fragments and manually call isHidden(true) on fragments like detail_panel at startup time.
I would prefer to declare the isHidden status in the XML layout. However, setting android:visibility="gone" in a <fragment> declaration does not change the isHidden status, and I can't find any documentation on another attribute that would do the trick.
Is it possible to set an XML attribute on a <fragment> to cause it to be hidden?
Note: I'm not concerned with view visibility, I'm concerned with the fragment.isHidden() value. That affects how FragmentManager manipulates the back stack and performs animations. If you call transaction.show(fragment) on a fragment whose view is invisible or gone, but the fragment.isHidden() value is false, then the FragmentManager will not make the view visible. See http://developer.android.com/reference/android/app/Fragment.html#isHidden() for reference.
I faced a similar situation, where I had to hide a fragment.
I simply included the fragment inside a LinearLayout and marked the layout to be visible/gone.
<LinearLayout
android:id="#+id/map_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible" >
<fragment
android:id="#+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.MapFragment" />
</LinearLayout>
Based off Jyo's post, use this:
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.hide(mFragment);
fragmentTransaction.commit();
This has worked for me on API Level 23. mFragment is the fragment that you want to hide.
This answer is a tad late thought it may be helpful for future reference. Visibility is part of the View class - Fragment extends object though not having access to the visibility values. A possibility is making the Fragment a child of a FrameLayout and calling invisible or gone on the layout. This will cause the fragment to appear to be hidden.
Hope it helps!
public void showHideFrgament(final Fragment fragment){
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.setCustomAnimations(android.R.animator.fade_in,
android.R.animator.fade_out);
if (fragment.isHidden()) {
ft.show(fragment);
Log.d("hidden","Show");
} else {
ft.hide(fragment);
Log.d("Shown","Hide");
}
ft.commit();
}
we have isVisible Method for fragment
seeting visibilty to Gone does not take any space
Where as Invisble takes the actual view space.

How to center a dialog within a fragment?

In a scenario where I've got multiple fragments on display (e.g. list fragment and detail fragment), and one of the fragments is showing an alert dialog (a DialogFrament), I'd like the dialog to be centered within the fragment that is showing it, not centered within the whole screen. Is there a way to do that without a whole lot of pixel calculations involving the dimensions of the screen, the dimensions and position of the fragment, etc.?
You can do this by using a FrameLayout, as Joe Simpson mentions. Say you want to put your dialog fragment over the top of the Details fragment that you mention. Put a FrameLayout around the details fragment in your activity layout, then embed the dialog fragment within the FrameLayout too (with layout_gravity="center"). Set the visibility="gone" until you need it. E.g.:
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >
<fragment
android:id="#+id/my_details_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.my.fragment.DetailsFragment" />
<fragment
android:id="#+id/my_dialog_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.my.fragment.DialogFragment"
android:layout_gravity="center" />
</FrameLayout>
You can also start it programmatically. Put a holder view (e.g. a LinearLayout) in the xml:
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >
<fragment
android:id="#+id/my_details_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.my.fragment.DetailsFragment" />
<LinearLayout
android:id="#+id/myfragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
</FrameLayout>
... and then replace it in code like so:
final MyDialogFragment dialog = MyDialogFragment.getInstance();
// get an instance of FragmentTransaction from your Activity
FragmentTransaction fragmentTransaction = getSupportFragmentManager()
.beginTransaction();
fragmentTransaction.add(R.id.myfragment, dialog);
fragmentTransaction.commit();
Note that the docs on DialogFragment say it's fine to embed it like this: "A DialogFragment can still optionally be used as a normal fragment, if desired. This is useful if you have a fragment that in some cases should be shown as a dialog and others embedded in a larger UI."

clarification on static vs dynamic fragment

Good day, trying to clarify this: if i have this layout for a portrait layout
main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<fragment
android:id="#+id/configFragment_id"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.example.settingsFragment">
</fragment>
and this for landscape(mainly for tablets), main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<fragment
android:id="#+id/configFragment_id"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.example.SettingsFragment" ></fragment>
<fragment
android:id="#+id/detailFragment_id"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.Example.DetailFragment" >
<!-- Preview: layout=#layout/details -->
</fragment>
</LinearLayout>
now within my SettingFragment, i implement a button to go to the DetailFragment:
DetailFragment fragment = (DetailFragment)getActivity().getSupportFragmentManager().findFragmentById(R.id.detailFragment_id);
FragmentTransaction fragmentTransaction = getActivity().getSupportFragmentManager().beginTransaction();
if (fragment != null && fragment.isInLayout()) {
DetailFragment detailfragment = new WallpaperFragment();
fragmentTransaction.add(R.id.detailFragment_id, detailfragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}else{
Intent intent = new Intent(getActivity().getApplicationContext(),DetailActivity.class);
startActivity(intent);
}
Am i now implementing a dynamic fragment or am i still using static fragment?.. or do i only use dynamic fragment when i use a viewgroup like FrameLayout in place of the fragment tag element?
Idea is to have a single pane for phones and multi-pane for tablets.I know its a bad idea to mix static and dynamic fragments, but its kind of confusing. Thank you.
You know, I might be wrong here (so feel free to correct me) but it looks like you're using both static AND dynamic fragments. In your XML layouts you've added the fragments then you are re-instancing them via a fragment transaction in your activity. Alternatively, if you had declared a container (FrameLayout for instance) instead of a fragment in your XML you would have to use a FragmentTransaction to add the fragment at run time.
In your example you are just stepping on the fragment that you all ready have. Basically, the OS sees your fragment in the XML when inflating (I think that's when it calls the fragment code?) and executes the code associated with it via the tag in the fragment. Your app then adds that same fragment over the top of itself. A simple way to show this is to add a LogCat call in your Fragment class's onCreateView method. If you see it more than once, then your stepping on the previous fragment with the same fragment (which I'm 99% sure you are). Hope this helps to answer your question!
Static fragments are defined in the layout and are not generally added or removed at runtime. They are referenced by their id in your code. They are generally put as a child element of a layout like below. Once they are defined here, android will know to make a fragment, that's all you have to do.
<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"
android:weightSum="1">
<fragment
android:id="#+id/fooFragment"
android:name="com.example.myapplication.TestFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
Dynamic fragments are defined in your code and can be manipulated, added, removed, etc during runtime. They look like this:
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.your_placeholder, new TestFragment());
ft.commit();

Categories

Resources