Android Programming The Big Nerd Ranch Guide 第11-15章

第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中队状态进行保存。

 

Leave a Reply

Your email address will not be published.