CS453 | mobile programming
  • outline
  • projects
  • syllabus
  • links

Fragments depending on the Scale/Orientation

from http://www.vogella.com/tutorials/AndroidFragments/article.html

Example Activity RssfeedActivity should show two fragments (two panes) if started
in landscape mode. If it is started in portrait mode only one fragment should be shown.
In this exercise you adjust your application to support this.

 

1. Create activity layout for portrait mode that contains a FrameLayout which can contain the single or TWO fragements (if orientation allows)

Create the following activity_main.xml layout file based on the port resource qualifier for orientation.

<?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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".RssfeedActivity" android:orientation="vertical">
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"/>
</LinearLayout>

2. Define a boolean flag dependent of the resource selector --this tells us if we are running with One or Two fragments visible

Create a file in your res/values folder called config.xml with the following setting.

<?xml version="1.0" encoding="utf-8"?>
<resources>
<item type="bool" name="twoPaneMode">false</item>
</resources>

Create the same file for the landscape configuration with a different value. This file is located in your res/values-land folder.

<?xml version="1.0" encoding="utf-8"?>
<resources>
<item type="bool" name="twoPaneMode">true</item>
</resources>

3. MyListFragment class -- this contains the selectable list which when selected will effect the display of info in a DetailFragment

package com.example.android.rssreader;

 

import android.app.Fragment;

import android.content.Context;

import android.os.Bundle;

import android.view.LayoutInflater;

import android.view.View;

import android.view.ViewGroup;

import android.widget.Button;

 

public class MyListFragment extends Fragment {

 

    private OnItemSelectedListener listener;

 

    @Override

    public View onCreateView(LayoutInflater inflater, ViewGroup container,

                             Bundle savedInstanceState) {

        View view = inflater.inflate(R.layout.fragment_rsslist_overview,

                container, false);

        Button button = (Button) view.findViewById(R.id.updateButton);

        button.setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View v) {

                updateDetail();

            }

        });

        return view;

    }

 

    public interface OnItemSelectedListener {

        void onRssItemSelected(String text);

    }

 

    @Override

    public void onAttach(Context context) {

        super.onAttach(context);

        if (context instanceof OnItemSelectedListener) {

            listener = (OnItemSelectedListener) context;

        } else {

            throw new ClassCastException(context.toString()

                    + " must implement MyListFragment.OnItemSelectedListener");

        }

    }

 

    // triggers update of the details fragment

    public void updateDetail() {

        // create fake data

        String newTime = String.valueOf(System.currentTimeMillis());

        // send data to activity

        listener.onRssItemSelected(newTime);

    }

}

 

5. The DetailFragment fragment has a method to set the Text of its TextView (info from the item selected in the MyListFragment)

Adjust the DetailFragment class to the following.

package com.example.android.rssreader;
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 DetailFragment extends Fragment {
public static final String EXTRA_TEXT ="text";
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_rssitem_detail,
container, false);
return view;
}

@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Bundle bundle = getArguments();
if (bundle != null) {
String text = bundle.getString(EXTRA_TEXT);
setText(text);
}
}

public void setText(String text) {
TextView view = (TextView) getView().findViewById(R.id.detailsText);
view.setText(text);
}
}

6. Adjust the RssfeedActivity activity

 RssfeedActivity class will first add the ListFragment dynamically to the Activity

ALSO, replace the existing fragment (ListFragment) with a DetailFragment when item selected in case you are in single pane mode.

package com.example.android.rssreader;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.os.Bundle;
public class RssfeedActivity extends Activity implements
MyListFragment.OnItemSelectedListener {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (getResources().getBoolean(R.bool.twoPaneMode)) {
// all good, we use the fragments defined in the layout
return;
}
// if savedInstanceState is null we do some cleanup
if (savedInstanceState != null) {
// cleanup any existing fragments in case we are in detailed mode
getFragmentManager().executePendingTransactions();
Fragment fragmentById = getFragmentManager().
findFragmentById(R.id.fragment_container);
if (fragmentById!=null) {
getFragmentManager().beginTransaction()
.remove(fragmentById).commit();
}
}
MyListFragment listFragment = new MyListFragment();
getFragmentManager().beginTransaction()
.replace(R.id.fragment_container, listFragment).commit();

}

@Override
public void onRssItemSelected(String text) {
if (getResources().getBoolean(R.bool.twoPaneMode)) {
DetailFragment fragment = (DetailFragment) getFragmentManager()
.findFragmentById(R.id.detailFragment);
fragment.setText(text); //set text of the already existing DetailFragement
} else { //pass the text selected in a Bundle to the arguments of the DetailFragment
// replace the fragment
// Create fragment and give it an argument for the selected article
DetailFragment newFragment = new DetailFragment();
Bundle args = new Bundle();
args.putString(DetailFragment.EXTRA_TEXT, text);
newFragment.setArguments(args);
FragmentTransaction transaction = getFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,

// and add the transaction to the back stack so the user can navigate back
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();

}
}
}

This cleanup is necessary because we are using dynamically switching between two fragments and one fragments. That is an unusual scenario, typically the selection of one or

two fragment is based on the smallest width of a device and does not change at runtime. The FragmentManager caches the fragment to optimize performance, so we
need to ensure that we remove an existing detailed fragment.

7. Run it

Test your application. If you run the application in portrait mode, you should see only one fragment. In horizontal mode you see both fragments.

Switch the orientation of the emulator. Press the button in portrait as well as in horizontal mode and verify that the detail activity shows the current time.

SHOWN WITH 2 FRAGMENTS


 

 

 

cs453:mobile programming

  • home
  • outline
  • projects
  • syllabus
  • links