有个滑动, 它是个非常不错的控件, 现在本章的内容就是用 ViewPager 实现 crime 的详细页面可以用滑动, 直接划到下一条 crime 的详细页面, 不需要返回, 是不是很赞, 本功能真的很赞噢, 在各大 APP 也是常用的
本章图解:
截取自此书
创建 CrimePagerActivity 类
定义包含 ViewPager 的视图层级结构
在 CrimePagerActivity 类中关联使用 ViewPager 及其 Adapter
修改 CrimeHolder.onClick(...)方法, 转而启动 CrimePagerActivity
创建 CrimePagerActivity
真正码代码的时刻再次到临, 照着写敲一遍增强理解注意, ViewPager 类来自于支持库, 添加布局时包名要完整
- public class CrimePagerActivity extends AppCompatActivity {
- private static final String EXTRA_CRIME_ID =
- "com.bignerdranch.android.criminalintent.crime_id";
- private ViewPager mViewPager;
- private List<Crime> mCrimes;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_crime_pager);
- mViewPager = findViewById(R.id.pager_crime);
- UUID crimeId = (UUID) getIntent().getSerializableExtra(EXTRA_CRIME_ID);
- mCrimes = CrimeLab.get(this).getCrimes();
- FragmentManager fragmentManager = getSupportFragmentManager();
- mViewPager.setAdapter(new FragmentStatePagerAdapter(fragmentManager) {
- @Override
- public Fragment getItem(int position) {
- Crime crime = mCrimes.get(position);
- return CrimeFragment.newInstance(crime.getId());
- }
- @Override
- public int getCount() {
- return mCrimes.size();
- }
- });
- for (int i = 0; i < mCrimes.size(); i++) {
- if (mCrimes.get(i).getId().equals(crimeId)) {
- mViewPager.setCurrentItem(i);
- break;
- }
- }
- }
- public static Intent newIntent(Context packageContext, UUID crimeId) {
- Intent intent = new Intent(packageContext, CrimePagerActivity.class);
- intent.putExtra(EXTRA_CRIME_ID, crimeId);
- return intent;
- }
- }
setOffscreenPageLimit(int)方法, 可定制预加载相邻页面的数目
FragmentStatePagerAdapter 是代理, 负责管理 ViewPager 的对话并协同工作, 将返回的 fragment 添加给托管 activity, 并帮助 ViewPager 找到 fragment 的视图并一一对应代理需首先将 getItem(int)方法返回的 fragment 添加给 activity, 然后才能使用 fragment 完成自己的工作
FragmentStatePagerAdapter 与 FragmentPagerAdapter
两者基本一致, 唯一区别在于, 卸载不需要的 fragment 时, 各自处理方式不一样
FragmentStatePagerAdapter 会销毁不需要的 fragment, 事务提交后, activity 的 FragmentManager 中的 fragment 会被彻底移除, 在销毁 fragment 时, 可在 onSaveInstanceState(Bundle)方法中保存 fragment 的 Bundle 信息
FragmentPagerAdapter 会选择调用事务的 detach(Fragment)方法来处理不需要的 fragment, 而非 remove(Fragment)方法即销毁了 fragment 的视图, 而 fragment 实例还保留在 FragmentManager 中
截图取自此书
截取自此书
因此, FragmentPagerAdapter 创建的 fragment 永远不会被销毁通常来说, 使用 FragmentStatePagerAdapter 更节省内存
如果用户界面只需要少量固定的 fragment, 那 FragmentPagerAdapter 是安全合适的选择比如使用 tab 选项页显示用户界面本案例大量 crime 记录, 在内存中保存所有信息就不合适了
深入学习: ViewPager 的工作原理
当需要 ViewPager 托管非 fragment 视图时, 就需要实现原生 PagerAdapter 接口了, 比如有时候看到 app 中的轮番图啊, 刚进入一个 app 引导页面的滑动切换, 都可以使用 ViewPager 实现效果的噢~
典型问题
PagerAdapter 内部实现比 Adapter 复杂点, PagerAdapter.instantiateItem(ViewGroup, int)方法告诉 pager adapter 创建指定位置的列表项视图, 然后将其添加给 ViewGroup 视图容器, destroyItem(ViewGroup, int, Object)方法则告诉 pagerAdapter 销毁已建视图
注意, instantiateItem(ViewGroup, int)方法并不要求立即创建视图因此, PagerAdapter 可自行决定何时创建视图
视图创建完成后, ViewPager 会在某个时间点看到它为确定该视图所属的对象, ViewPager 会调用 isViewFromObject(View, Object) 方法 (Object 参数是 instantiateItem (ViewGroup,int) 方法返回的对象)
FragmentStatePagerAdapter 中 isViewFromObject 方法的具体实现:
- @Override
- public boolean isViewFromObject(View view, Object object) {
- return ((Fragment)object).getView() == view;
- }
FragmentPagerAdapter 和 FragmentStatePagerAdapter 这两个东东真是好用呢, 平常其他的要实现滑动的效果的, 就只能自己去实现 pagerAdapter 来实现自己想要的效果了, 不过想引导页面就一张图片这种, 很容易实现的啦, 不过有的时候设计师加点动态效果呀什么的, 就跟动画相关了, 得加上动画
深入学习: 以代码的方式创建视图
使用以下代码定义 ViewPager(调用视图类的构造方法, 并传入 Context 参数):
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- ViewPager viewPager = new ViewPager(this);
- setContentView(viewPager);
- ...
- }
但是! 布局文件能很好地分离控制器层和视图层对象, 为了更好的维护代码, 还是使用布局文件吧
注意, 由于本章使用到了 ViewPager, 在详情 crime 中可以随时修改若干的详细状态, 因此, 在本章已经将原来那个专门刷新某一条的代码删去, 回到列表还是刷新所有的记录
挑战练习: 恢复 CrimeFragment 的边距
将 android:layout_margin="16dp"
改为 android:padding="16dp"
OK, 设置内边距, 这个问题就搞定了! 哈哈~
挑战练习: 添加 Jump to First 按钮和 Jump to Last 按钮
给 CrimePagerActivity 添加两个按钮允许使用它们快速跳至第一条和最后一条 crime 记录当然, 要注意控制, 查看第一条记录时应禁用 Jump to First 按钮, 查看最后一条时禁用 Jump to Last 按钮
这里需要看清题目, 题目给定意思是在 CrimePagerActivity 中添加按钮, 而不是 fragment 的布局中添加, 这样剧更简单了, 先改布局, 再在代码中添加一些条件即可
布局:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
- <android.support.v4.view.ViewPager
- android:id="@+id/pager_crime"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1" />
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:padding="16dp">
- <Button
- android:id="@+id/btn_to_first"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/to_first" />
- <Button
- android:id="@+id/btn_to_last"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="end"
- android:text="@string/to_last" />
- </FrameLayout>
- </LinearLayout>
然后在代码中给 viewpager 添加个监听器, 监听它的切换, 当切换到第一条或是最后一条, 则隐藏跳上一页或是跳下一页的按钮, 在给两按钮添加个点击事件, 本挑战就解决啦
核心代码:
- mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
- @Override
- public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
- if (position == 0) {
- btnToFirst.setVisibility(View.INVISIBLE);
- } else if (position == mCrimes.size() - 1) {
- btnToLast.setVisibility(View.INVISIBLE);
- } else {
- btnToLast.setVisibility(View.VISIBLE);
- btnToFirst.setVisibility(View.VISIBLE);
- }
- }
- @Override
- public void onPageSelected(int position) {
- }
- @Override
- public void onPageScrollStateChanged(int state) {
- }
- });
- btnToFirst.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- mViewPager.setCurrentItem(0);
- }
- });
- btnToLast.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- mViewPager.setCurrentItem(mCrimes.size() - 1);
- }
- });
好啦, 又完成了一章内容啦, 在自己手动敲写代码的时候, 也有遇到一些小问题, 不过都调试好过去了, 所以说, 还是得多敲代码, 即使是看懂了技术点, 也应该自己实践一遍, 鬼晓得自己敲会不会出现莫名其妙的问题, 可能自己少写了几个单词, 哈哈哈最好敲完再总结一遍, 印象就更加深刻啦
来源: http://www.jianshu.com/p/d874c0cef9ea