Sunday, April 21, 2013

Handle different layout for phone and tablet, load fragment in Java code vs XML

Last exercise demonstrate to create /res/layout/activity_main.xml for phone, and /res/layout-sw600dp/activity_main.xml for tablet. We are going to handle the layout separately.

In single pane mode run on phone, We have one pane displayed only. We have to switch fragment using Java code programm programmatically; if define in XML we cannot switch fragment later. We have a FrameLayout with id "phone_container" in layout xml. It will be used to determine if it's run on phone or tablet. We will load MyListFragment in onCreate() programmatically.

/res/layout/activity_main.xml
<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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Normal" />
    
    <FrameLayout
        android:id="@+id/phone_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    </FrameLayout>

</LinearLayout>

Run on phone with a ListFragment


In two pane mode, run on tablet. There are two fragments in layout. The fragments are loaded in XML.

/res/layout-sw600dp/activity_main.xml
<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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="sw600dp" />
    <LinearLayout 
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal">
        <fragment
            android:name="com.example.androiddualmode.MainActivity$MyListFragment"
            android:id="@+id/list_fragment"
            android:layout_height="match_parent"
            android:layout_width="0dp"
            android:layout_weight="1" />
        <fragment
            android:name="com.example.androiddualmode.MainActivity$MyDetailFragment"
            android:id="@+id/detail_fragment"
            android:layout_height="match_parent"
            android:layout_width="0dp"
            android:layout_weight="2" />
        
    </LinearLayout>

</LinearLayout>

Run on tablet with two panes


Create /res/layout/layout_detailfragment.xml, it's a simple layout in the MyDetailFragment.
<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">

    <TextView
        android:id="@+id/title_detailfragment"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Detail Fragment"/>
    
</LinearLayout>


Modify MainActivity.java. Implement inner class of MyListFragment and MyDetailFragment.
package com.example.androiddualmode;

import android.os.Bundle;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.app.ListFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;

public class MainActivity extends Activity {
 
 // if run on phone, isSinglePane = true
 // if run on tablet, isSinglePane = false
 boolean isSinglePane;
 
 static String[] month ={
   "January", "February", "March", "April",
   "May", "June", "July", "August",
   "September", "October", "November", "December"};

 public static class MyListFragment extends ListFragment {

  @Override
  public void onActivityCreated(Bundle savedInstanceState) {
   // TODO Auto-generated method stub
   super.onActivityCreated(savedInstanceState);
   
   ListAdapter myArrayAdapter = 
     new ArrayAdapter<String>(
       getActivity(), android.R.layout.simple_list_item_1, month);
   setListAdapter(myArrayAdapter);
   
  }

 }
 
 public static class MyDetailFragment extends Fragment {

  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
    Bundle savedInstanceState) {
   // TODO Auto-generated method stub
   View view = inflater.inflate(R.layout.layout_detailfragment, null);
   return view;
  }

 }

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  View v = findViewById(R.id.phone_container);
  if(v == null){
   //it's run on tablet
   isSinglePane = false;
   /*
    * MyListFragment and MyDetailFragment have been loaded in XML,
    * no need load.
    */
   
  }else{
   //it's run on phone
   //Load MyListFragment programmatically
   isSinglePane = true;
   
   if(savedInstanceState == null){
    //if's the first time created
    MyListFragment myListFragment = new MyListFragment();
    FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
    fragmentTransaction.add(R.id.phone_container, myListFragment);
    fragmentTransaction.commit();
   }
  }
 }

}


download filesDownload the files.

Next:
- Handle onListItemClick() of ListFragment, to pass data between fragment


2 comments:

Жопа said...

how strictly determine layouts for any phone and any tablet?

Erik said...

Hello,

As I remember, Google post recently not to determine determine device by tablet/phone. (I forgot the source).

Developer should determine based on the amount of space, such as sw600dp, sw720dp.

from Google document:
For the first generation of tablets running Android 3.0, the proper way to declare tablet layouts was to put them in a directory with the xlarge configuration qualifier (for example, res/layout-xlarge/). In order to accommodate other types of tablets and screen sizes—in particular, 7" tablets—Android 3.2 introduces a new way to specify resources for more discrete screen sizes. The new technique is based on the amount of space your layout needs (such as 600dp of width), rather than trying to make your layout fit the generalized size groups (such as large or xlarge).

~ reference: http://developer.android.com/guide/practices/screens_support.html#DeclaringTabletLayouts