2.App Components-Activities/Fragments

1. Fragments

  A Fragment represents a behavior or a portion of user interface in an Activity.

  You can combine multiple fragments in a single activity to build a multi-pane UI and reuse a fragment in multiple activities. You can think of a

    fragment as a modular section of an activity, which has its own lifecycle, receives its own input events, and which you can add or remove

    while the activity is running (sort of like a "sub activity" that you can reuse in different activities).

  A fragment must always be embedded in an activity and the fragment's lifecycle is directly affected by the host activity's lifecycle

  

2. Design Philosophy

  For example, a news application can use one fragment to show a list of articles on the left and another fragment to display an article on the

     right—both fragments appear in one activity, side by side, and each fragment has its own set of lifecycle callback methods and handle

     their own user input events. Thus, instead of using one activity to select an article and another activity to read the article, the user can

     select an article and read it all within the same activity, as illustrated in the tablet layout in figure 1.

    

  

3. Creating a Fragment

    

  There are also a few subclasses that you might want to extend, instead of the base Fragment class:

DialogFragment
Displays a floating dialog. Using this class to create a dialog is a good alternative to using the dialog helper methods in the Activity class, because you can incorporate a fragment dialog into the back stack of fragments managed by the activity, allowing the user to return to a dismissed fragment.
ListFragment
Displays a list of items that are managed by an adapter (such as a SimpleCursorAdapter), similar to ListActivity. It provides several methods for managing a list view, such as the onListItemClick() callback to handle click events.
PreferenceFragment
Displays a hierarchy of Preference objects as a list, similar to PreferenceActivity. This is useful when creating a "settings" activity for your application.

 

4. Adding a user interface

  To provide a layout for a fragment, you must implement the onCreateView() callback method, which the Android system calls when it's time

    for the fragment to draw its layout. Your implementation of this method must return a View that is the root of your fragment's layout.

  To return a layout from onCreateView(), you can inflate it from a layout resource defined in XML. To help you do so, onCreateView() provides

    a LayoutInflater object

 

public static class ExampleFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.example_fragment, container, false);
    }
}
View Code

 

  The container parameter passed to onCreateView() is the parent ViewGroup (from the activity's layout) in which your fragment layout will be

    inserted.

  The savedInstanceState parameter is a Bundle that provides data about the previous instance of the fragment, if the fragment is being resumed

  The inflate() method takes three arguments:

    <1>The resource ID of the layout you want to inflate

    <2>The ViewGroup to be the parent of the inflated layout. Passing the container is important in order for the system to apply layout

      parameters to the root view of the inflated layout, specified by the parent view in which it's going.

    <3>A boolean indicating whether the inflated layout should be attached to the ViewGroup (the second parameter) during inflation.

      (In this case, this is false because the system is already inserting the inflated layout into the container—passing true would create a

      redundant view group in the final layout.)

 

5. Adding a fragment to an activity

  Usually, a fragment contributes a portion of UI to the host activity, which is embedded as a part of the activity's overall view hierarchy.

    There are two ways you can add a fragment to the activity layout:

  <1>Declare the fragment inside the activity's layout file

<?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:name="com.example.news.ArticleListFragment"
            android:id="@+id/list"
            android:layout_weight="1"
            android:layout_width="0dp"
            android:layout_height="match_parent" />
    <fragment android:name="com.example.news.ArticleReaderFragment"
            android:id="@+id/viewer"
            android:layout_weight="2"
            android:layout_width="0dp"
            android:layout_height="match_parent" />
</LinearLayout>
View Code

    The android:name attribute in  <fragment> specifies the Fragment class to instantiate in the layout

    When the system creates this activity layout, it instantiates each fragment specified in the layout and calls the onCreateView() method for each

      one, to retrieve each fragment's layout. The system inserts the View returned by the fragment directly in place of the <fragment> element.

  <2>Or progammatically add the fragment to an existing ViewGroup   

    At any time while your activity is running, you can add fragments to your activity layout. You simply need to specify a ViewGroup in which to

      place the fragment. To make fragment transactions in your activity (such as add, remove, or replace a fragment), you must use APIs

      from FragmentTransaction. You can get an instance of FragmentTransaction from your Activity like this:

FragmentManager fragmentManager = getFragmentManager()
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
View Code

    You can then add a fragment using the add() method, specifying the fragment to add and the view in which to insert it. For example:

ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();
View Code

   

6. Managing Fragments

  To manage the fragments in your activity, you need to use FragmentManager. To get it, call getFragmentManager() from your activity.

 

7. Performing Fragment Trasactions

  A great feature about using fragments in your activity is the ability to add, remove, replace, and perform other actions with them, in response

    to user interaction. Each set of changes that you commit to the activity is called a transaction and you can perform one using APIs in

    FragmentTransaction. You can also save each transaction to a back stack managed by the activity, allowing the user to navigate backward

    through the fragment changes (similar to navigating backward through activities).

  Each transaction is a set of changes that you want to perform at the same time. You can set up all the changes you want to perform for a

    given transaction using methods such as add(), remove(), and replace(). Then, apply the transaction to the activity, you must call commit().

  For example, here's how you can replace one fragment with another, and preserve the previous state in the back stack: 

// Create new fragment and transaction
Fragment newFragment = new ExampleFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();

// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);

// Commit the transaction
transaction.commit();
View Code

  

8. Communicating with the Activity

  Although a Fragment is implemented as an object that's independent from an Activity and can be used inside multiple activities, a given

    instance of a fragment is directly tied to the activity that contains it.

View listView = getActivity().findViewById(R.id.list);
View Code

  

9. Creating event callback  to the activity

  In some cases, you might need a fragment to share events with the activity. A good way to do that is to define a callback interface inside the

    fragment and require that the host activity implement it. When the activity receives a callback through the interface, it can share the

    information with other fragments in the layout as necessary.

  For example, if a news application has two fragments in an activity—one to show a list of articles (fragment A) and another to display an

    article (fragment B)—then fragment A must tell the activity when a list item is selected so that it can tell fragment B to display the article.

    In this case, the OnArticleSelectedListener interface is declared inside fragment A:

public static class FragmentA extends ListFragment {
    ...
    // Container Activity must implement this interface
    public interface OnArticleSelectedListener {
        public void onArticleSelected(Uri articleUri);
    }
    ...
}
View Code

  Then the activity that hosts the fragment implements the OnArticleSelectedListener interface and overrides onArticleSelected() to notify

    fragment B of the event from fragment A.

  To ensure that the host activity implements this interface, fragment A's onAttach() callback method (which the system calls when adding the

     fragment to the activity) instantiates an instance of OnArticleSelectedListener by casting the Activity that is passed into onAttach():

public static class FragmentA extends ListFragment {
    OnArticleSelectedListener mListener;
    ...
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            mListener = (OnArticleSelectedListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener");
        }
    }
    ...
}
View Code

  If the activity has not implemented the interface, then the fragment throws a ClassCastException

  For example, if fragment A is an extension of ListFragment, each time the user clicks a list item, the system calls onListItemClick() in the

    fragment, which then calls onArticleSelected() to share the event with the activity 

public static class FragmentA extends ListFragment {
    OnArticleSelectedListener mListener;
    ...
    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        // Append the clicked item's row ID with the content provider Uri
        Uri noteUri = ContentUris.withAppendedId(ArticleColumns.CONTENT_URI, id);
        // Send the event and Uri to the host activity
        mListener.onArticleSelected(noteUri);
    }
    ...
}
View Code

 

10. Fragment Lifecycle

    

 

11. Examples

  To bring everything discussed in this document together, here's an example of an activity using two fragments to create a two-pane layout.

    The activity below includes one fragment to show a list of Shakespeare play titles and another to show a summary of the play when

    selected from the list. It also demonstrates how to provide different configurations of the fragments, based on the screen configuration.

    

    First, However, not all screen configurations are wide enough to show both the list of plays and the summary, side by side. So, the layout

      is used only for the landscape screen configuration, by saving it at res/layout-land/fragment_layout.xml.

    Thus, when the screen is in portrait orientation, the system applies the following layout, which is saved at res/layout/fragment_layout.xml

      

//fragment_layout.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" 
    android:layout_height="match_parent">
    
    <fragment
        class = "mirror.android.fragmentactivity.MainActivity$TitleFragment"//修改资源文件之后, 需要说明class = 包名 + 类名A$类名B
        android:id="@+id/titles"
        android:layout_width="match_parent" 
        android:layout_height="match_parent" />
    
</FrameLayout>
View Code
///land\fragment_layout
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent" 
    android:layout_height="match_parent">

    <fragment
        class = "mirror.android.fragmentactivity.MainActivity$TitleFragment" 
        android:id="@+id/titles" 
        android:layout_weight="1"
        android:layout_width="0px" 
        android:layout_height="match_parent" />

    <FrameLayout 
        android:id="@+id/details" 
        android:layout_weight="1"
        android:layout_width="0px" 
        android:layout_height="match_parent" />

</LinearLayout>
View Code

    fragment_layout.xml includes only TitlesFragment. This means that, when the device is in portrait orientation, only the list of play titles is

      visible. So, when the user clicks a list item in this configuration, the application will start a new activity to show the summary, instead

      of loading a second fragment.

    As you inspect this code, notice that there are two possible behaviors when the user clicks a list item: depending on which of the two

      layouts is active, it can either create and display a new fragment to show the details in the same activity (adding the fragment to the

      FrameLayout), or start a new activity (where the fragment can be shown).

//MainActivity
package mirror.android.fragmentactivity;

import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.app.ListFragment;
import android.content.Intent;
import android.os.Bundle;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.ScrollView;
import android.widget.Scroller;
import android.widget.TextView;


public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.fragment_layout);
    }
    
    public static class TitleFragment extends ListFragment{
        //To tell tablet and phones
        boolean mDualPane;
        int mCurCheckPosition;
        
        @Override
        public void onActivityCreated(Bundle savedInstanceState) {
            // TODO Auto-generated method stub
            super.onActivityCreated(savedInstanceState);
            
            // Populate list with our static array of titles.
            setListAdapter(new ArrayAdapter<String>(getActivity(),
                    android.R.layout.simple_list_item_activated_1, 
                    Shakespeare.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);
            }
        }

        @Override
        public void onSaveInstanceState(Bundle outState) {
            // TODO Auto-generated method stub
            super.onSaveInstanceState(outState);
            outState.putInt("curChoice", mCurCheckPosition);
        }

        @Override
        public void onListItemClick(ListView l, View v, int position, long id) {
            // TODO Auto-generated method stub
            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_fragment = (DetailsFragment)getFragmentManager().findFragmentById(R.id.details);
                if (details_fragment == null || details_fragment.getShownIndex() != index)
                {
                    // Make new fragment to show this selection.
                    details_fragment = 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_fragment);
                    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);
            }
        }
    }

    public static class DetailsFragment extends Fragment{
        /**
         * Create a new instance of DetailsFragment, initialized to
         * show the text at 'index'.
         */
        public static DetailsFragment newInstance(int index){
            DetailsFragment f = new DetailsFragment();
            
            // Supply index input as an argument.
            Bundle bundle = new Bundle();
            bundle.putInt("index", index);
            f.setArguments(bundle);

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

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            // TODO Auto-generated method stub
            if(container == null)
                // We have different layouts, and in one of them this
                // fragment's containing frame doesn't exist.  The fragment
                // may still be created from its saved state, but there is
                // no reason to try to create its view hierarchy because it
                // won't be displayed.  Note this is not needed -- we could
                // just run the code below, where we would create and return
                // the view hierarchy; it would just never be used.
                return null;
            ScrollView scroller = new ScrollView(getActivity());
            TextView text = new TextView(getActivity());
            int padding = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                    4, getActivity().getResources().getDisplayMetrics());
            text.setPadding(padding, padding, padding, padding);
            scroller.addView(text);
            text.setText(Shakespeare.DIALOGUE[getShownIndex()]);
            return scroller;
        }
    }
}
View Code
//DetailsActivty
package mirror.android.fragmentactivity;

import mirror.android.fragmentactivity.MainActivity.DetailsFragment;
import android.app.Activity;
import android.content.res.Configuration;
import android.os.Bundle;

public class DetailsActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        
        if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE){
            // If the screen is now in landscape mode, we can show the
            // dialog in-line with the list so we don't need this activity.
            finish();
            return;
        }
        
        if(savedInstanceState == null){
            // During initial setup, plug in the details fragment.
            DetailsFragment details = new DetailsFragment();
            details.setArguments(getIntent().getExtras());
            getFragmentManager().beginTransaction().add(android.R.id.content, details).commit();
        }
    }
}
View Code
//Shakespeare.java
package mirror.android.fragmentactivity;

public interface Shakespeare {
    public static String[] DIALOGUE={"Detail of A","Detail of B","Detail of C","Detail of D"};
    public static String[] TITLES={"A","B","C","D"};
}
View Code

    Add DetailsActivity to the AndroirMainfest.xml

    

    Run , ManiActivity OnCreateActivity ---> the initial state is different as below.

    

     

转载于:https://www.cnblogs.com/iMirror/p/4046754.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值