Android 点将台:撒豆成兵[- Fragment -]

栏目: Android · 发布时间: 6年前

内容简介:拥有生命周期,含有视图,可视可交互的界面 。后者先创建,前者再销毁注

拥有生命周期,含有视图,可视可交互的界面 。 本文源码可见此处

1.Fragment和Activity生命周期的测试
    --- Fragment和Activity生命周期
    --- Fragment和Fragment切换时生命周期
    --- Fragment与嵌套的子Fragment的生命周期
    
2.Fragment和Activity或其他Fragment数据传递
    --- Activity --> Fragment
    --- Fragment --> Activity
    --- Fragment --> Fragment
    
3.Fragment和ViewPager的结合以及懒加载的实现
4.Fragment中如何载入子Fragment
复制代码

一、生命周期

0.基本的生命周期方法一览

setUserVisibleHint(boolean isVisibleToUser)
    |--- 第一个回调函数(多Fragment时),isVisibleToUser用户是否可见
onCreate(Bundle savedInstanceState):
    |--- Fragment初始化时
onAttach(Context context):
    |--- Fragment与Context已经绑定时
View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState)
    |--- 创建Fragment的布局时(加载布局和findViewById,不建议执行耗时的操作)
onViewCreated(View view, @Nullable Bundle savedInstanceState)
    |--- 当视图创建完成
onActivityCreated( Bundle savedInstanceState)
    |--- 与Fragment绑定的Activity的onCreate方法已经执行完成并返回(可以进行与Activity交互的UI操作)
onViewStateRestored(Bundle savedInstanceState)
    |--- 通知fragment,该视图层已保存
onStart()
    |--- 界面已经可见
onAttachFragment()
    |--- 有子Fragment被添加时回调
onResume()
    |--- 界面已经可交互
onPause()
    |--- 界面已经可见不可交互
onStop()
    |--- 界面不可见
onSaveInstanceState(Bundle outState)
    |--- 保存对象状态
onDestroyView()
    |--- 销毁与Fragment有关的视图,但未与Activity解除绑定
onDestroy()
    |--- 销毁Fragment
onDetach()
    |--- 解除与Activity
复制代码

1、当打开Activity时Fragment的生命周期回调:

Android 点将台:撒豆成兵[- Fragment -]
2019-04-26 16:53:27.702: LifeCycleActivity--onCreate: 
2019-04-26 16:53:27.704: Fragment -卍卍卍卍卍卍卍- onAttach: 
2019-04-26 16:53:27.704: Fragment -卍卍卍卍卍卍卍- onCreate: 
2019-04-26 16:53:27.704: Fragment -卍卍卍卍卍卍卍- onCreateView: 
2019-04-26 16:53:27.711: Fragment -卍卍卍卍卍卍卍- onViewCreated: 
2019-04-26 16:53:27.711: Fragment -卍卍卍卍卍卍卍- onActivityCreated: 
2019-04-26 16:53:27.711: Fragment -卍卍卍卍卍卍卍- onViewStateRestored: 
2019-04-26 16:53:27.714: LifeCycleActivity--onStart: 
2019-04-26 16:53:27.715: Fragment -卍卍卍卍卍卍卍- onStart: 
2019-04-26 16:53:27.720: LifeCycleActivity--onResume: 
2019-04-26 16:53:27.720: Fragment -卍卍卍卍卍卍卍- onResume: 
复制代码

2.当跳转到其他Activity时

Android 点将台:撒豆成兵[- Fragment -]
2019-04-26 17:35:30.611: Fragment -卍卍卍卍卍卍卍- onPause: 
2019-04-26 17:35:30.611: LifeCycleActivity--onPause: 
2019-04-26 17:35:31.342: Fragment -卍卍卍卍卍卍卍- onSaveInstanceState: 
2019-04-26 17:35:31.344: LifeCycleActivity--onSaveInstanceState: 
2019-04-26 17:35:31.345: Fragment -卍卍卍卍卍卍卍- onStop: 
2019-04-26 17:35:31.346: LifeCycleActivity--onStop: 
复制代码

3.跳转后返回时

Android 点将台:撒豆成兵[- Fragment -]
2019-04-26 17:41:28.254: LifeCycleActivity--onRestart: 
2019-04-26 17:41:28.255: LifeCycleActivity--onStart: 
2019-04-26 17:41:28.256: Fragment -卍卍卍卍卍卍卍- onStart: 
2019-04-26 17:41:28.258: LifeCycleActivity--onResume: 
2019-04-26 17:41:28.258: Fragment -卍卍卍卍卍卍卍- onResume: 
复制代码

4.退出该Activity

Android 点将台:撒豆成兵[- Fragment -]
2019-04-26 18:12:25.328: Fragment -卍卍卍卍卍卍卍- onPause: 
2019-04-26 18:12:25.328: LifeCycleActivity--onPause: 
2019-04-26 18:12:25.864: Fragment -卍卍卍卍卍卍卍- onStop: 
2019-04-26 18:12:25.865: LifeCycleActivity--onStop: 
2019-04-26 18:12:25.865: Fragment -卍卍卍卍卍卍卍- onDestroyView: 
2019-04-26 18:12:25.867: Fragment -卍卍卍卍卍卍卍- onDestroy: 
2019-04-26 18:12:25.867: Fragment -卍卍卍卍卍卍卍- onDetach: 
2019-04-26 18:12:25.868: LifeCycleActivity--onDestroy: 
复制代码

5.Fragment被替换时的生命周期

后者先创建,前者再销毁

Android 点将台:撒豆成兵[- Fragment -]
2019-04-26 20:10:20.095 : Fragment -卍卍卍卍卍卍卍- onAttach: 
2019-04-26 20:10:20.095 : Fragment -卍卍卍卍卍卍卍- onCreate: 
2019-04-26 20:10:20.095 : Fragment -卍卍卍卍卍卍卍- onCreateView: 
2019-04-26 20:10:20.112 : Fragment -卍卍卍卍卍卍卍- onViewCreated: 
2019-04-26 20:10:20.112 : Fragment -卍卍卍卍卍卍卍- onActivityCreated: 
2019-04-26 20:10:20.113 : Fragment -卍卍卍卍卍卍卍- onViewStateRestored: 
2019-04-26 20:10:20.113 : Fragment -卍卍卍卍卍卍卍- onStart: 
2019-04-26 20:10:20.115 : Fragment -卍卍卍卍卍卍卍- onResume: 

2019-04-26 20:10:20.115 : Fragment -卍卍卍卍卍卍卍- onPause: 
2019-04-26 20:10:20.115 : Fragment -卍卍卍卍卍卍卍- onStop: 
2019-04-26 20:10:20.116 : Fragment -卍卍卍卍卍卍卍- onDestroyView: 
2019-04-26 20:10:20.118 : Fragment -卍卍卍卍卍卍卍- onDestroy: 
2019-04-26 20:10:20.118 : Fragment -卍卍卍卍卍卍卍- onDetach: 
复制代码

6.嵌套Fragment的生命周期

卍卍卍卍卍卍卍 表示父, ☯☯☯☯☯☯☯☯表示子

---->[打开时]-----------先父后子,注意在onStart之后子Fragment开始attach---------------
2019-04-27 08:36:44.332 : Fragment -卍卍卍卍卍卍卍- onAttach: 
2019-04-27 08:36:44.332 : Fragment -卍卍卍卍卍卍卍- onCreate: 
2019-04-27 08:36:44.346 : Fragment -卍卍卍卍卍卍卍- onCreateView: 
2019-04-27 08:36:44.351 : Fragment -卍卍卍卍卍卍卍- onViewCreated: 
2019-04-27 08:36:44.351 : Fragment -卍卍卍卍卍卍卍- onActivityCreated: 
2019-04-27 08:36:44.351 : Fragment -卍卍卍卍卍卍卍- onViewStateRestored: 
2019-04-27 08:36:44.352 : Fragment -卍卍卍卍卍卍卍- onStart: 
2019-04-27 08:36:44.353 : Fragment -☯☯☯☯☯☯☯☯- onAttach: 
2019-04-27 08:36:44.353 : Fragment -卍卍卍卍卍卍卍- onAttachFragment:  <--- 注意这里
2019-04-27 08:36:44.353 : Fragment -☯☯☯☯☯☯☯☯- onCreate:  
2019-04-27 08:36:44.354 : Fragment -☯☯☯☯☯☯☯☯- onCreateView: 
2019-04-27 08:36:44.358 : Fragment -☯☯☯☯☯☯☯☯- onViewCreated: 
2019-04-27 08:36:44.358 : Fragment -☯☯☯☯☯☯☯☯- onActivityCreated: 
2019-04-27 08:36:44.358 : Fragment -☯☯☯☯☯☯☯☯- onViewStateRestored: 
2019-04-27 08:36:44.358 : Fragment -☯☯☯☯☯☯☯☯- onStart: 
2019-04-27 08:36:44.365 : Fragment -卍卍卍卍卍卍卍- onResume: 
2019-04-27 08:36:44.365 : Fragment -☯☯☯☯☯☯☯☯- onResume: 

---->[退出时]-----------先子后父交替回调---------------
2019-04-27 08:44:13.697 : Fragment -☯☯☯☯☯☯☯☯- onPause: 
2019-04-27 08:44:13.698 : Fragment -卍卍卍卍卍卍卍- onPause: 
2019-04-27 08:44:14.639 : Fragment -☯☯☯☯☯☯☯☯- onStop: 
2019-04-27 08:44:14.639 : Fragment -卍卍卍卍卍卍卍- onStop: 
2019-04-27 08:44:14.654 : Fragment -☯☯☯☯☯☯☯☯- onDestroyView: 
2019-04-27 08:44:14.654 : Fragment -卍卍卍卍卍卍卍- onDestroyView: 
2019-04-27 08:44:14.655 : Fragment -☯☯☯☯☯☯☯☯- onDestroy: 
2019-04-27 08:44:14.655 : Fragment -☯☯☯☯☯☯☯☯- onDetach: 
2019-04-27 08:44:14.655 : Fragment -卍卍卍卍卍卍卍- onDestroy: 
2019-04-27 08:44:14.655 : Fragment -卍卍卍卍卍卍卍- onDetach: 
复制代码

二、数据传递

1. Activity-->Fragment

实现:在Activity传入颜色数据,在Fragment中接收数据并使用

Android 点将台:撒豆成兵[- Fragment -]
---->[在Activity中设置Fragment的参数]-----------------------------------
FragmentManager fm = getFragmentManager();//1.获取FragmentManager
FragmentTransaction ft = fm.beginTransaction();//2.fm开启事务

Bundle bundle = new Bundle();//创建Bundle对象
bundle.putString("data", "#ff0000");//为bundle赋值
BoxFragment boxFragment = new BoxFragment();
boxFragment.setArguments(bundle);//为Fragment设置Arguments

//3.动态添加 (控件id,fragment对象,tag)
ft.add(R.id.fl_fragmemt_content, new ResultFragment());
ft.commit();//4.提交事务

---->[在BoxFragment中读取参数并使用]-----------------------------------
TextView txt = view.findViewById(R.id.id_tv_txt);
Bundle bundle = getArguments();
if (bundle != null) {
    String result = bundle.getString("color");
    view.setBackgroundColor(Color.parseColor(result));
    txt.setText(result);
}
复制代码

这可以稍微改进一下:将Bundle在Fragment中创建,通过一个静态方法+入参创建Fragment

---->[在BoxFragment中添加静态方法]-----------------------------------
public static BoxFragment newInstance(String color) {
    BoxFragment fragment = new BoxFragment();
    Bundle bundle = new Bundle();
    bundle.putString("color", color);
    fragment.setArguments(bundle);
    return fragment;
}

---->[在Activity中创建对象]-----------------------------------
BoxFragment boxFragment = BoxFragment.newInstance("#238AF8")
复制代码

好处在于方便创建Fragment,比如加个红色:

Android 点将台:撒豆成兵[- Fragment -]
BoxFragment radFragment = BoxFragment.newInstance("#ff0000")
复制代码

2. Fragment-->Activity

点击Fragment内的View,Fragment将颜色值传给Activity

Android 点将台:撒豆成兵[- Fragment -]
  • 方式一、通过回调
---->[在BoxFragment中添加回调接口]-----------------------------------
public interface OnDataSend {
    void send(String data);
}
private OnDataSend mOnDataSend;
public void setmOnDataSend(OnDataSend mOnDataSend) {
    this.mOnDataSend = mOnDataSend;
}

---->[在BoxFragment#onViewCreated触发回调]-----------------------------------
txt.setOnClickListener(v -> {
    if (mOnDataSend != null) {
        mOnDataSend.send(txt.getText().toString().toUpperCase());
    }
});

---->[在Activity中设置回调]-----------------------------------
radFragment.setmOnDataSend {
    id_tv_result.text = it
}
复制代码
  • 方式二、向上转型,获取宿主对象并调用方法
---->[在Activity中添加公共方法]-----------------------------------
fun setData(s: String) {
    id_tv_result.text = s
}

---->[在BoxFragment#onViewCreated获取宿主对象并调用方法]-----------
txt.setOnClickListener(v -> {//这里强转最好加个instanceof判断
    LifeCycleActivity activity = (LifeCycleActivity) getActivity();
    activity.setData(txt.getText().toString().toUpperCase());
});
复制代码

挺方便的,不过这对宿主Activity增加了负担,不须强转成宿主类型,感觉有点狭隘

感觉新建一个接口用来规定数据传输的方法,应该会好一些,就像正规军和游击队吧...

//数据传送的接口(当然你可以定义很多接口,这里只举个例子)
public interface IBoxSender {
    void setData(String data);
}

---->[在Activity实现接口]-----------------------------------
class LifeCycleActivity : AppCompatActivity(), IBoxSender {
...
    override fun setData(s: String) {
        id_tv_result.text = s
    }
}

---->[在BoxFragment#onViewCreated获取宿主对象并调用方法]-----------
txt.setOnClickListener(v -> {//这里强转最好加个instanceof判断
    IBoxSender sender = (IBoxSender) getActivity();
    sender.setData(txt.getText().toString().toUpperCase());
});
//从我个人审美来说,这样似乎更优雅一点
复制代码

当然你也可以将控件直接传给Fragment,但感觉太无聊了,就不说了

3.Fragment-->Fragment

红色Fragment在点击时,将自己的颜色值传送给蓝色Fragment,蓝色Fragment接收后变色

很容易想到分两步:红色Fragment --> Activity , Activity --> 蓝色Fragment

Android 点将台:撒豆成兵[- Fragment -]
//数据传送的接口
public interface IBoxSender {
    void setData(String data);
    void update(String color); //增加接口
}

---->[在Activity实现接口方法]-----------------------------------
override fun update(color: String) {
    fragmentManager.beginTransaction()
        .replace(R.id.fl_title, BoxFragment.newInstance(color))
        .commit()
}

---->[在BoxFragment#onViewCreated获取宿主对象并调用方法]-----------
txt.setOnClickListener(v -> {//这里强转最好加个instanceof判断
    IBoxSender sender = (IBoxSender) getActivity();
    sender.update(txt.getText().toString());
});
复制代码

4.Fragment可以写构造函数传入参数吗?

这是曾经让我疑惑的一点:构造函数入参来传参不是挺好的吗?但是:

貌似AS 不给我们用构造,需要通过 Fragment#setArguments(Bundle) 来传参

Android 点将台:撒豆成兵[- Fragment -]

如果我任性,偏要用呢?----虽然画红线但是还是运行还是能跑起来的,效果也没有差别, 于是乎,问题来了: 为什么谷歌的大佬不推荐我们在Fragment中使用构造函数呢?

|--- 在旋转屏幕时:Fragment将面临 销毁+重建 ,但测试中Fragment并没有什么变化
|--- 重建的Fragment是系统帮我们做的,那它怎么还原刚才的参数呢(颜色)?一个词 :Bundle
|--- 下面是Fragment中创建ragment静态方法,其中用反射实例化了无参构造,并将配置参数进行还原
|--- 所以为什么不能自定义Fragment的构造函数不言而喻:Fragment销毁+重建只会使用无参构造

public static Fragment instantiate(Context context, String fname) {
    return instantiate(context, fname, null);
}

public static Fragment instantiate(Context context, String fname, @Nullable Bundle arg)
    try {
        Class<?> clazz = sClassMap.get(fname);
        if (clazz == null) {
            // Class not found in the cache, see if it's real, and try to add it
            clazz = context.getClassLoader().loadClass(fname);
            if (!Fragment.class.isAssignableFrom(clazz)) {
                throw new InstantiationException("Trying to instantiate a class " + f
                        + " that is not a Fragment", new ClassCastException());
            }
            sClassMap.put(fname, clazz);
        }
        Fragment f = (Fragment) clazz.getConstructor().newInstance(); <--- 注意这里用的是无参构造创建f对象
        if (args != null) { <--- 当Binder对象非空时,会将args再给f对象,也就还原了配置
            args.setClassLoader(f.getClass().getClassLoader());
            f.setArguments(args);
        }
        return f;
    } catch (ClassNotFoundException e) {
        .....
    }
}
复制代码

你可以试一下:使用Fragment一参构造,然后转屏时,程序会崩掉,所以咱们还是别任性...

三、Fragment与ViewPager的爱恨情仇

1.最简单的Fragment + ViewPager

Android 点将台:撒豆成兵[- Fragment -]
public class ViewPagerActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_viewpager);
        ViewPager viewPager = findViewById(R.id.id_vp);
        //颜色数组
        String[] colors = new String[]{"#F73815", "#FAA43E", "#FCE73C", "#51F81E", "#1E94F8", "#8CE9F4", "#B24DF4"};
        //详情数组
        String[] info = new String[]{"红", "橙", "黄", "绿", "蓝", "靛", "紫"};
        
        viewPager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {//设置FragmentPagerAdapter
            @Override
            public Fragment getItem(int position) {
                return BoxFragment.newInstance(colors[position],info[position]);
            }
            @Override
            public int getCount() {
                return colors.length;
            }
        });
    }
}
复制代码

2.滑动时Fragment的生命周期回调

  • 打开时

isVisibleToUser 出现了,而且是最先调用的,一开始是红色 isVisibleToUser= true
总的来说就是生成了红色和黄色两个Fragment并对两者其进行了初始化

2019-04-26 23:12:59.444 - isVisibleToUser: 红---false
2019-04-26 23:12:59.444 - isVisibleToUser: 橙---false
2019-04-26 23:12:59.444 - isVisibleToUser: 红---true
2019-04-26 23:12:59.446 - onAttach: 红
2019-04-26 23:12:59.446 - onCreate: 红
2019-04-26 23:12:59.446 - onAttach: 橙
2019-04-26 23:12:59.446 - onCreate: 橙
2019-04-26 23:12:59.447 - onCreateView: 红
2019-04-26 23:12:59.455 - onViewCreated: 红
2019-04-26 23:12:59.456 - onActivityCreated: 红
2019-04-26 23:12:59.456 - onViewStateRestored: 红
2019-04-26 23:12:59.456 - onStart: 红
2019-04-26 23:12:59.456 - onResume: 红
2019-04-26 23:12:59.457 - onCreateView: 橙
2019-04-26 23:12:59.463 - onViewCreated: 橙
2019-04-26 23:12:59.463 - onActivityCreated: 橙
2019-04-26 23:12:59.463 - onViewStateRestored: 橙
2019-04-26 23:12:59.464 - onStart: 橙
2019-04-26 23:12:59.464 - onResume: 橙
复制代码
  • 滑到下一屏()橙色

橙色和用户见面了,所以橙色的 isVisibleToUser= true
这时对黄色Fragment(即下一页)进行了初始化,俗称预加载

2019-04-26 23:16:53.738 - isVisibleToUser: 黄---false
2019-04-26 23:16:53.738 - isVisibleToUser: 红---false
2019-04-26 23:16:53.738 - isVisibleToUser: 橙---true
2019-04-26 23:16:53.739 - onAttach: 黄
2019-04-26 23:16:53.740 - onCreate: 黄
2019-04-26 23:16:53.743 - onCreateView: 黄
2019-04-26 23:16:53.766 - onViewCreated: 黄
2019-04-26 23:16:53.767 - onActivityCreated: 黄
2019-04-26 23:16:53.767 - onViewStateRestored: 黄
2019-04-26 23:16:53.767 - onStart: 黄
2019-04-26 23:16:53.767 - onResume: 黄
复制代码
  • 滑到下一屏(黄色)

黄色和用户见面了,所以黄色的 isVisibleToUser= true 红色(上上页)销毁了,绿色(下一页)进行初始化

2019-04-26 23:19:38.122 - isVisibleToUser: 绿---false
2019-04-26 23:19:38.123 - isVisibleToUser: 橙---false
2019-04-26 23:19:38.123 - isVisibleToUser: 黄---true
2019-04-26 23:19:38.124 - onAttach: 绿
2019-04-26 23:19:38.125 - onCreate: 绿
2019-04-26 23:19:38.129 - onPause: 红
2019-04-26 23:19:38.130 - onStop: 红
2019-04-26 23:19:38.132 - onDestroyView: 红
2019-04-26 23:19:38.140 - onCreateView: 绿
2019-04-26 23:19:38.154 - onViewCreated: 绿
2019-04-26 23:19:38.155 - onActivityCreated: 绿
2019-04-26 23:19:38.155 - onViewStateRestored: 绿
2019-04-26 23:19:38.155 - onStart: 绿
2019-04-26 23:19:38.156 - onResume: 绿
复制代码

之后都是相似的,当前页的上上页(如果有的话)会被销毁,下一页(如果有的话)会被初始化到onResume

3.懒加载的实现

也就是不想要预加载,毕竟有些时候不想提前为以后的消耗买单

  • 方法一、针对Fragment,让其不要加载下一页数据( 但Fragment还是会创建的 )
private boolean initialization; // 界面是否已初始化完毕
private boolean isVisibleToUser; // 是否对用户可见

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    this.isVisibleToUser = isVisibleToUser;
    lazyLoad();
}

private void lazyLoad() {
    if (initialization && isVisibleToUser) {
        loadData();
    }
}

/**
 * 核心加载方法(可抽象)
 */
private void loadData() {
    Log.e("loadData", "initData: "+info);
}

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    Log.e(TAG, "Fragment -卍卍卍卍卍卍卍- onActivityCreated: " + info);
    initialization = true;
    lazyLoad();
    super.onActivityCreated(savedInstanceState);
}
复制代码
  • 方法2:针对ViewPager

预加载是ViewPager的锅,虽然可以设置预加载多个,但是不能不预加载...

锅是这行代码的 private static final int DEFAULT_OFFSCREEN_PAGES = 1;// 默认的加载页面

所以自定义一个View 将ViewPager代码拷贝一份,上面哪行改成0,代码不贴了, 详见此处

但是,考虑到兼容问题,还是用懒加载Fragment比较好,毕竟创建两个对象也没什么大不了,加载数据限制住就OK了

4.ViewPager的动画效果

既然提到ViewPager就简单说一下吧

Android 点将台:撒豆成兵[- Fragment -]
|--- 使用方式 ------------------------------------------
viewPager.setPageTransformer(true, new VPTFadeScale());

public class VPTFadeScale implements ViewPager.PageTransformer {
    private static float MIN_SCALE = 0.7f;

    //A==>B  A的position 0==>-1   B的position 1==>0
    @Override
    public void transformPage(View page, float position) {
        int width = page.getWidth();
        int height = page.getHeight();

        if (position < -1) {//非A、B页
            page.setAlpha(1);
        } else if (position <= 0) {//A页的动画
            page.setAlpha(1 + position * 2);
            page.setScaleX(1);
            page.setScaleY(1);

            page.setPivotX(0);
            page.setPivotY(height / 2);

            page.setRotationX(-100 * position);
            page.setRotationY(-100 * position);

        } else if (position <= 1) {//B页的动画
            page.setAlpha(1 - position);
            page.setTranslationX(width * (-position));
//            0.75~1
            float scaleFactor = MIN_SCALE + (1 - MIN_SCALE) * (1 - Math.abs(position));
            page.setScaleX(scaleFactor);
            page.setScaleY(scaleFactor);
        }
    }
}
复制代码

5、ViewPager滑动监听

根据滑动时的参数可以做一些好玩的事

Android 点将台:撒豆成兵[- Fragment -]
//[]ViewPager滑动监听
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
    /**
     * 当页面滑动过程中的回调
     * @param position             当前滑动页面的位置
     * @param positionOffset       下一页在当前页所占的宽度百分比
     * @param positionOffsetPixels 下一页在当前页所占的宽度像素值
     */
    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels
        Log.e(TAG, "onPageScrolled: " + "position==>"
                + position + "----positionOffset==>"
                + positionOffset + "----positionOffsetPixels" + positionOffsetPixels);
    }
    /**
     * 某个页面被选中(从0计数) 翻页成功才会调用
     * @param position 翻页后的视图在集合中位置
     */
    @Override
    public void onPageSelected(int position) {
        Log.e(TAG, "onPageSelected: " + position);
    }
    /**
     * 页面状态发生变化的回调  1 滑动开始到手指离开前  2 手指离开后到结束之间 0  滑动结束
     * @param state 状态
     */
    @Override
    public void onPageScrollStateChanged(int state) {
        Log.e(TAG, "onPageScrollStateChanged: " + state);
    }
});
复制代码

四、子Fragment的使用

1.基本使用

关键是通过 getChildFragmentManager 获取管理器,注意要在onStart或之后获取

Android 点将台:撒豆成兵[- Fragment -]
---->[PagerFragment]------------一个孩子----------
 @Override
 public void onStart() {
     SideFragment fragment = new SideFragment();
     getChildFragmentManager()
             .beginTransaction()
             .add(R.id.fl_side, fragment)
             .show(fragment)
             .commit();
 }
 
---->[PagerFragment]------------多个孩子----------
@Override
public void onStart() {
    super.onStart();
    SideFragment side = new SideFragment();
    BoxFragment title = BoxFragment.newInstance("#eeeeee");
    BoxFragment footer = BoxFragment.newInstance("#eeeeee");
    getChildFragmentManager()
            .beginTransaction()
            .add(R.id.fl_side, side)
            .add(R.id.fl_title, title)
            .add(R.id.fl_bottom, footer)
            .show(title)
            .show(side)
            .show(footer)
            .commit();
}
复制代码

2.隐藏子Fragment

效果是点击主Fragment侧边栏会显示/隐藏切换

private boolean sideShowing = true;
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    Log.e(TAG, "Fragment -卍卍卍卍卍卍卍- onViewCreated: ");
    super.onViewCreated(view, savedInstanceState);
    view.setOnClickListener(v -> {
        if (sideShowing) {
            hideAt(0);
        } else {
            showAt(0);
        }
        sideShowing = !sideShowing;
    });
}
public void hideAt(int i) {
    List<Fragment> fragments = getChildFragmentManager().getFragments();
    getChildFragmentManager().beginTransaction().hide(fragments.get(i)).commit();
}
public void showAt(int i) {
    List<Fragment> fragments = getChildFragmentManager().getFragments();
    getChildFragmentManager().beginTransaction().show(fragments.get(i)).commit();
}
复制代码

四、其他要说的

1. startActivityForResult + onActivityResult

Android 点将台:撒豆成兵[- Fragment -]
view.setOnLongClickListener(v -> {
    Intent i = new Intent(Intent.ACTION_PICK,
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
    startActivityForResult(i, 0);// 设定结果返回
    return false;
});

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (data != null) {
        switch (requestCode) {
            case 0:
                //打开相册并选择照片,这个方式选择单张
                // 获取返回的数据,这里是android自定义的Uri地址
                Uri selectedImage = data.getData();
                Log.e("startActivityForResult", "startActivityForResult: " + selectedImage);
                break;
        }
    }
}
复制代码

2.动态权限申请

注意申请时用 XXXFragment.this.requestPermissions 否则 onRequestPermissionsResult 无法回调

private static final int PERMISSION_REQ_ID = 22;
private static final String[] REQUESTED_PERMISSIONS = {
        Manifest.permission.RECORD_AUDIO,//录音权限
        Manifest.permission.CAMERA,//相机权限
        Manifest.permission.WRITE_EXTERNAL_STORAGE//SD卡写权限
};

view.setOnClickListener(v -> {//点击申请权限
    if (checkSelfPermission(REQUESTED_PERMISSIONS[0], PERMISSION_REQ_ID) &&
            checkSelfPermission(REQUESTED_PERMISSIONS[1], PERMISSION_REQ_ID) &&
            checkSelfPermission(REQUESTED_PERMISSIONS[2], PERMISSION_REQ_ID)) {
        //执行到此处说明已有权限成功
        Toast.makeText(getActivity(), "已有权限成功", Toast.LENGTH_SHORT).show();
    }
});

/**
 * 检查权限的方法
 *
 * @param permission  权限
 * @param requestCode 请求码
 * @return 是否拥有权限
 */
public boolean checkSelfPermission(String permission, int requestCode) {
    if (ContextCompat.checkSelfPermission(getActivity(), permission)
            != PackageManager.PERMISSION_GRANTED) {
        //发送权限请求
        PagerFragment.this.requestPermissions(REQUESTED_PERMISSIONS, requestCode); <-- 注意申请时用XXXFragment.this.
        return false;
    }
    return true;
}


@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    switch (requestCode) {
        case PERMISSION_REQ_ID: {//请求码
            if (grantResults[0] != PackageManager.PERMISSION_GRANTED ||
                    grantResults[1] != PackageManager.PERMISSION_GRANTED ||
                    grantResults[2] != PackageManager.PERMISSION_GRANTED) {
                //三个权限有任意的未被允许,弹吐司,退出
                Toast.makeText(getActivity(), "用户没有允许权限", Toast.LENGTH_SHORT).show();
                getActivity().finish();
                break;
            }
            Log.e(TAG, "onRequestPerm: OK");
            break;
        }
    }
}
复制代码

3.Fragment的优势

[1].将整个界面的责任碎片化,分散到各个部分,缓解Activity的负担
[2].方便修改/更新:那个地方出现问题/需要更新界面样式,可以直接去找对应的Fragment,而不是像以前在Activity里大海捞针
[3].方便复用: Fragment迁移很方便,哪里需要哪里搬。遇到差不多的需求,改改就能用了。
[4].运行中可以动态地移除、加入、交换,使用灵活
[5].可以`startActivityForResult + onActivityResult`,有目的的开启一个Activity
[6].可以动态申请权限 requestPermissions + onRequestPermissionsResult
[7].ViewPager和Fragment结合容易实现切换效果
复制代码

以上所述就是小编给大家介绍的《Android 点将台:撒豆成兵[- Fragment -]》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Learn Python the Hard Way

Learn Python the Hard Way

Zed Shaw / Example Product Manufacturer / 2011

This is a very beginner book for people who want to learn to code. If you can already code then the book will probably drive you insane. It's intended for people who have no coding chops to build up t......一起来看看 《Learn Python the Hard Way》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器