第11章
1、Android基本的左右滑屏(Swipe),是通过ViewPager组件实现的。
2、ViewPager需要关联一个PagerAdapter。由后者提供滑动时所需要展现的Fragment。
3、PagerAdapter大致有2种:FragmentStatePagerAdapter、FragmentPagerAdapter。
前者的用法如下:
mViewPager.setAdapter(new FragmentStatePagerAdapter(fm) { @Override public int getCount() { return mCrimes.size(); } @Override public Fragment getItem(int i) { Crime crime = mCrimes.get(i); return CrimeFragment.newInstance(crime.getID()); } });
4、如上所述,getCount()需要提供Page的总数。getItem需要返回滑动到第i屏时,要展现哪个Fragment。
5、可以通ViewPager的setCurrentItem方法,设定当前(或者初始)选择到的是第几屏幕。
mViewPager.setCurrentItem(i);
6:FragmentStatePagerAdapter 与 FragmentPagerAdapter的区别:
- FragmentStatePagerAdapter:当不再使用时,即时销毁FragmentManager中的Fragment。
- FragmentPagerAdapter:当不再使用时,只在FragmentManager中标记为detach。不主动进行销毁。当Pager量小时,用这种方法显然响应速度更快(相当于有了Cache)。
第12章
1、Dialog是浮现于Activity之上的对话框。常见的子类有AlertDialog等。
2、AlertDialog一般要被封装在一个DialogFragment内,否则:手机旋转后Dialog会被销毁。
封装方法即需要重载onCreateDialog函数,如下:
public class DatePickerFragment extends DialogFragment { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { return new AlertDialog.Builder(getActivity()) .setTitle(R.string.date_picker_title) .setPositiveButton(android.R.string.ok, null).create(); } }
其中,setTitle设定标题,setPositiveButton设定“确定”按钮的文本,以及按下之后的动作。这里用null表示暂时无动作。
3、除了上述PositiveButton,还可以添加NeutralButton。通过setNeutralButton添加。
4、显示Dialog,也要通过其外置的Fragment,即DialogFragment.show(FragmentManager)。
FragmentManager fm = getFragmentManager(); DatePickerFragment dialog = DatePickerFragment.newInstance(mCrime.getDate()); dialog.show(fm, "other title");
要显示DialogFragment,必须要先获取FragmentManager,这个很常规了。
5、让Dialog加载自定义的layout.xml,在Builder后,增加setView
public class DatePickerFragment extends DialogFragment { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { return new AlertDialog.Builder(getActivity()).setView(v) .setTitle(R.string.date_picker_title) .setPositiveButton(android.R.string.ok, null).create(); } }
6、如何向DialogFragment传递参数呢?与传统的Fragment之间传递无差异。
假设A(CrimeFragment)为向B(DateDialogFragment)要传递参数。
在A端,使用Bundle和setArguments传入:
Bundle args = new Bundle(); args.putSerializable(EXTRA_DATE, date); DatePickerFragment fragment = new DatePickerFragment(); fragment.setArguments(args);
在B端,通过getArguments读出:
Date mDate = (Date) getArguments().getSerializable(EXTRA_DATE);
7、Dialog如何将数据传递给调用它的Fragment?
依然假设在Fragment A调用B Fragment,B包装了Dialog C。
希望数据从C返回给A
A端,设定Fragment B的TargetFragment为A自身:
dialog.setTargetFragment(CrimeFragment.this, REQUEST_DATE);
同时,需要设定好返回接口,即onActivityResult,requestCode用于区分A调用的不同Fragment,resultCode是C向A发送的状态码(成功or失败),Intent data就是要传递的数据啦:
@Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode != Activity.RESULT_OK) { return; } if (requestCode == REQUEST_DATE) { Date date = (Date) data .getSerializableExtra(DatePickerFragment.EXTRA_DATE); mCrime.setDate(date); updateDate(); } }
B端,调用onActivityResult返回,Intent传递数据,这就是上面那个回掉。。:
if (getTargetFragment() == null) { return; } Intent i = new Intent(); i.putExtra(EXTRA_DATE, mDate); getTargetFragment().onActivityResult( CrimeFragment.REQUEST_DATE, Activity.RESULT_OK, i);
8、关于Dialog点击确定后的事件,需要通过setPositiveButton的第三个参数传递:
mDatePicker.init(year, month, day, new OnDateChangedListener() { @Override public void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth) { mDate = new GregorianCalendar(year, monthOfYear, dayOfMonth) .getTime(); getArguments().putSerializable(EXTRA_DATE, mDate); } });
第13章
1、对于Fragment,除了通过FragmentActivtity + FrameLayout + FragmentManager的方法载入外。也可以直接使用fragment组件,在其中写好要载入的Fragment的包名class名。
<fragment xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/helloMoonFragment" android:name="com.coder4.android.hellomoon.HelloMoonFragment" android:layout_width="match_parent" android:layout_height="match_parent" > </fragment>
然后在Activity中直接加载:
public class HelloMoonActivity extends FragmentActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_hello_moon); } }
2、这样做比较简单,省却了FragmentManager等步骤,但是失去了随时载入新Fragment的自由,一旦加载起来,就无法替换为其他的Fragment。
3、音频播放,通过MediaPlayer组件完成,注意stop后,要及时release资源:
package com.coder4.android.hellomoon; import android.content.Context; import android.media.MediaPlayer; import android.media.MediaPlayer.OnCompletionListener; public class AudioPlayer { private MediaPlayer mPlayer = null; public void stop() { if (mPlayer != null) { mPlayer.stop(); mPlayer.release(); mPlayer = null; } } public void play(Context c) { mPlayer = MediaPlayer.create(c, R.raw.one_small_step); mPlayer.setOnCompletionListener(new OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { stop(); } }); mPlayer.start(); } }
4、视频播放,建议使用VideoView:
<VideoView android:id="@+id/videoView" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1" />
mVideoView = (VideoView) v.findViewById(R.id.videoView); mVideoView.setOnCompletionListener(new OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { // getActivity().finish(); } }); mVideoView.setOnErrorListener(new OnErrorListener() { @Override public boolean onError(MediaPlayer mp, int what, int extra) { return false; } });
mPlayButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mVideoView.setVideoPath("/sdcard/apollo_17_stroll.mp4"); mVideoView .setMediaController(new MediaController(getActivity())); mVideoView.start(); mVideoView.requestFocus(); } });
5、在Fragment的onCreate中加入,setRetainInstance(true),可以防止因为 屏幕Rotation等导致的销毁、重建Fragment过程。
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setRetainInstance(true); }
默认情况,setRetainInstance(false):
需要重建Activity、FM、下挂的Fragment以及View,如下图所示:
设定了setRetainInstance(true)后:
Fragment将被保留,并下挂到新FragmentManager下,如下图所示:
6、如果Activity由于内存不足被Android os回收,则上面setRetainInstance也无法保证Fragment不被销毁。所以,建议可能的话还是在onSaveInstanceState中队状态进行保存。