10

I have looked at questions related to my problem on SO but couldn't figure out what the problem is. Bear with me if this is a repost.

Here is what i am looking for:

Phone Portrait Mode

Phone Landscape Mode

The layout changes for phone are working fine as it involves the same views inside the fragment. The problem is when i try get the below layout in tablets.

Tablet Portrait Mode

Tablet Landscape Mode

My Layout Structure

activity_main.xml in portrait mode:

<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" >

        <include
            android:id="@+id/toolbar"
            layout="@layout/toolbar_with_spinner" />

        <FrameLayout
            android:id="@+id/fragment_container"
            android:layout_width="match_parent"
            android:layout_height="match_parent" >
        </FrameLayout>
    </LinearLayout>

    <ListView
        android:id="@+id/listview_drawer"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="@color/white"
        android:choiceMode="singleChoice"
        android:divider="@null"
        android:dividerHeight="0dp" />

</android.support.v4.widget.DrawerLayout>

activity_main.xml in landscape mode:

<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" >

        <include
            android:id="@+id/toolbar"
            layout="@layout/toolbar_with_spinner" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:baselineAligned="false"
            android:orientation="horizontal" >

            <FrameLayout
                android:id="@+id/fragment_container"
                android:layout_width="0dip"
                android:layout_height="match_parent"
                android:layout_weight="0.40" />

            <FrameLayout
                android:id="@+id/detail_fragment_container"
                android:layout_width="0dip"
                android:layout_height="match_parent"
                android:layout_weight="0.60" />
        </LinearLayout>
    </LinearLayout>

    <ListView
        android:id="@+id/listview_drawer"
        android:layout_width="240dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="@color/white"
        android:choiceMode="singleChoice"
        android:divider="@null"
        android:dividerHeight="0dp" />

</android.support.v4.widget.DrawerLayout>

fragment_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/swipeRefreshLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white"
    android:orientation="vertical"
    android:padding="4dp" >

    <GridView
        android:id="@+id/gridview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/white"
        android:horizontalSpacing="0dp"
        android:numColumns="3"
        android:stretchMode="columnWidth"
        android:verticalSpacing="0dp" >
    </GridView>

    <WebView
        android:id="@+id/webview_fragment_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="gone" />

</android.support.v4.widget.SwipeRefreshLayout>

MainActivity - onCreate():

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    if (Const.DEBUGGING_INT)
        Log.d(Const.DEBUG, "Activity - onCreate");

    prefs = PreferenceManager.getDefaultSharedPreferences(this);

    if (Const.DEBUGGING)
        Log.d(Const.DEBUG, "Index = " + index);

    mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
    toolbar = (Toolbar) findViewById(R.id.toolbar);
    spinner = (Spinner) toolbar.findViewById(R.id.spinner);
    mDrawerList = (ListView) findViewById(R.id.listview_drawer);

    setUpHeaderAndFooter();

    setSupportActionBar(toolbar);
    getSupportActionBar().setHomeButtonEnabled(true);
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    getSupportActionBar().setTitle("");

    setUpToolBar();
    setUpSpinner();
    setUpNavigationDrawer();

    if ((findViewById(R.id.fragment_container) != null)
            && (findViewById(R.id.detail_fragment_container) != null)) {
        mTwoPane = true;
    } else {
        mTwoPane = false;
    }

    setFragment(prefs.getInt(Const.Prefs.MAININDEX, 0), 0);

}

setFragment Method in MainActivity:

private void setFragment(int mainIndex, int subIndex) {

    if (Const.DEBUGGING_INT) {
        Log.d(Const.DEBUG, "Activity - setFragment");
        Log.d(Const.DEBUG, "Position = " + mainIndex);
        Log.d(Const.DEBUG, "TwoPane? " + mTwoPane);
    }

    incrementClickCount(mainIndex);

    mMainFragment = new MainFragment();
    getSupportFragmentManager().beginTransaction()
            .replace(R.id.fragment_container, mMainFragment).commit();

    Bundle bundle = new Bundle();
    bundle.putInt(Const.BundleParameters.MAININDEX, mainIndex);
    bundle.putInt(Const.BundleParameters.SUBINDEX, subIndex);
    bundle.putBoolean(Const.BundleParameters.TWOPANE, mTwoPane);
    bundle.putInt(Const.BundleParameters.CURRENTPOSITION, 0);
    mMainFragment.setParameters(bundle);

    mDrawerList.setItemChecked(mainIndex, true);
    mDrawerLayout.closeDrawer(mDrawerList);

    if (mTwoPane) {

        mDetailFragment = new DetailFragment();
        getSupportFragmentManager().beginTransaction()
                .replace(R.id.detail_fragment_container, mDetailFragment)
                .commit();

        if (Const.DEBUGGING)
            Log.d(Const.DEBUG,
                    "URL = " + prefs.getString(Const.Prefs.CURRENT_URL, ""));

        Bundle b = new Bundle();
        b.putString("url", prefs.getString(Const.Prefs.CURRENT_URL, ""));
        mDetailFragment.setParameters(b);
    }

}

MainFragment - onCreateView():

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {

    Log.d(Const.DEBUG, "Fragment - onCreateView");

    View view = inflater.inflate(R.layout.fragment_main, container, false);

    mSwipeRefreshLayout = (SwipeRefreshLayout) view
            .findViewById(R.id.swipeRefreshLayout);
    mSwipeRefreshLayout.setOnRefreshListener(this);
    mSwipeRefreshLayout.setColorSchemeResources(R.color.blue,
            R.color.green, R.color.pink, R.color.yellow);

    // if (!mSwipeRefreshLayout.isRefreshing())
    mSwipeRefreshLayout.setRefreshing(true);

    prefs = PreferenceManager.getDefaultSharedPreferences(activity);

    mDbAdapter = DatabaseHelper.get(getActivity().getApplicationContext())
            .getDbAdapter();

    gridview = (GridView) view.findViewById(R.id.gridview);
    webview = (WebView) view.findViewById(R.id.webview_fragment_main);
    webview.setWebViewClient(new MyWebViewClient());
    webview.setWebChromeClient(new WebChromeClient() {
        @Override
        public boolean onJsAlert(WebView view, String url, String message,
                JsResult result) {
            return super.onJsAlert(view, url, message, result);
        }
    });

    WebSettings settings = webview.getSettings();
    settings.setJavaScriptEnabled(true);
    settings.setCacheMode(WebSettings.LOAD_NO_CACHE);
    webview.setVerticalScrollBarEnabled(false);
    webview.setHorizontalScrollBarEnabled(false);

    setTableDetails();

    isTableEmpty = mDbAdapter.isTableEmpty(mTableName);
    if (isTableEmpty) {
        getDataFromServer(true);
    } else {
        checkRefreshTimeAndGetData();
    }

    return view;
}

DetailFragment - onCreateView():

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {

    if (Const.DEBUGGING) {
        Log.d(Const.DEBUG, "***** DetailFragment - onCreateView *****");
        Log.d(Const.DEBUG, "URL = " + url);
    }

    View view = inflater
            .inflate(R.layout.fragment_detail, container, false);

    prefs = PreferenceManager.getDefaultSharedPreferences(activity);
    pd = (ProgressBar) view.findViewById(R.id.progressBar);

    webview = (WebView) view.findViewById(R.id.webview_fragment_detail);
    webview.setWebViewClient(new MyWebViewClient());
    webview.setWebChromeClient(new WebChromeClient() {
        @Override
        public boolean onJsAlert(WebView view, String url, String message,
                JsResult result) {
            return super.onJsAlert(view, url, message, result);
        }
    });

    pd.setVisibility(View.VISIBLE);
    webview.setVisibility(View.GONE);

    WebSettings settings = webview.getSettings();
    settings.setJavaScriptEnabled(true);
    webview.setVerticalScrollBarEnabled(false);
    webview.setHorizontalScrollBarEnabled(false);
    webview.loadUrl(url);
    // webview.loadDataWithBaseURL(url, null, "text/html", "utf-8", null);

    return view;
}

Logcat:

11-16 23:15:05.234: E/FragmentManager(26609): No view found for id 0x7f0a0052 (com.xx.xxx:id/detail_fragment_container) for fragment DetailFragment{42476ab8 #1 id=0x7f0a0052}

I understand that it's looking for detail_fragment_container, that i don't have in portrait mode. How do i fix this? Let me know if you need the code as well. Will be glad to post..

Vamsi Challa
  • 11,038
  • 31
  • 99
  • 149
  • can you put your full code? – Akash Moradiya Nov 17 '14 at 06:16
  • @MoradiyaAkash, edited question with code. – Vamsi Challa Nov 17 '14 at 06:19
  • Can you post the code of your `DetailFragment` class? Or confirm that there is no call of `R.id.detail_fragment_container` in your `DetailFragment`. – Neoh Nov 19 '14 at 15:09
  • @Neoh, posted DetailFragment class.. ya there is no call for R.id.detail_fragment_container in it. – Vamsi Challa Nov 19 '14 at 15:16
  • Is your problem arises when you change from landscape to portrait mode? – Neoh Nov 19 '14 at 15:17
  • Yes.. When i rotate the tablet from landscape to portrait, i get the crash. If i open the app in portrait mode, the app doesn't crash, but then if i change to landscape and then again to portrait, it crashes. – Vamsi Challa Nov 19 '14 at 15:19
  • please see suggestion in my edited answer. – Neoh Nov 19 '14 at 15:37
  • @VamsiChalla The scenario you describe should work (indeed, it's more or less the same as in the [Fragments documentation](https://developer.android.com/guide/components/fragments.html#Example) in the API Guide). There must be something specific that is causing the problem, but I was not able to reproduce it with these snippets. Is there any way you could publish your full source code, or at least a trimmed version where the error occurs? – matiash Nov 20 '14 at 00:36
  • @matiash, Ya.. I will update the post in couple of hours and will leave you a message – Vamsi Challa Nov 20 '14 at 02:52
  • @matiash, at the moment I am not sure, if i can post the whole code. What do you think, the problem(s) might be, if i use super.onCreate(null). I agree this might not be the best solution, but want to know what problems might occur, in worst case scenario. – Vamsi Challa Nov 20 '14 at 09:13
  • @VamsiChalla Basically, whenever the activity is recreated (not only due to rotation, but also when the app is unloaded from memory by the OS) the old state will not be restored. "State" includes whatever Views and Fragments save when their `onSaveInstanceState()` is called (e.g. text in `EditText`). – matiash Nov 20 '14 at 14:22

3 Answers3

7

The only way I can think of you getting that error is that your old layout is used. That could be because super.onCreate(savedInstanceState);

Instead try ignoring the saved state:

super.onCreate(null);

Edit:

Response to @matiash comment:

Since OP didn't provide code to re-create the problem, it's hard to test other (if any) solutions.

However I do agree that resetting the savedInstanceState is kind of an overkill. Therefore I think OP should try it himself and see to it that he saves as many views as possible.

The first thing that comes to mind is preventing the problematic view from being saved:

<FrameLayout
    android:id="@+id/detail_fragment_container"
    android:layout_width="0dip"
    android:layout_height="match_parent"
    android:layout_weight="0.60"
    android:saveEnabled="false"/>
Simas
  • 43,548
  • 10
  • 88
  • 116
  • This solved the issue. Thanks a lot.. I will award the bounty in 15 hours – Vamsi Challa Nov 19 '14 at 18:04
  • 2
    @VamsiChalla This is not a good solution, since it will also completely break [Activity recreation](http://developer.android.com/training/basics/activity-lifecycle/recreating.html) by the Android framework (for example when an activity is torn down due to low memory). When the user switches back to your app after some time, the views will not restore their old state. While this _might_ be acceptable, depending on the app, it's kinda like using a cannon to kill a fly. I would think that there must be a better fix for the underlying issue rather than "just discard the old instance state". – matiash Nov 19 '14 at 22:07
  • since i got my issue resolved, though might not be the correct solution, i am awarding this answer the bounty. – Vamsi Challa Nov 25 '14 at 09:12
  • @user3249477 Please, do you think you could give me some advice/ideas over here http://goo.gl/E37NSu – eddy Mar 01 '15 at 15:31
0

The FragmentManager will try to recreate all the fragments when rotated, but in portrait mode you don't have the DetailFragment layout anymore, so you should remove the fragment to prevent it from being attached.

EDIT: Perhaps the cleanest way to detect your orientation change is to use the OrientationEventListener. You can refer to this post and this for some example. You should remove the fragment inside the method onOrientationChanged (int orientation) which you should override.

Community
  • 1
  • 1
Neoh
  • 15,906
  • 14
  • 66
  • 78
  • The value of mTwoPane is coming as false in portrait and true in landscape, even with my existing code. So, I think, that part is not the source for the problem. – Vamsi Challa Nov 19 '14 at 14:23
  • I tried adding your code in the Activity, but it still gives me the same crash. – Vamsi Challa Nov 19 '14 at 15:50
-1

Change the code,

if ((findViewById(R.id.fragment_container) != null)
            && (findViewById(R.id.detail_fragment_container) != null)) {
        mTwoPane = true;
    } else {
        mTwoPane = false;
    }

by

if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE){
    //Do some stuff
mTwoPane = true;
} else{
 mTwoPane = false;
}

hope it will help you

Akash Moradiya
  • 3,318
  • 1
  • 14
  • 19
  • My scenario is, i am using different layouts in tablet's portrait and landscape modes. The view detail_fragment_container only appears in the layout for tablet. If i go with your solution, this will change even for phones on landscape orientation. – Vamsi Challa Nov 17 '14 at 06:25