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

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

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

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

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 -]》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

创造突破性产品

创造突破性产品

Jonathan Cagan、Craig M.Vogel / 机械工业出版社 / 2004-1 / 35.00元

在《创造突破性产品:从产品策略到项目定案的创新》中作者总结多年的研究成果,指明了与产品创新相关的一系列因素,并提供了一套全新的开发突破性产品的理论与方法,该书旨在帮助企业,技术和设计人员: 获得对用户的需求和市场新的趋势的准确洞察力; 认识可以创造新市场的产品机会缺口; 指导产品模糊前期的构造; 正确地运用定性和定量的研究方法; ......一起来看看 《创造突破性产品》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具