“基本功不扎实,功力永远无法登峰造极”
自从Fragment出现一直到现在,对于Fragment的使用一直都是出于表层,所以我决定去刨一刨。
在Fragment的官网上是这样解释的:
A Fragment is a piece of an application's user interface or behavior that can be placed in an Activity
. Interaction with fragments is done through FragmentManager
, which can be obtained via Activity.getFragmentManager()
and Fragment.getFragmentManager()
.
The Fragment class can be used many ways to achieve a wide variety of results. In its core, it represents a particular operation or interface that is running within a larger Activity
. A Fragment is closely tied to the Activity it is in, and can not be used apart from one. Though Fragment defines its own lifecycle, that lifecycle is dependent on its activity: if the activity is stopped, no fragments inside of it can be started; when the activity is destroyed, all fragments will be destroyed.
All subclasses of Fragment must include a public no-argument constructor. The framework will often re-instantiate a fragment class when needed, in particular during state restore, and needs to be able to find this constructor to instantiate it. If the no-argument constructor is not available, a runtime exception will occur in some cases during state restore.
他的意思是:
这个Fragment是应用程序的用户界面或可以放置在活动中的行为的一部分。碎片的交互是通过FragmentManager进行的,这可以通过Activity.getFragmentManager获得()和Fragment.getFragmentManager()。
Fragment可以用多种方式来实现各种各样的结果。它的核心是一个特定的操作或接口,它在一个更大的活动中运行。片断与它所处的活动紧密相关,并且不能与它分开。尽管Fragment定义了它自己的生命周期,但是生命周期依赖于它的活动:如果停止活动,就不能启动它内部的片断;当活动被破坏时,所有的碎片都将被销毁。
Fragment的所有子类必须包含一个公共的无参数构造函数。当需要时,框架通常会重新实例化一个片段类,特别是在状态恢复期间,并且需要能够找到这个构造函数来实例化它。如果没有参数的构造函数不可用,那么在某些情况下,在状态恢复期间会出现一个运行时异常。
简单的来说,就是说Fragment是用户界面(Activity)的一个组成部分,相对应它的宿主Activity的生命周期,它也有自己的生命周期与之对应,通过FragmentManager这个方法来对Fragment进行操作。
Fragment的生命周期:
onAttach :将Fragment挂载到Activity中。
onCreate : 创建Fragment。
onCreateView : 创建并返回与Fragment关联的视图层次结构。
onActivityCreate :告诉Fragment,它的活动已经完成。
onViewStateRestored :通知Fragment,它的视图层次结构的所有保存状态都已恢复。
onStart() : 让用户可以看到这个Fragment(基于其包含的活动开始)。
onResume() : Fragment开始与用户交互(基于其包含的活动被恢复)。
onPause() : Fragment失去焦点,它的活动正在被暂停,或者一个Fragment操作在活动中对它进行了修改。
onStop() : Fragment对用户不再可见,它的活动被停止,或者是在活动中对其进行了修改。
onDestryView() :Fragment清理它的视图相关的资源。
onDestry() : 对Fragment的状态进行最后的清理。
onDetach() : 将Fragment从Activity挂载下来,取消关联性。
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent">
<fragment class="com.example.android.apis.app.FragmentLayout$TitlesFragment"
android:id="@+id/titles"
android:layout_width="match_parent" android:layout_height="match_parent" />
</FrameLayout>
在xml文件中使用的时候,通过<fragment/>标签将一个Fragment引入到布局文件当中:
<fragment android:id="@+id/fragmentlist" android:name="com.lzn.leiyaqiang.lznfragment.fragment.FragmentList" android:layout_width="wrap_content" android:layout_height="wrap_content" />
这样就可以如同一个普通的控件一样去使用这个Fragment了。
Fragment在xml中有以下属性可以进行设置:
android:fragmentallowentertransitionoverlap 是否进入和退出转换时应重叠过渡。 android:fragmentallowreturntransitionoverlap 是否进入和退出转换时应重叠过渡,因为弹出栈。 android:fragmententertransition 过渡,将用于视图进入初始场景。 android:fragmentexittransition 过渡,将用于视图的场景片段被删除时,隐藏,或分离时不弹出堆栈。 android:fragmentreentertransition 过渡,将用于移动视图在现场返回由于弹出栈。 android:fragmentsharedelemententertransition 将用于共享的过渡元素转回期间流行的堆栈。
当然,Fragment在使用的时候还有许多方法,这里就不一一列举了,有兴趣的同学可以去看看Fragment的API:
https://developer.android.google.cn/reference/android/app/Fragment.html?hl=zh-cn
在本篇中先简单说说Fragment的简单用法:
Fragment的普通切换:
当OneFragment输入之后,点击Fragment,通知Activity进行Fragment的切换,切换成TwoFragment;
在这里面有使用到EventBus3.0,这里先不做讲解:
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction transaction = fm.beginTransaction();transaction.add(R.id.fragment_group,OneFragment.newInstance("这是第一次创建"));
transaction.commit();
FragmentManager : 用于与活动内部的Fragment对象进行交互的接口
FragmentTransaction beginTransaction()
管理器相关的fragment上启动一系列的编辑操作。
注意:在一个活动保存状态之前,片断事务只能被创建/提交。后如果你想提交一个事务Activity.onSaveInstanceState()(之前和之后的活动。onStart或activity.onResume(),你会得到一个错误。这是因为框架负责在状态中保存当前的片段,如果在保存状态后进行更改,那么它们就会丢失。
add (int containerViewId, Fragment fragment) 添加一个Fragment到containerViewId中。
hide (Fragment fragment) 隐藏Fragment
remove (Fragment fragment) 删除Fragment
replace (int containerViewId, Fragment fragment) 相当于remove -> add
commit 提交事务
通过调用Fragment的newInstance方法实例Fragment,并且以fragment.setArguments(args)的方法传入值。
流程理了一下,感觉有点思路了吧!
话不多说,赶紧趁热打铁,写个demo试试。
首先来看看布局文件:switch_activity_layout
<?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" > <include android:id="@+id/switch_bar" layout="@layout/titler_bar" android:layout_width="match_parent" android:layout_height="wrap_content" /> <FrameLayout android:id="@+id/fragment_group" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"/> </LinearLayout>这里很简单,通过<include/>标签引入一个头部,当然这不是重点,现在把目光放在我们的<FrameLayout/>中,这就是我们Fragment的载体,动态加载Fragment的时候,我们需要一个ViewGroup来承载我们的Fragment,也就是说,我们接下来要放置Fragment的地方。然后写一个Activity。
public class SwitchActivity extends BaseActivity { private TextView titlebar; private FragmentManager fm; //嫌麻烦,直接做了一个状态判断 private boolean mark; //重写的方法,获取布局文件 @Override protected int getView() { return R.layout.switch_activity_layout; } @Override protected void initView() { //实例化<include/>标签引入的布局中的控件 titlebar = findViewById(R.id.switch_bar).findViewById(R.id.bar_title_tv); titlebar.setText("SwitchActivity"); //EventBus注册 EventBus.getDefault().register(this); //获取Fragment的管理者 fm = getSupportFragmentManager(); FragmentTransaction transaction = fm.beginTransaction(); //添加Fragment,通过newInstance方法获取Fragment实例 transaction.add(R.id.fragment_group,OneFragment.newInstance("这是第一次创建")); //提交事务 transaction.commit(); //置状态 mark = true; } //采用EventBus接受从Fragment传过来的消息 @Subscribe(threadMode = ThreadMode.MAIN) public void onEvent(SwitchFragmentEvent event){ String string = ""; if (event.equals("")){ string = "那家伙啥也没说"; }else { string = event.getString(); } fm = getSupportFragmentManager(); FragmentTransaction transaction = fm.beginTransaction(); if (mark) { //替换Fragment transaction.replace(R.id.fragment_group, TwoFragment.newInstance(string)); }else { transaction.replace(R.id.fragment_group, OneFragment.newInstance(string)); } transaction.commit(); mark = !mark; } //在Activity销毁的时候取消注册EventBus @Override protected void onDestroy() { super.onDestroy(); EventBus.getDefault().unregister(this); } } 切记,在使用Fragment的时候,Activity要继承FragmentActivitypublic abstract class BaseActivity extends FragmentActivity { protected abstract int getView(); @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(getView()); initView(); } protected abstract void initView(); }为什么呢?大兄弟请移步这里:
在Fragment的源码中可以看到,getActivity()方法返回值的是FragmentActivity。哦!我了了。
现在轮到我们的主角了,Fragment:
public class OneFragment extends BaseFragment{ private TextView switch_tv,fragment_title; private EditText switch_edit; private Button button_switch; @Override protected int getview() { return R.layout.switch_fragment_layout; } //提供一个方法可以获取OneFragment的实例,并且通过setArguments方法来将值在Fragment创建之前传入数据 public static OneFragment newInstance(String string) { OneFragment fragment = new OneFragment(); Bundle args = new Bundle(); args.putString("String", string); fragment.setArguments(args); return fragment; } @Override protected void initview(View view) { //控件实例化 switch_tv = view.findViewById(R.id.switch_tv); fragment_title = view.findViewById(R.id.fragment_title); switch_edit = view.findViewById(R.id.switch_edit); button_switch = view.findViewById(R.id.button_switch); //接收传入的数据 Bundle bundle = getArguments(); switch_tv.setText(bundle.getString("String")); fragment_title.setText("OneFragment"); //Fragment内部的控件的事件自己处理 button_switch.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String str = switch_edit.getText().toString(); //通过EventBus传递事件给Activity EventBus.getDefault().post(new SwitchFragmentEvent(str)); } }); } }
Fragment的使用,有效的提高了代码的复用,同时也变成开发中最经常使用的方式,熟练掌握Fragment使用,能够有效提升开发效率。Fragment还有一个分支:ListFragmentListFragment继承于Fragment。因此它具有Fragment的特性,能够作为activity中的一部分,目的也是为了使页面设计更加灵活。相比Fragment,ListFragment的内容是以列表(list)的形式显示的。
ListFragment的默认布局包含一个list viewListFragment的源码中,在它的onCreateView方法中,会发现,他已经加载了一个布局,点进去看看:
ListView!!!我去,还真有啊!
ListFragment的源码中,在它的onCreateViewpublic class FragmentList extends ListFragment{ private ArrayAdapter<String> adapter; List<String> data = new ArrayList<String>(); @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); for (int i = 0; i < 30; i++) { data.add("Item " + i + " of the fragmentlist item's click event"); } adapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, data); setListAdapter(adapter); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return super.onCreateView(inflater, container, savedInstanceState); } //点击事件
@Override public void onListItemClick(ListView l, View v, int position, long id) { super.onListItemClick(l, v, position, id); Toast.makeText(getActivity(),data.get(position),Toast.LENGTH_LONG).show(); } }
因为ListFragment已经有布局了,那我就算了添加吧!有的人又说了,我擦,你小子哪里来的点击事件啊!大兄弟莫慌,你看final private AdapterView.OnItemClickListener mOnClickListener = new AdapterView.OnItemClickListener() { public void onItemClick(AdapterView<?> parent, View v, int position, long id) { onListItemClick((ListView)parent, v, position, id); } };
在ListFragment的源码中,有这样的片段,添加点击事件
public void onListItemClick(ListView l, View v, int position, long id) { }就是这个方法,???为什么什么都没有啊?当然。当List需要点击事件的时候,只要重写这个方法,就可以了@Override public void onListItemClick(ListView l, View v, int position, long id) { super.onListItemClick(l, v, position, id); Toast.makeText(getActivity(),data.get(position),Toast.LENGTH_LONG).show(); }
@Override注解是重写父类方法最后上ListFragment的效果图:至于DialogFragment的使用,网上有很多,这里给大家提供一篇来自鸿洋大神的博文,