Android Activity数据间传递媒介Intent和任务与后退栈(进阶之路)

一、简介

 1. 通过进阶之路,学习对多个Activity的操作,最终了解以下知识点:

  • 不通过向导,创建新的Activity及配套布局。
  • 从一个Activity启动另外一个Activity。即,请求操作系统创建一个新的Activity实例,并调用它的onCreate(Bundle)成员方法。
  • 在父Activity(启动方)与子Acitity(被启动方)间进行数据传递。

 2. 了解Activity的任务与后退栈

二、创建Activity子类与新布局

  • 创建新布局

  选择New-->Layout source file选项,再填相应的配置。

  • 创建Activity子类

  选择New-->Java Class选项,新建一个继承于android.app.Activity的子类。在新的Activity子类中,覆盖方法onCreate(),即可。

  • 为新建的Activity在ActivityManager中进行注册。

  在AndroidManifest.xml文件中,添加新建的Activity,进入注册。

<activity android:name=".MainActivity">
    <intent-filter>
        <!-- 设置当前Activity为应用的第一个Activity -->
        <action android:name="android.intent.action.MAIN"/>
        <!-- 此应用程序是否显示在系统的程序列表中(此配置仅需在第一个Activity中配置) -->
        <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
</activity>

 

三、启动Activity

  一个Activity启动另一个Activity,最简单的方法就是使用以下Activity方法:

1 public void startActivity(Intent intent)
2 {
3 
4 }

  从形式上,startActivity(...)方法是Activity类的成员方法,实际上并非是这样。Activity调用startActivity(...)方法,是通过startActivity(...)方法向OS发送消息。准确来说,是通过startActivity(...)在向OS的ActivityManager类发送消息,由ActivityManager类收到消息后,创建Activity实例,并且调用onCreate(Bundel)方法。如下图所示:

                 (此图来自于android书籍权威指南)

四、Activity间传递数据

  Activity间传递数据是通过Intent实现的,是基于Intent的通信。Intent对象是component与OS进行通信的一个媒介,即通信数据载体。还有一些其它的component,比如:service、broadcast receiver以及content provider。Intent又分为显示Intent与隐式Intent,详细查看Intent详解篇。(http://www.cnblogs.com/naray/p/5300592.html

  1. 使用Intent作为媒介传递数据:

1 // 创建intent媒介,第一个参数为当前activity实例,第二个参数为将要创建的activity类信息
2 Intent i = new Intent(getActivity(), CrimeActivity.class);
3 // 附加extra信息
4 i.putExtra(CrimeFragment.sExtra_Crime_ID, c.getmId());
5 // 通过os,activityManager启动activity
6 startActivity(i);

  2.在新创建的activity中获取附加extra信息,即Intent实例对象,有两个方式获取,即简单直接方式和复杂灵活方式:

  • 简单直接方式,使用getActivity()方法,获取托管的Activity对象,从而获取Intent对象:
    1 // 附加extra信息,在创建activity对象时初始化,通过getIntent()方法获取Intent对象
    2 UUID crimeId = (UUID)getActivity().getIntent().getSerializableExtra(sExtra_Crime_ID);
    3 // 单例数据对象中,获取指定数据
    4 mCrime = CrimeLab.get(getActivity()).getCrime(crimeId);

    PS:此种方式简单直接,但缺点是使用fragment不再是可复用的,因它需要某个具体的Activity托管者。

  • 复杂灵活方式,使用fragment argument传递数据,每个fragment实例都可以有一个Bundle对象。该Bundle包含有键-值对(key-value),可以像附加extra到Activity的Intent中,一样使用Bundle对象。

  (1). 附加extra信息argument给Fragment,要创建Fragment argument,首先需创建Bundle对象,然后,使用Bundle限定类型的“put”方法(类似于Intent的方法),将argument添加到Bundle对象中。如下所示:

1 // 创建Bundle对象
2 Bundle args = New Bundle();
3 // 附加extra数据,以键-值对形式
4 // put一个boolean类型值
5 args.putSerializable(EXTRA_MY_OBJECT, myObject);
6 // put一个int类型值
7 args.putInt(EXTRA_MY_INT, myInt);
8 // put一个字符串类型值
9 args.putCharSequence(EXTRA_MY_STRING, myString);

  (2). 获取Fragment的argument,只需要通过Fragment类的getArgument()方法,即可:

1 UUID crimeId = (UUID)getArguments().getSerializable(sExtra_Crime_ID);

Demo:

获取Intent对象数据,赋给Fragment对象的argument:(CrimePagerActivity.java,使用ViewPager视图显示详情界面)

 1 public class CrimePagerActivity extends SingleFragmentActivity
 2 {
 3     private ViewPager mViewPager;
 4     private ArrayList<Crime> mCrimes;
 5 
 6     public CrimePagerActivity()
 7     {
 8     }
 9 
10     @Override
11     public Fragment createFragment()
12     {
13         return null;
14     }
15 
16     @Override
17     public void onCreate(Bundle savedInstanceState)
18     {
19         super.onCreate(savedInstanceState);
20 
21         // 实例化ViewPager
22         mViewPager = new ViewPager(this);
23         // 为ViewPager实例配置资源ID
24         mViewPager.setId(R.id.viewPager);
25         // 定制预加载相邻页面的数目
26         mViewPager.setOffscreenPageLimit(3);
27         // 设置为Activity的容器
28         setContentView(mViewPager);
29 
30         mCrimes = CrimeLab.get(this).getCrimes();
31 
32         // fragment manager
33         FragmentManager fm = getSupportFragmentManager();
34         mViewPager.setAdapter(new FragmentStatePagerAdapter(fm)
35         {
36             // 获取当前视图的Fragment对象
37             @Override
38             public Fragment getItem(int position)
39             {
40                 Crime crime = mCrimes.get(position);
41                 return CrimeFragment.newInstance(crime.getmId());
42             }
43 
44             // 获取数据条数,计算显示视图个数
45             @Override
46             public int getCount()
47             {
48                 return mCrimes.size();
49             }
50         });
51 
52         // 设置从列表进入详细界面的具体数据
53         for (int idx = 0; idx < mCrimes.size(); idx++)
54         {
55             UUID code = (UUID) getIntent().getSerializableExtra(CrimeFragment.sExtra_Crime_ID);
56             if (mCrimes.get(idx).getmId().equals(code))
57             {
58                 // 设置界面当前显示的View
59                 mViewPager.setCurrentItem(idx);
60                 break;
61             }
62         }
63 
64         mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener()
65         {
66             @Override
67             public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
68             {
69 
70             }
71 
72             @Override
73             public void onPageSelected(int position)
74             {
75                 Crime crime = mCrimes.get(position);
76                 if (null != crime.getmTitle())
77                 {
78                     setTitle(crime.getmTitle());
79                 }
80             }
81 
82             @Override
83             public void onPageScrollStateChanged(int state)
84             {
85 
86             }
87         });
88     }
89 
90 }

获取Fragment的argument:(CrimeFragment.java)

  1 public class CrimeFragment extends Fragment
  2 {
  3     private Crime mCrime;
  4     private EditText mEditText;
  5     private Button mDateBtn;
  6     private CheckBox mSolvedCheckBox;
  7     private static final String TAG = "CrimeFragment";
  8     // extra key
  9     public static final String sExtra_Crime_ID = "crimeId";
 10     private static final String sCrime_date = "date";
 11     private static final int REQUEST_DATE = 0x002;
 12 
 13     public static CrimeFragment newInstance(UUID crimeId)
 14     {
 15         Bundle args = new Bundle();
 16         args.putSerializable(sExtra_Crime_ID, crimeId);
 17         CrimeFragment fragment = new CrimeFragment();
 18         fragment.setArguments(args);
 19         return fragment;
 20     }
 21 
 22     @Override
 23     public void onCreate(Bundle savedInstanceState)
 24     {
 25         super.onCreate(savedInstanceState);
 26         // 附加extra信息,在创建activity对象时初始化,通过getIntent()方法获取Intent对象
 27 //        UUID crimeId = (UUID)getActivity().getIntent().getSerializableExtra(sExtra_Crime_ID);
 28         // 单例数据对象中,获取指定数据
 29         UUID crimeId = (UUID) getArguments().getSerializable(sExtra_Crime_ID);
 30         mCrime = CrimeLab.get(getActivity()).getCrime(crimeId);
 31     }
 32 
 33     @Override
 34     public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState)
 35     {
 36         View v = inflater.inflate(R.layout.fragment_crime, parent, false);
 37 
 38         mEditText = (EditText) v.findViewById(R.id.crime_title);
 39         mEditText.setText(mCrime.getmTitle());
 40         mEditText.addTextChangedListener(new TextWatcher()
 41         {
 42             @Override
 43             public void beforeTextChanged(CharSequence s, int start, int count, int after)
 44             {
 45                 mCrime.setmTitle(s.toString());
 46             }
 47 
 48             @Override
 49             public void onTextChanged(CharSequence s, int start, int before, int count)
 50             {
 51 
 52             }
 53 
 54             @Override
 55             public void afterTextChanged(Editable s)
 56             {
 57 
 58             }
 59         });
 60 
 61         mDateBtn = (Button) v.findViewById(R.id.crime_date);
 62         mDateBtn.setText(mCrime.getmDate().toString());
 63 //        mDateBtn.setEnabled(false);
 64         mDateBtn.setOnClickListener(new View.OnClickListener()
 65         {
 66             @Override
 67             public void onClick(View v)
 68             {
 69                 FragmentManager fm = getActivity().getSupportFragmentManager();
 70 //                DatePickerFragment dialog = new DatePickerFragment();
 71                 DatePickerFragment dialog = DatePickerFragment.newInstanceState(mCrime.getmDate());
 72                 dialog.setTargetFragment(CrimeFragment.this, REQUEST_DATE);
 73                 dialog.show(fm, sCrime_date);
 74             }
 75         });
 76 
 77         mSolvedCheckBox = (CheckBox) v.findViewById(R.id.crime_solved);
 78         mSolvedCheckBox.setChecked(mCrime.getmSolved());
 79         mSolvedCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener()
 80         {
 81             @Override
 82             public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
 83             {
 84                 mCrime.setmSolved(isChecked);
 85             }
 86         });
 87 
 88         Button choose = (Button) v.findViewById(R.id.choose_btn);
 89         choose.setOnClickListener(new Button.OnClickListener()
 90         {
 91             @Override
 92             public void onClick(View v)
 93             {
 94                 Intent i = new Intent(Intent.ACTION_SEND);
 95                 i.setType("text/plain");
 96                 i.putExtra(Intent.EXTRA_TEXT, "Choose");
 97                 i.putExtra(Intent.EXTRA_SUBJECT, "Choose Suspect");
 98                 i = Intent.createChooser(i, "Chooser");
 99                 startActivity(i);
100             }
101         });
102 
103         Button contacter = (Button) v.findViewById(R.id.contact_btn);
104         contacter.setOnClickListener(new View.OnClickListener()
105         {
106             @Override
107             public void onClick(View v)
108             {
109                 Intent i = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI);
110                 i.setType("text/plain");
111                 startActivityForResult(i, 2);
112             }
113         });
114 
115         return v;
116     }
117 
118     @Override
119     public void onActivityResult(int requestCode, int resultCode, Intent data)
120     {
121         if (resultCode != Activity.RESULT_OK)
122         {
123             return;
124         }
125 
126         switch (requestCode)
127         {
128             case REQUEST_DATE:
129             {
130                 Log.d(TAG, "result data: " + data);
131                 mDateBtn.setText(data.toString());
132                 break;
133             }
134             default:
135                 break;
136         }
137     }
138 }

   显示日历picker,创建DatePickerFragment.java:

 1 public class DatePickerFragment extends DialogFragment
 2 {
 3     private static final String EXTRA_DATA = "date";
 4     private Date mDate;
 5 
 6     public DatePickerFragment()
 7     {
 8     }
 9 
10     public static DatePickerFragment newInstanceState(Date date)
11     {
12         Bundle args = new Bundle();
13         args.putSerializable(EXTRA_DATA, date);
14 
15         DatePickerFragment datePickerFragment = new DatePickerFragment();
16         datePickerFragment.setArguments(args);
17         return datePickerFragment;
18     }
19 
20     @Override
21     public Dialog onCreateDialog(Bundle savedInstanceState)
22     {
23         // 获取日期数据
24         mDate = (Date) getArguments().getSerializable(EXTRA_DATA);
25         int year = 0, month = 0, day = 0;
26 
27         if (null != mDate)
28         {
29             // calender
30             Calendar calendar = Calendar.getInstance();
31             calendar.setTime(mDate);
32             year = calendar.get(Calendar.YEAR);
33             month = calendar.get(Calendar.MONTH);
34             day = calendar.get(Calendar.DAY_OF_MONTH);
35         }
36 
37         View v = getActivity().getLayoutInflater().inflate(R.layout.dialog_date, null);
38 
39         DatePicker datePicker = (DatePicker) v.findViewById(R.id.dialog_date_datepicker);
40         datePicker.init(year, month, day, new DatePicker.OnDateChangedListener()
41         {
42             @Override
43             public void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth)
44             {
45                 mDate = new GregorianCalendar(year, monthOfYear, dayOfMonth).getTime();
46                 getArguments().putSerializable(EXTRA_DATA, mDate);
47             }
48         });
49 
50         return new AlertDialog.Builder(getActivity())
51                 .setView(v)
52                 .setTitle(R.string.date_picker_dialog)
53                 .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener()
54                 {
55                     @Override
56                     public void onClick(DialogInterface dialogInterface, int which)
57                     {
58                         sendResult(Activity.RESULT_OK);
59                     }
60                 }).create();
61     }
62 
63     private void sendResult(int resultCode)
64     {
65         if (null == getTargetFragment())
66         {
67             return;
68         }
69 
70         // 创建Intent对象
71         Intent i = new Intent();
72         // 设置传递的数据
73         i.putExtra(EXTRA_DATA, mDate);
74         // 同一Activity间传递数据,可以通过getTargetFragment()方法,获取另一个fragment实例,
75         // 手动调用onActivityResult(...)方法,传递数据
76         getTargetFragment().onActivityResult(getTargetRequestCode(), resultCode, i);
77     }
78 }

  1. Activity.onActivityResult(...)方法是ActivityManager在子activity销毁后调用的父activity方法。处理activity间的数据返回时,无需亲自动手,ActivityManager会自动调用Activity.onActivityResult(...)方法。父Activity接收到Activity.onActivityResult(...)方法的调用后,其FragmentManager会调用对应fragment的Fragment.onActivityResult(...)方法。

  说白了就是Activity托管各自的fragment,在子Activity销毁后,ActivityManager会自己调用父类的Activity方法。在处理Activity间的数据返回时,无需亲自动手,ActivityManager会自动调用Activity.onActivityResult(...)方法。

  2. 在同一Activity托管两个Fragment间的数据返回时,借用Fragment.onActivityResult(...)方法。因此,直接调用目标fragment的Fragment.onActivityResult(...)方法。即可实现数据的回传,该方法有我们需要的信息:

  • 一个与传入setTargetFragment(...)方法相匹配的请求代码,用以告知目标fragment返回结果来自于哪里。
  • 一个决定下一步该采取什么行动的结果代码。
  • 一个含有extra数据信息的Intent。

五、任务与后退栈

  在每一个应用中,Android OS都使用任务来跟踪用户的状态,任务就是Activity栈。栈底Activity称为基Activity,栈顶Activity是正在显示的Activity,当用户点击回退键时,栈顶Activity会被从栈中弹出(销毁)。如果,后退的Activity是基Activity,那么就会回到OS主屏幕。

  

                     (此图来自于Android书籍权威指南)

  1. 通过在显示Intent设置Activity_flag,来管理任务,即Activity栈中的Activity。

  2. 对显示Intent设置Activity_flag_new_task,新启动的Activity在新的任务栈中。

  3. 在Intent中可以设置多个flag。

(未完待续...)

原文地址:https://www.cnblogs.com/naray/p/5281783.html