Fragment使用解析

Fragment(碎片/片段),在Android 3.0被引入,主要为了给大屏幕上更加动态、灵活的UI设计提供支持。


Fragment基本使用

创建Fragment

使用Fragment,需要创建一个类继承Fragment

如果使用support v4包下提供的Fragment,需要注意:

  • Activity需要继承FragmentActivity
  • 需通过getSupportFragmentManager()来获取FragmentManager
class TestFragment : Fragment() {
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_test, container, false)
    }
}

如果Fragment是有界面的,那么必须实现onCreateView()生命周期方法,并在其中利用LayoutInflater返回所需要显示的View

inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)参数说明:

  • resource为你所需要展示布局的ID
  • root为所需要扩展布局的父布局ViewGroup
  • attachToRoot表示是否将所需扩展布局添加到root中。因为系统最后会将onCreateView()返回的View添加到root中,所以在此处设置为false,避免重复添加导致java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.异常错误。

添加Fragment

将创建好的Fragment添加到Activity中,有两种方式:静态添加和动态添加。

静态添加

Activity布局文件中直接使用添加:




    
        

标签的class属性设置具体的Fragment,就完成了将Fragment静态添加到Activity中。

动态添加

Activity运行期间,使用FragmentTransaction相应方法将Fragment动态添加到Activity中:

supportFragmentManager.beginTransaction()
                .add(R.id.frameLayout, TestFragment())
                .commit()

add()方法第一个参数是ViewGroupid,告知FragmentManager片段应该被放置的位置,同时id也是此Fragment的唯一标识,可以通过FragmentManagerfindFragmentById()查找。

通信方式

setArguments()

在实例化Fragment时,可以通过setArguments()方法,利用Bundle来传递参数。所以通常我们会在Fragment中创建一个newInstance方法,告诉使用者需要传递哪些参数:

class TestFragment : Fragment() {

    companion object {
        fun newInstance(name: String, bgColor: Int): TestFragment {
            val fragment = TestFragment()
            val bundle = Bundle()
            bundle.putString("name", name)
            bundle.putInt("bgColor", bgColor)
            fragment.arguments = bundle
            return fragment
        }
    }
    
}

newInstance方法中处理数据传递,实例化Fragment并将其返回。

getActivity()

Fragment中我们可以通过getActivity()方法获取Activity实例,从而使用Activity相应方法。

值得注意的是:

  • getActivity()有可能返回的是null,在使用过程中最好进行相应的判断(例如:fragment.isAdd()getActivity() != null等)。
  • 最好不要在Fragment中长期持有宿主Activity的引用,处理不当,很有可能导致宿主Activity不能正常回收,导致内存泄露。

使用接口

Fragment的一些操作想要告知宿主Activity,我们可以创建一个回调接口,让Activity实现:

class TestFragment : Fragment() {

    private var name: String = "TestFragment"
    private var bgColor: Int = Color.WHITE
    private var onSubmitClickListener: OnSubmitClickListener? = null

    companion object {
        fun newInstance(name: String, bgColor: Int): TestFragment {
            val fragment = TestFragment()
            val bundle = Bundle()
            bundle.putString("name", name)
            bundle.putInt("bgColor", bgColor)
            fragment.arguments = bundle
            return fragment
        }
    }
    
    interface OnSubmitClickListener {
        fun onSubmitClick(result: String)
    }

    override fun onAttach(context: Context) {
        super.onAttach(context)
        try {
            onSubmitClickListener = context as OnSubmitClickListener
        } catch (e: ClassCastException) {
            throw ClassCastException("${(context as Activity)} must implement OnSubmitClickListener")
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        arguments?.let {
            name = it.getString("name", "Fragment1")
            bgColor = it.getInt("bgColor", Color.WHITE)
        }
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_test, container, false)
    }

    override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        parent?.setBackgroundColor(bgColor)
        tv.text = name
        
        btnSubmit.setOnClickListener {
            onSubmitClickListener?.onSubmitClick("The submit button is clicked.")
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        onSubmitClickListener = null
    }
}
class MainActivity : AppCompatActivity(), TestFragment.OnSubmitClickListener {
    
    override fun onSubmitClick(result: String) {
        Toast.makeText(this, result, Toast.LENGTH_SHORT).show()
    }
   ...
}

startActivityForResult

Fragment提供了startActivityForResult用于Activity跳转与数据回传,查看源码可以发现最终调用的是FragmentActivity中的startActivityForResult()

FragmentActivityonActivityResult()方法中,根据requestCode查找到相应的Fragment,并调用它的onActivityResult()。所以可以在FragmentonActivityResult()中获取回传数据。

因为Fragment并没有提供setResult(),如果是在目标ActivityFragment中处理回传数据,可以使用getActivity().setResult()

Fragment懒加载

FragmentViewPager结合使用时,我们通常希望当Fragment显示的时候再加载获取相应数据。而ViewPager默认会预加载下一页,ViewPagersetOffscreenPageLimit(int limit)方法也无法设置小于1的数字,其内部处理了limit小于1直接取默认值,也就是1。

ViewPager实际上是通过FragmentsetUserVisibleHint方法来实现其页面的展示和隐藏,我们可以利用setUserVisibleHintFragment相应生命周期及方法,来达到我们想要的效果:

abstract class LazyLoadFragment : Fragment() {
    protected var isViewInitiated: Boolean = false
    protected var isDataLoaded: Boolean = false
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        isViewInitiated = true
        prepareRequestData()
    }

    override fun setUserVisibleHint(isVisibleToUser: Boolean) {
        super.setUserVisibleHint(isVisibleToUser)
        prepareRequestData()
    }

    abstract fun requestData()
    
    fun prepareRequestData(forceUpdate: Boolean = false): Boolean {
        if (userVisibleHint && isViewInitiated && (!isDataLoaded || forceUpdate)) {
            requestData()
            isDataLoaded = true
            return true
        }
        return false
    }
}

想要实现懒加载,只需继承LazyLoadFragment,实现requestData()方法即可。


Fragment生命周期

Fragment依附于ActivityFragment的生命周期与Activity的生命周期息息相关。

下面贴上一张来自GitHub上的 xxv/android-lifecycle
项目里的图:

Fragment使用解析_第1张图片
image

注意:

  • 使用FragmentTransactionhide()show()方法隐藏和显示Fragment,并不会触发Fragment的生命周期方法。可在onHiddenChanged()方法中进行监听其变化。
  • 使用FragmentTransactionreplace()方法,会导致同一容器中之前添加的所有Fragment被销毁,依次执行onPause->onStop->onDestroyView->onDestroy->onDetach

参考链接

  • Android Fragment 的使用,一些你不可不知的注意事项
  • Android Fragment+ViewPager 组合,一些你不可不知的注意事项
  • Fragment全解析系列(一):那些年踩过的坑
  • Android Fragment 你应该知道的一切
  • 官方文档

你可能感兴趣的:(Fragment使用解析)