一、简介
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。
(未完待续...)