How to create layouts that change according to the phone and Tablet in Android Using Fragments?

By | February 17, 2013

Hello all…..

This is a simple tutorial to show how to create a FLEXIBLE UI for both Phone and Tablets in Android.
In the figure below you can see the result after running this tutorial.

Tablet in LANDSCAPE MODE

Fragments

TABLET IN PORTRAIT MODE

Fragments

TABLET IN PORTRAIT MODE – AFTER CLICKING THE LIST.

Fragments

PHONE IN PORTRAIT MODE

Fragments

PHONE IN LANDSCAPE MODE.

Fragments

This can be achieved through Fragments.
This tutorial will have two parts.
1. In the first part I will show you how to use Fragments from API 11 (Honeycomb) and greater.
2. In the second I will show how to change it to adapt to lower versions from 1.6(Donut) using Google’s supporting Library.

First Part ( ONLY for HONEYCOMB and GREATER).
(How to use Fragments from API 11 (Honeycomb) and greater)

First we will start with the layout XML Files.
This layout is for the first left part.

left_panel.xml. (Create this XML inside the res/layout folder)

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >


    <fragment
        android:id="@+id/titles"
        android:layout_width="0px"
        android:layout_height="match_parent"
        android:layout_weight="1"
        class="com.coderzheaven.googlefragment.TitlesFragment" />

</LinearLayout>

Now create the same copy of this XML inside the res/layout-land folder and change it’s contents slightly to this.
Here we have two fragments because this is used for the landscape mode in the tablets.
So Two fragments will be there at the same time in a single activity.
In the above XML this is not the case since there is only one fragment.

left_panel.xml (inside layout-land folder)

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal" >

    <fragment
        android:id="@+id/titles"
        android:layout_width="0px"
        android:layout_height="fill_parent"
        android:layout_weight="1"
        class="com.coderzheaven.googlefragment.TitlesFragment" />

    <LinearLayout
        android:id="@+id/details"
        android:layout_width="0px"
        android:layout_height="fill_parent"
        android:layout_weight="2"
        android:background="?android:attr/detailsElementBackground" >
        
        
    </LinearLayout>

</LinearLayout>

The above XML will contain the two layout(fragments) for showing the details while clicking or selecting from the Left Panel for the TABLETS not the PHONES.

So we have to create another XML for showing details in the PHONES.

For that Create a new XML named “details_lay.xml” inside res/layout folder.

details_lay.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >


    <LinearLayout
        android:id="@+id/details"
        android:layout_width="0px"
        android:layout_height="match_parent"
        android:layout_weight="1"
        class="com.coderzheaven.googlefragment.DetailsFragment" />

</LinearLayout>

NOTE the class attribute inside the Fragments. It will be pointing to the Class for corresponding Fragments This is the important part in Fragments.
Make sure you change this to your package name before running your project.

Now we will create another layout for content inside the Details (right Panel).
I am naming it “layout_details_content.xml”.

layout_details_content.xml ( this is simply for showing a textview inside the Right Panel, However you can create this dynamically also)
layout_details_content.xml.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    android:layout_gravity="center" >

    <TextView
        android:id="@+id/tv1"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:text="test"
        android:gravity="center" />

</LinearLayout>

OK Now all layouts are complete. Now we will go to the JAVA coding Part.

Remember all Fragments are associated with a java class that extends the Fragment Class.
That’s why we can use Fragments anywhere.

Now the MainActivity that launches First.

MainActivity.java

package com.coderzheaven.googlefragment;

import android.app.Activity;
import android.os.Bundle;

public class MainActivity extends Activity {

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

}

The MainActivity has left_panel.xml as layout which will be layout/left_panel.xml in the phones and layout-land/ left_panel.xml in the tablets.
One thing to remember is Tablets will take layout/left_panel.xml while in it’s PORTRAIT mode.

Now we will write the classes for the Fragments.

First we will write class for the List Fragment on the Left.
TitlesFragment.java

package com.coderzheaven.googlefragment;

import android.app.FragmentTransaction;
import android.app.ListFragment;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;

public class TitlesFragment extends ListFragment {
	boolean mDualPane;
	int mCurCheckPosition = 0;

	static String titles[] = { "CoderzHeaven", "Google", "Apple","Android","Microsoft","Samsung" };
 
	@Override 
	public void onActivityCreated(Bundle savedInstanceState) {
		super.onActivityCreated(savedInstanceState);

		// Populate list with our static array of titles.
		setListAdapter(new ArrayAdapter<String>(getActivity(),
				android.R.layout.simple_dropdown_item_1line, titles));

		// Check to see if we have a frame in which to embed the details
		// fragment directly in the containing UI.
		View detailsFrame = getActivity().findViewById(R.id.details);
		mDualPane = detailsFrame != null
				&& detailsFrame.getVisibility() == View.VISIBLE;

		if (savedInstanceState != null) {
			// Restore last state for checked position.
			mCurCheckPosition = savedInstanceState.getInt("curChoice", 0);
		} 

		if (mDualPane) {  
			// In dual-pane mode, the list view highlights the selected item.
			getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
			// Make sure our UI is in the correct state.
			showDetails(mCurCheckPosition);
		}else{
			System.out.println("NOT DUAL");
		}
	}  

	@Override
	public void onSaveInstanceState(Bundle outState) {
		super.onSaveInstanceState(outState);
		outState.putInt("curChoice", mCurCheckPosition);
	}

	@Override
	public void onListItemClick(ListView l, View v, int position, long id) {
		showDetails(position);
	}

	/**
	 * Helper function to show the details of a selected item, either by
	 * displaying a fragment in-place in the current UI, or starting a whole new
	 * activity in which it is displayed.
	 */
	void showDetails(int index) { 
		mCurCheckPosition = index;
		if (mDualPane) {
			// We can display everything in-place with fragments, so update
			// the list to highlight the selected item and show the data.
			getListView().setItemChecked(index, true);

			// Check what fragment is currently shown, replace if needed.
			DetailsFragment details = (DetailsFragment) getFragmentManager()
					.findFragmentById(R.id.details);
			if (details == null || details.getShownIndex() != index) {
				// Make new fragment to show this selection.
				details = DetailsFragment.newInstance(index);

				// Execute a transaction, replacing any existing fragment
				// with this one inside the frame.
				FragmentTransaction ft = getFragmentManager()
						.beginTransaction();
				ft.replace(R.id.details, details);
				ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
				ft.commit();
			}

		} else {
			// Otherwise we need to launch a new activity to display
			// the dialog fragment with selected text.
			Intent intent = new Intent();
			intent.setClass(getActivity(), DetailsActivity.class);
			intent.putExtra("index", index);
			startActivity(intent);
		}
	}
}

Note this Line

View detailsFrame = getActivity().findViewById(R.id.details);
		mDualPane = detailsFrame != null
				&& detailsFrame.getVisibility() == View.VISIBLE;

Since for phones there will not be Details Fragment in the first layout as you see in the phones the layout/left_panel.xml is loaded and it has no DETAIL Fragment.

Now we will write class for the DETAIL Fragment.
DetailsFragment.java

package com.coderzheaven.googlefragment;

import android.app.Fragment;
import android.graphics.Color;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class DetailsFragment extends Fragment {
	/**
	 * Create a new instance of DetailsFragment, initialized to show the text at
	 * 'index'.
	 */
	static int i = 0;

	public static DetailsFragment newInstance(int index) {
		DetailsFragment f = new DetailsFragment();

		// Supply index input as an argument.
		Bundle args = new Bundle();
		args.putInt("index", index);

		i = index;
		f.setArguments(args);

		return f;
	}

	public int getShownIndex() {
		return getArguments().getInt("index", 0);
	}


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

		View v = getActivity().getLayoutInflater().inflate(
				R.layout.layout_details_content, null);
		TextView tv = (TextView)v.findViewById(R.id.tv1);
		tv.setText(TitlesFragment.titles[i].toUpperCase());
		tv.setTextColor(Color.RED);
		tv.setTextSize(25);
		
		return v;
	}
}

Note this fragement is used By phones and TABLETS while in portrait or LANDSCAPE Mode.
Only difference is when in Phone it is not shown at first since it is not present in the first layout.
Then we will check whether it is in the first layout. If it is there then we are in the LANDSCAPE MODE and if it is not present then we are in the portrait mode in PHONES AND TABLETS. So at that time we will start another DetailActivity which will load the DetaisFragment.

This is the line checking whether second fragment is in the layout or not

View detailsFrame = getActivity().findViewById(R.id.details);
		mDualPane = detailsFrame != null
				&& detailsFrame.getVisibility() == View.VISIBLE;

Here is the Second Activity for PHONES AND TABLETS IN THE PORTRAIT MODE.
DetailsActivity.java

package com.coderzheaven.googlefragment;

import android.app.Activity;
import android.app.Fragment;
import android.content.res.Configuration;
import android.os.Bundle;

public class DetailsActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
			setContentView(R.layout.details_lay); 
			return;
		}

		if (savedInstanceState == null) {
			// During initial setup, plug in the details fragment.
			Fragment details = new DetailsFragment();
			details.setArguments(getIntent().getExtras());
			getFragmentManager().beginTransaction()
					.add(android.R.id.content, details).commit();
		}
	}
}

For you understanding I am also pasting the AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.coderzheaven.googlefragment"
    android:versionCode="1"
    android:versionName="1.0" >
  
    <uses-sdk
        android:minSdkVersion="11"
        android:targetSdkVersion="17" />

    <uses-permission android:name="android.permission.INTERNET"/>
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.coderzheaven.googlefragment.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name="com.coderzheaven.googlefragment.DetailsActivity"></activity>
    </application>

</manifest>

One thought on “How to create layouts that change according to the phone and Tablet in Android Using Fragments?

  1. Pingback: | CoderzHeaven

Leave a Reply

Your email address will not be published. Required fields are marked *