5) Modular UI Design with better fragment communication

The slides on this page are screen shots from Vivz slidenerd videos on youtube. Full video can be seen on the bottom of this page.

Snap 2014-04-26 at 10.57.10

Below we have the modified communicator design pattern which is better than the previous example because out interface isn’t exposed (used as a separate class) and is only accessible to where it’s needed. In java, a general rule is ‘do not expose something more that it needs to be’. In our example, only FragmentA and MainActivity need use of the communicator so we place it WITHIN Fragment A. Since Fragment B doesn’t care about the communicator, we can hide it in Fragment A.

Snap 2014-04-26 at 11.10.06

The full design pattern including the orientation switching.

Snap 2014-04-26 at 12.06.54

MainActivity.java

package com.jamesfroggatt.fragments5.app;

import android.app.Activity;
import android.app.FragmentManager;
import android.content.Intent;
import android.os.Bundle;


public class MainActivity extends Activity implements FragmentA.Communicator {

    // create references to the fragments
    FragmentA f1;
    FragmentB f2;
    FragmentManager manager;

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

        // initialise the fragment manager
        manager=getFragmentManager();

        // initialise fragment A because this fragment will ALWAYS be there in
        // portrait and landscape mode
        f1 = (FragmentA) manager.findFragmentById(R.id.fragment);

        f1.setCommunicator(this); // this = we're passing an object of type MainActivity
    }

    // this method is generated from the Communicator interface (in Fragment A) as
    // we're implementing it here in MainActivity
    @Override
    public void respond(int position) {
        // we need to check, are we in portrait or landscape mode
        f2= (FragmentB) manager.findFragmentById(R.id.fragment2);
        if (f2!=null && f2.isVisible())
        {
            // we are ˙in landscape mode
            f2.changeData(position);
        }else
        {
            // we are in portrait mode
            // start the new activity
            Intent intent= new Intent(this,AnotherActivity.class);
            intent.putExtra("index",position);
            startActivity(intent);
        }
    }
}

FragmentA.java

package com.jamesfroggatt.fragments5.app;

import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;

public class FragmentA extends Fragment implements AdapterView.OnItemClickListener {

    // create a reference to the ListView
    ListView list;

    // create a reference to the Communicator
    Communicator communicator;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        //return super.onCreateView(inflater, container, savedInstanceState);

        // link the layout to the view object
        View view=inflater.inflate(R.layout.fragment_a, container, false);

        // find the ListView object from the inflated view and attach it to list
        list= (ListView) view.findViewById(R.id.listView);

        // now we set an adapter which will contain our datasource
        ArrayAdapter adapter = ArrayAdapter.createFromResource(getActivity(),R.array.chapters,android.R.layout.simple_list_item_1);

        // set the adapter
        list.setAdapter(adapter);

        // set the on
        list.setOnItemClickListener(this);

        return view;
    }

    // this is the generated onItemClick listener for the listView
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        communicator.respond(position);
    }

    // COMMUNICATOR INTERFACE - method "respond" is responsible for carrying data from
    // fragment A to fragment B
    public interface Communicator{
        public void respond(int position); // calls the respond method inside MainActivity
    }

    // method to set the communicator
    public void setCommunicator(Communicator communicator){
        this.communicator=communicator;
    }
}

FragmentB.java

package com.jamesfroggatt.fragments5.app;

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

public class FragmentB extends Fragment {

    // get a reference to the textView
    TextView text;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        //return super.onCreateView(inflater, container, savedInstanceState);

        // link the layout to the view object
        View view=inflater.inflate(R.layout.fragment_b, container, false);

        // find the TextView object from the inflated view and attach it to text
        text= (TextView) view.findViewById(R.id.textView);



        return view;
    }

    // we need a method to change the contents of our TextView
    public void changeData(int index){

        String[] descriptions=getResources().getStringArray(R.array.description);
        text.setText(descriptions[index]);
    }
}

AnotherActivity.java

package com.jamesfroggatt.fragments5.app;

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

public class AnotherActivity extends Activity{

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

        Intent intent=getIntent();
        int index = intent.getIntExtra("index",0);

        // get a reference to fragment B
        FragmentB f2= (FragmentB) getFragmentManager().findFragmentById(R.id.fragment2);
        if (f2!=null){
            f2.changeData(index);
        }

    }
}

layout/activity_main.xml

<RelativeLayout 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:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:background="#FF3399"
    tools:context="com.jamesfroggatt.fragments5.app.MainActivity">


    <fragment
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:name="com.jamesfroggatt.fragments5.app.FragmentA"
        android:id="@+id/fragment"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_alignParentRight="true" />
</RelativeLayout>

layout/fragment_a.xml

<?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"
    android:background="#AA4499">

    <ListView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/listView"
        android:layout_gravity="center_horizontal"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentBottom="true" />
</LinearLayout>

layout/fragment_b.xml

<?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"
    android:background="#FFBB00">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="New Text"
        android:id="@+id/textView"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"/>
</LinearLayout>

layout/activity_another.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:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:name="com.jamesfroggatt.fragments5.app.FragmentB"
        android:id="@+id/fragment2"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        tools:layout="@layout/fragment_b" />
</LinearLayout>

layout-land/activity_main.xml

<?xml version="1.0" encoding="utf-8"?>

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

    <fragment
        android:layout_weight="1"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:name="com.jamesfroggatt.fragments5.app.FragmentA"
        android:id="@+id/fragment"
        android:layout_gravity="left|center_vertical"/>

    <fragment
        android:layout_weight="1"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:name="com.jamesfroggatt.fragments5.app.FragmentB"
        android:id="@+id/fragment2"
        android:layout_gravity="left|center_vertical" />
</LinearLayout>

values/strings.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="app_name">Fragments5</string>
    <string name="hello_world">Hello world!</string>
    <string name="action_settings">Settings</string>

    <!-- used for populating the listView using the ArrayAdapter -->
    <string-array name="chapters">
        <item>Washing Machines</item>
        <item>Computers</item>
        <item>Phones</item>
        <item>Countries</item>
    </string-array>

    <string-array name="description">
        <item>Washing machines are quite popular and we\'ve got loads of them.</item>
        <item>Computers do quite alot of good stuff these days as they\'re powerful enough</item>
        <item>Most people have a phone. There are many models and types, many run Android</item>
        <item>How any are there, tonnes of them. France, Spain, Germany, loads of them</item>
    </string-array>

</resources>

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.jamesfroggatt.fragments5.app" >

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.jamesfroggatt.fragments5.app.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.jamesfroggatt.fragments5.app.AnotherActivity"></activity>

    </application>

</manifest>

Final product.

This is the full tutorial on youtube

Leave a Reply