第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名。
android:id="@+id/helloMoonFragment"
android:name="com.coder4.android.hellomoon.HelloMoonFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" >
然后在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:
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中队状态进行保存。