CS499 * Mobile Application Development

Download Report

Transcript CS499 * Mobile Application Development

CS499 – Mobile Application
Development
Fall 2013
Fragments
Supporting Different Screens
• Device orientation changes characteristics of
display
• Tablets have larger displays than phones
• Need ways to support multiple UI panes/user
behaviors at the same time
– May not need “one activity per screenfull of
data””
Example - Email
• On phone – 2 activites:
– List email messages
– View selected message
• On tablet – 2 fragments embedded into a
single activity
Supporting different screens
• Can use the resources to deal with this (in
limited ways)
– In drawables - four generalized densities: low
(ldpi), medium (mdpi), high (hdpi), extra high
(xhdpi)
– In layouts, can do similar things - four generalized
sizes: small, normal, large, xlarge
– In layouts, can also differentiate between portrait
and landscape orientations
Supporting different screens
• For example, this project includes a default layout
and an alternative layout for large screens:
MyProject/
res/
layout/ main.xml
layout-large/ main.xml
• The file name must be exactly the same
(main.xml), but their contents are different in
order to provide an optimized UI for the
corresponding screen size.
Supporting different screens
• http://developer.android.com/training/multis
creen/index.html
Fragments
• Introduced in 3.0
• Represent a behavior/portion of UI within an
Activity
• Multiple fragments can be embedded in a
Activity to create a multi-pane UI
• A single fragment can be reused across
multiple activities
• http://developer.android.com/training/basics/
fragments/index.html
Fragment Lifecycle
• Fragments have their own lifecycles and
receive their own events
• But Activity lifecycle interacts with associated
fragment’s lifecycle:
– When activity pauses, its fragments are paused.
– When activity is destroyed, its fragments are
destroyed
Fragment Lifecycle
• Similar to Activity lifecycle states
• Resumed
– Fragment is visible in running activity
• Paused
– Another activity is in the foreground and has focus
– The containing activity is still visible
• Stopped
– The fragment is not visible
Lifecycle callbacks
• onCreate()
–
Initial creation of the fragment
• onStart()
–
Fragment is visible to the user
• onResume()
–
Fragment is visible to the user and actively running
• onPause()
–
Fragment is visible, but does not have focus
• onStop()
–
Fragment is no longer visible
• onDestroy()
–
Fragment is no longer in use
Lifecycle callbacks
• onAttach()
– Fragment is first attached to its activity
• onCreateView()
– Fragment instantiates its user inteface view
• onActivityCreated
– Fragment’s activity created and Fragment’s view hierarchy
instantiated
• onDestroyView()
– View previously cretaed by onCreateView() detached from
the fragment
• onDetatch()
– Fragment no longer attached to its activity
Fragment & Activity Lifecycles
onCreate()
onStart()
onResume() onPause()
onStop()
onDestroy()
onAttach()
onStart()
onResume()
onStop()
onDestroy()
onPause()
onCreate()
onDestroy()
onCreateView()
onDetach()
onActivityCreated()
Fragment Layout
• Fragments usually, but not always, have a UI
• Layout can be inflated/implemented in
onCreateView()
– onCreateView() must return the View at the root
of the Fragment’s layout
– The returned View will be added to the containing
Activity
• Container represented as a ViewGroup within the
containing Activities view hierarchy
Fragment Layout
• Two ways to add Fragments to an Activity's
layout
– Declare statically in the Activity’s layout file
– Add it programmatically to a ViewGroup in the
Activity's layout
Example App: Static Fragments
Going to create two versions
• Version 1 – create activity with two fragments
– Screen will not change with orientation
• Version 2 – extend version 1 so that using
both fragments in landscape mode but only
one (at a time) in portrait
Main Activity
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle
savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
MainActivity.java
<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 = "horizontal">
<fragment
android:name = "cs499.examples.fragmentstatic.Fragment1"
android:id = "@+id/fragment1"
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="match_parent" />
<fragment
android:name = "cs499.examples.fragmentstatic.Fragment2"
android:id = "@+id/fragment2"
android:layout_weight="0"
android:layout_width="wrap_content"
android:layout_height="match_parent" />
</LinearLayout>
activity_main.xml
Fragments
<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" android:background="#0000FF“ >
<ListView
android:layout_width="match_parent" android:layout_height="match_parent"
android:id="@+id/fruits_list"
android:drawSelectorOnTop="false" />
</LinearLayout>
fragment1.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:orientation="vertical" >
<TextView
android:layout_width="match_parent" android:layout_height="match_parent"
android:id="@+id/selectedopt"
android:text="Please Select a Fruit" />
</LinearLayout>
fragment2.xml
Fragment1.java
public class Fragment1 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Context c = getActivity().getApplicationContext();
View vw = inflater.inflate(R.layout.fragment1, container, false);
final String[] fruits={"Apple", "Mango", "Orange", "Grapes","Banana" };
ListView fruitsList = (ListView) vw.findViewById(R.id.fruits_list);
ArrayAdapter<String> arrayAdpt = new ArrayAdapter<String>(c,android.R.layout.simple_list_item_1,fruits);
fruitsList.setAdapter(arrayAdpt);
fruitsList.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
TextView selectedOpt = (TextView)getActivity().findViewById(R.id.selectedopt);
selectedOpt.setText("You have selected "+ ((TextView)view).getText().toString());
}
});
return vw;
modifies fragment2
}
}
Fragment2.java
public class Fragment2 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment2, container, false);
}
}
No change on orientation change
Version 2
• When in landscape mode, show both
fragments.
• When in portrait mode, show fragment 1.
Selecting an item starts a new activity that
shows fragment 2.
• Want to move seamlessly between these
modes.
Modify activity_main.xml:
• When a device switches to landscape mode,
the layout file from res/layout-land is used to
display view. So,
– Create folder in /res/layout-land
– Copy activity_main.xml (with two fragments) in
new folder
– Remove second fragment from activity_main in
/res/layout
in Fragments1.java:
fruitsList.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (getResources().getConfiguration().orientation ==
Configuration.ORIENTATION_LANDSCAPE) {
TextView selectedOpt = (TextView)getActivity().findViewById(R.id.selectedopt);
selectedOpt.setText("You have selected "+ ((TextView)view).getText().toString());
} else {
Intent intent = new Intent(getActivity().getApplicationContext(), ShowItemActivity.class);
intent.putExtra("item", ((TextView)view).getText().toString());
startActivity(intent);
}
}
});
ShowItemActivity.java
public class ShowItemActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
finish();
return;
}
setContentView(R.layout.fragment2);
Bundle extras = getIntent().getExtras();
if (extras != null) {
String selectedItem = extras.getString("item");
TextView textView = (TextView) findViewById(R.id.selectedopt);
textView.setText("You have selected "+selectedItem);
}
}
}
Be sure to add this activity to
manifest
Creating/Managing Fragments in Java
• Uses a FragmentManager
– access to the fragments available in an activity
– perform FragmentTransaction – required to add,
remove and replace fragments
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
if (null==fm.findFragmentByTag(TAG1)) {
Frag1Activity fragment1 = new Frag1Activity();
ft.add(R.id.fragment_container,fragment,”TAG1”);
}
ft.commit();
Create same app
• Use Fragment1.xml and Fragment2.xml
• Change 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:orientation="horizontal">
<LinearLayout
android:id="@+id/fragment1"
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="match_parent"
/>
<LinearLayout
android:id="@+id/fragment2"
android:layout_weight="0"
android:layout_width="wrap_content"
android:layout_height="match_parent"
/>
</LinearLayout>
public class Fragment1 extends Fragment {
protected static final String FRAG2 = "2";
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Context c = getActivity().getApplicationContext();
View vw = inflater.inflate(R.layout.fragment1, container, false);
final String[] fruits={"Apple", "Mango", "Orange", "Grapes","Banana" };
ListView fruitsList = (ListView) vw.findViewById(R.id.fruits_list);
ArrayAdapter<String> arrayAdpt = new ArrayAdapter<String>(c,android.R.layout.simple_list_item_1,fruits);
fruitsList.setAdapter(arrayAdpt);
final FragmentManager fragmentManager = getFragmentManager();
fruitsList.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (null != fragmentManager.findFragmentByTag(FRAG2)) {
TextView selectedOpt = (TextView)getActivity().findViewById(R.id.selectedopt);
selectedOpt.setText("You have selected "+((TextView)view).getText().toString());
} else {
Intent intent = new Intent(getActivity().getApplicationContext(),ShowItemActivity.class);
intent.putExtra("item", ((TextView) view).getText().toString());
startActivity(intent);
} } });
return vw;
}
}
Fragment1.java
MainActivity.java
public class MainActivity extends Activity {
private static final String FRAG1 = "1";
private static final String FRAG2 = "2";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE){
ft.add(R.id.fragment1, new Fragment1(),FRAG1);
ft.add(R.id.fragment2,new Fragment2(),FRAG2);
} else {
if (null != fm.findFragmentByTag(FRAG2))
ft.remove(fm.findFragmentByTag(FRAG2));
ft.add(R.id.fragment1, new Fragment1(),FRAG1);
}
ft.commit();
}
Use same Fragment2.java and
}
ShowItemActivity.java as static version
Dynamic UIs
• Can replace fragments (in addition to
add/delete) to react.
• To allow the user to navigate backward
through the fragment transactions, you must
call addToBackStack() before you commit
theFragmentTransaction.
Updating Fragments
// Create fragment and give it an argument specifying the article it should
show
ArticleFragment newFragment = new ArticleFragment();
Bundle args = new Bundle();
args.putInt(ArticleFragment.ARG_POSITION, position);
newFragment.setArguments(args);
FragmentTransaction transaction =
getSupportFragmentManager().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();
Special Fragment
• ListFragment – builtin listview
• DialogFragment
• PreferenceFragment