1、Fragments
Fragment是Activity中用户界面的一个行为或者是一部分,你可以在一个单独的Activity上把多个Fragment组合成为一个多区域的UI,并且可以在多个Activity中再次使用。可以认为Fragment是Activity的一个模块零件,他有自己的生命周期,接受他自己的输入事件,并且可以在Activity运行时添加或者删除。
两个概念:
Fragment、宿主
fragment的生命周期只接受其宿主activity的生命周期的影响。例如,一旦activity被暂停,它里面所有的fragment也会被暂停,一旦activity被销毁,它里面的所有fragment也会被销毁。
2、创建Fragment
要创建一个fragment,必须创建一个fragment的子类(或是继承自他的子类),fragment类的代码看起来很像activity,他与activity一样都有回调函数,例如onCreate,onStart,onPause和onStop,事实上,如果你正在讲一个现成的Android应用转而使用Fragment来实现,可以将代码从activity的回调函数移植到各自的fragment中。
除了基类fragment,还有几个可能会继承的子类:
DialogFragment、ListFragment、PreferenceFragment
3、添加用户界面
fragment常被用作activity用户界面的一部分,并且将本身的布局够见到activity中去。
将fragment添加到activity有两种方式:
1)在activity的布局文件里声明fragment
activity_main.xml
<?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="horizontal"> <fragment android:id="@+id/fragTitle" android:name="com.wzh.pushdemo.fragment.TitleFragment" android:layout_width="0dip" android:layout_height="match_parent" android:layout_weight="1" /> <fragment android:id="@+id/fragContent" android:name="com.wzh.pushdemo.fragment.ContentFragment" android:layout_width="0dip" android:layout_height="match_parent" android:layout_weight="3" /> </LinearLayout>
2)通过编码将fragment添加到已存在的ViewGroup中
<?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="horizontal"> <fragment android:id="@+id/fragTitle" android:name="com.wzh.pushdemo.fragment.TitleFragment" android:layout_width="0dip" android:layout_height="match_parent" android:layout_weight="1" /> <FrameLayout android:id="@+id/framLayout" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="3"> </FrameLayout> </LinearLayout>
public class MainActivity extends Activity { TitleFragment titleFragment; ContentFragment contentFragment; FrameLayout framLayout; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //通过fragmentManager(Fragment管理器)获取fragment实例 // titleFragment = (TitleFragment) getFragmentManager().findFragmentById(R.id.fragTitle); // contentFragment = (ContentFragment) getFragmentManager().findFragmentById(R.id.fragContent); framLayout = (FrameLayout) findViewById(R.id.framLayout); /** * 通过代码添加Fragment */ FragmentManager fm = getFragmentManager(); //开启一个事务 FragmentTransaction ft = fm.beginTransaction(); contentFragment = new ContentFragment(); //添加Fragment ft.add(R.id.framLayout, contentFragment); // ft.remove(); //删除 // ft.replace();//替换 //提交事务 ft.commit(); } }
- transaction.add() 往Activity中添加一个
- Fragment transaction.remove() 从Activity中移除一个Fragment,如果被移除的Fragment没有添加到回退栈,这个Fragment实例将会被销毁。
- transaction.replace() 使用另一个Fragment替换当前的,实际上就是remove()然后add()的合体
- transaction.hide() 隐藏当前的Fragment,仅仅是设为不可见,并不会销毁,
- transaction.show() 显示之前隐藏的Fragment
- detach()
会将view从UI中移除,和remove()不同,此时fragment的状态依然由FragmentManager维护。 - attach()
重建view视图,附加到UI上并显示。 - transatcion.commit()
提交一个事务
4、管理Fragments
想要管理activity中的fragment,可以使用FragmentManager,可以通过在activity中调用getFragmentManager()获得。
使用FragmentManger可以做如下事情,包括:
(1)使用findFragmentById()(用于在activity不居中提供有界面的fragment)或者findFragmentByTag()获取activity中存在的fragment(用于有界面或者没有界面的fragment)
(2)使用popBackStack()(模仿用户的back命令)从后台栈弹出fragment
(3)使用addOnBackStackChangedListener()注册一个监听后台栈变化的监听器
public class PopBackActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_pop_back); } public void oneClick(View view) { PopBackFragment p1 = new PopBackFragment("one"); FragmentTransaction ft = getFragmentManager().beginTransaction(); ft.replace(R.id.content, p1); //把当前fragment添加到Activity栈 ft.addToBackStack(null); ft.commit(); } public void twoClick(View view) { //用构造方法传值,有问题 PopBackFragment p1 = new PopBackFragment("two"); FragmentTransaction ft = getFragmentManager().beginTransaction(); ft.replace(R.id.content, p1); //把当前fragment添加到Activity栈 ft.addToBackStack(null); ft.commit(); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { if (getFragmentManager().getBackStackEntryCount() == 0) { finish(); } else { getFragmentManager().popBackStack(); } } return true; } }
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="oneClick" android:text="one" /> <Button android:id="@+id/button2" android:layout_width="88dp" android:layout_height="48dp" android:onClick="twoClick" android:text="two" /> <FrameLayout android:id="@+id/content" android:layout_width="match_parent" android:layout_height="match_parent" tools:layout_editor_absoluteX="8dp" tools:layout_editor_absoluteY="72dp"> </FrameLayout> </LinearLayout>
public class PopBackFragment extends Fragment { private String title; public PopBackFragment() { super(); } public PopBackFragment(String title) { this.title = title; } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.frag_pop_back, null); TextView textView = (TextView) view.findViewById(R.id.text); textView.setText(title); return view; } }
5.Fragment的传参方式:
public static Fragment newInstance(String arg) { TitleFragment titleFragment1 = new TitleFragment(); Bundle bundle = new Bundle(); bundle.putString("info", arg); titleFragment1.setArguments(bundle); return titleFragment1; }
例如:
public void twoClick(View view) { //用构造方法传值,有问题,在横竖屏切换时数据会丢失,不建议使用 // PopBackFragment p1 = new PopBackFragment("two"); //建议使用 PopBackFragment p1 = PopBackFragment.getInstance("two"); FragmentTransaction ft = getFragmentManager().beginTransaction(); ft.replace(R.id.content, p1); //把当前fragment添加到Activity栈 ft.addToBackStack(null); ft.commit(); }
PopBackFragment
/** * Fragmen的传参方法 * @param title * @return */ public static PopBackFragment getInstance(String title) { PopBackFragment p = new PopBackFragment(); Bundle bundle = new Bundle(); bundle.putString("title", title); p.setArguments(bundle); return p; } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.frag_pop_back, null); TextView textView = (TextView) view.findViewById(R.id.text); textView.setText(getArguments().getString("title")); return view; }
Fragment与Activity交互
fragment可以通过getActivity()函数访问Activity,并且很容易的执行类似于查找activity布局中的视图任务:
View listView = getActivity().findViewById(R.id.list);
activity能够调用fragment的函数findFragmentById()或者findFragmentByTag(),从FragmentManager中获取Fragment
TitleFragment fragment = (TitleFragment) getFragmentManager().findFragmentById(R.id.fragTitle);
fragment与activity共享事件
一个好方法是fragment内部定义一个回调接口,并需要宿主activity实现它。
当activity通过接口接收到回调时,可以在必要时与布局中的其他fragment共享信息。
宿主Activity:
public class MainActivity extends Activity implements TitleFragment.TitleListener { private TitleFragment titleFragment; private ContentFragment contentFragment; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); titleFragment = (TitleFragment) getFragmentManager().findFragmentById(R.id.fragTitle); contentFragment = (ContentFragment) getFragmentManager().findFragmentById(R.id.fragContent); } @Override public void changeValue(String value) { contentFragment.change(value); } }
activity_main.xml
<?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="horizontal"> <fragment android:id="@+id/fragTitle" android:name="com.wzh.pushdemo.fragment.TitleFragment" android:layout_width="0dip" android:layout_height="match_parent" android:layout_weight="1" /> <fragment android:id="@+id/fragContent" android:name="com.wzh.pushdemo.fragment.ContentFragment" android:layout_width="0dip" android:layout_height="match_parent" android:layout_weight="3" /> </LinearLayout>
触发事件的frag
public class TitleFragment extends Fragment implements View.OnClickListener { private TitleListener titleListener; private Button btnTime; private Button btnSpace; @Override public void onAttach(Activity activity) { super.onAttach(activity); titleListener = (TitleListener) activity; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.frag_title, container, false); btnTime = (Button) view.findViewById(R.id.btnTime); btnSpace = (Button) view.findViewById(R.id.btnSpace); btnTime.setOnClickListener(this); btnSpace.setOnClickListener(this); return view; } @Override public void onClick(View view) { switch (view.getId()) { case R.id.btnTime: titleListener.changeValue("时间"); break; case R.id.btnSpace: titleListener.changeValue("地点"); break; } } //定义一个回调接口,宿主要实现这个接口 public static interface TitleListener { public void changeValue(String value); } }
frag_title.xml
<?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:background="#00ff00" android:orientation="vertical" android:padding="10dp"> <Button android:id="@+id/btnTime" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="时间" /> <Button android:id="@+id/btnSpace" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="地点" /> </LinearLayout>
要改变值的frag
public class ContentFragment extends Fragment { private TextView tvValue; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.frag_content, container, false); tvValue = (TextView) view.findViewById(R.id.tvValue); return view; } public void change(String value) { tvValue.setText(value); } }
frag_content.xml
<?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:background="#00ddff" android:orientation="vertical"> <TextView android:id="@+id/tvValue" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:textSize="30dp" android:text="Hello" /> </LinearLayout>
6、PreferenceFragment
有时候,我们的程序需要提供一些选项功能,能让用户去定制化自己的是用风格。例如,允许用户是否自动保存登录信息,允许用户设定某个页面的刷新时间等等,我们可以使用PreferenceActivity基类去显示给用户一个选项设置的界面,在Android3.0或更高版本上,可以使用PreferenceFragment类去实现这个功能。
下面将展示如何常见和使用PreferenceFragment:
(1)在res文件夹下新疆一个xml文件夹,在xml文件夹下面新建一个文件:preference.xml
(2)在包路径下面新建一个类:Fragment继承PreferenceFragment
(3)从xml文件加载选项addPreferencesFromResource(R.xml.preferences);
7、Fragment懒加载
如果ViewPager的每个Fragment都会拉取网络数据加载,而Fragment默认是加载前两个界面的,这样有可能会出现网络丢包或者网络堵塞情况,所以事先懒加载就有必要了。
实现懒加载的重点是public void setUserVisibleHint(boolean isVisibleToUser)方法,这个方法会优先于onCreate()方法的,即在调用onCreate前,isVisibleToUser已经获取到了,isVisibleToUser为true时表示页面用户能看到,false时看不到,之后可以在用户能看到页面时进行数据加载,不提前进行加载就可以了
@Override public void onStart() { super.onStart(); Log.d("TAG", mTagName + " onStart()"); ... if(getUserVisibleHint()) { pullData(); } }