Fragment
Fragment是Android 3.0 (Honeycomb)被引入的。主要目的是为了给大屏幕(如平板电脑)上更加动态和灵活的UI设计提供支持。由于平板电脑的屏幕比手机的屏幕大很多,因此可用于组合和交换的UI组件的空间更大,利用Fragment实现此类设计的时,就无需管理对视图层次结构的复杂更改。
Fragment的优势是布局在不同设备上的适配。
使用Fragment还有这么几个方面优势:代码复用,可控性。
目录:
- Fragment是什么
- Fragment静态加载怎么用
- Fragment动态加载怎么用
- ViewPager+Fragment实现页卡滑动
- Fragment生命周期
- Fragment通信
1.Fragment是什么
Fragment也可以叫为“片段”,它可以表示Activity中的行为或用户界面部分。我们可以在一个Activity中用多个Fragment组合来构建多窗格的UI,以及在多个Activity中重复使用某个Fragment。它有自己的生命周期,能接受自己的输入,并且可以在 Activity 运行时添加或删除Fragment(有点像在不同 Activity 中重复使用的“子 Activity”)。
简单来说,Fragment其实可以理解为一个具有自己生命周期的控件,只不过这个控件又有点特殊,它有自己的处理输入事件的能力,有自己的生命周期,又必须依赖于Activity,能互相通信和托管。
2.Fragment静态加载怎么用
1、继承Fragment,重写onCreateView决定Fragemnt的布局
2、在Activity中声明此Fragment,就当和普通的View一样
- 首先是布局文件:fragment1.xml
这个布局文件非常简单,只有一个LinearLayout,里面加入了一个TextView
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#00ff00" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="This is fragment 1"
android:textColor="#000000"
android:textSize="25sp" />
</LinearLayout>
- 我们再新建一个fragment2.xml :
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffff00" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="This is fragment 2"
android:textColor="#000000"
android:textSize="25sp" />
</LinearLayout>
- 然后新建一个类Fragment1,这个类是继承自Fragment的:
在onCreateView()方法中加载了fragment1.xml的布局。
public class Fragment1 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment1, container, false);
}
}
- fragment2.xml也是一样的做法,新建一个Fragment2类:
public class Fragment2 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment2, container, false);
}
}
- 然后打开或新建activity_main.xml作为主Activity的布局文件,在里面加入两个Fragment的引用,使用android:name前缀来引用具体的Fragment:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:baselineAligned="false" >
<fragment
android:id="@+id/fragment1"
android:name="com.example.fragmentdemo.Fragment1"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1" />
<fragment
android:id="@+id/fragment2"
android:name="com.example.fragmentdemo.Fragment2"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>
- 最后MainActivity作为程序的主Activity,里面的代码非常简单,都是自动生成的:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
- 现在我们来运行一次程序,就会看到,一个Activity很融洽地包含了两个Fragment,这两个Fragment平分了整个屏幕,效果图如下:
3.Fragment动态加载怎么用
上面仅仅是Fragment简单用法,它真正强大部分是在动态地添加到Activity中,那么动态用法又是如何呢?
还是在静态用法代码的基础上修改,打开activity_main.xml,将其中对Fragment的引用都删除,只保留最外层的LinearLayout,并给它添加一个id,因为我们要动态添加Fragment,不用在XML里添加了,删除后代码如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:baselineAligned="false" >
</LinearLayout>
- 然后打开MainActivity,修改其中的代码如下所示:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Display display = getWindowManager().getDefaultDisplay();
if (display.getWidth() > display.getHeight()) {
Fragment1 fragment1 = new Fragment1();
getFragmentManager().beginTransaction().replace(R.id.main_layout, fragment1).commit();
} else {
Fragment2 fragment2 = new Fragment2();
getFragmentManager().beginTransaction().replace(R.id.main_layout, fragment2).commit();
}
}
}
首先,我们要获取屏幕的宽度和高度,然后进行判断,如果屏幕宽度大于高度就添加fragment1,如果高度大于宽度就添加fragment2。动态添加Fragment主要分为4步:
1.获取到FragmentManager,在Activity中可以直接通过getFragmentManager得到。
2.开启一个事务,通过调用beginTransaction方法开启。
3.向容器内加入Fragment,一般使用replace方法实现,需要传入容器的id和Fragment的实例。
4.提交事务,调用commit方法提交。
- 现在运行一下程序,效果如下图所示:
4.ViewPager+Fragment实现页卡滑动
- 我们新建一个ContoctFragment类 :
package com.example.administrator.paopao;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
/**
* A simple {@link Fragment} subclass.
*/
public class ContactFragment extends Fragment {
public ContactFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_contact, container, false);
}
}
- fragment_contoct.xml布局
<FrameLayout 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:background="#fcc"
tools:context="com.example.administrator.paopao.ContactFragment">
<!-- TODO: Update blank fragment layout -->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</FrameLayout>
- 我们再新建一个FriendsFragment类:
package com.example.administrator.paopao;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
/**
* A simple {@link Fragment} subclass.
*/
public class FriendsFragment extends Fragment {
public FriendsFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_friends, container, false);
}
}
- fragment_friends.xml布局:
<FrameLayout 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:background="#f59"
tools:context="com.example.administrator.paopao.FriendsFragment">
<!-- TODO: Update blank fragment layout -->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</FrameLayout>
- 我们再新建一个ChatFragment类:
package com.example.administrator.paopao;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
/**
* A simple {@link Fragment} subclass.
*/
public class ChatFragment extends Fragment {
public ChatFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_chat, container, false);
}
}
- fragment_chat.xml布局:
<FrameLayout 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:background="#fc9"
tools:context="com.example.administrator.paopao.ChatFragment">
<!-- TODO: Update blank fragment layout -->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</FrameLayout>
- 新建一个适配器MyPagerAdapter
package com.example.administrator.paopao;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import java.util.List;
/**
* Created by Administrator on 2018/6/5.
*/
public class MyPagerAdapter extends FragmentPagerAdapter{
private List<Fragment>fragmentList;
public MyPagerAdapter(FragmentManager fm,List<Fragment> fragmentList) {
super(fm);
this.fragmentList=fragmentList;
}
@Override
public Fragment getItem(int position) {
return fragmentList.get(position);
}
@Override
public int getCount() {
return fragmentList.size();
}
}
- 打开或新建activity_main.xml作为主Activity的布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.administrator.paopao.VPActivity"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="60dp">
<TextView
android:id="@+id/tv_chat"
android:layout_width="0dp"
android:gravity="center"
android:layout_height="match_parent"
android:layout_weight="1"
android:textSize="20dp"
android:text="页面一"/>
<TextView
android:id="@+id/tv_contact"
android:layout_width="0dp"
android:gravity="center"
android:layout_height="match_parent"
android:layout_weight="1"
android:textSize="20dp"
android:text="页面二"/>
<TextView
android:id="@+id/tv_friends"
android:gravity="center"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:textSize="20dp"
android:text="页面三"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="3dp"
>
<View
android:id="@+id/view_chat"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#666666"/>
<View
android:id="@+id/view_contact"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#666666"/>
<View
android:id="@+id/view_friends"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#666666"/>
</LinearLayout>
<android.support.v4.view.ViewPager
android:id="@+id/main_vp"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v4.view.ViewPager>
</LinearLayout>
- 新建MainActivity作为程序的主Activity
package com.example.administrator.paopao;
import android.graphics.Color;
import android.support.v4.app.Fragment;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
public class VPActivity extends AppCompatActivity implements View.OnClickListener{
private ViewPager viewPager;
private ContactFragment contactFragment;
private FriendsFragment friendsFragment;
private ChatFragment chatFragment;
private View chatView;
private View contactView;
private View friendsView;
private TextView chatTV;
private TextView contactTV;
private TextView friendsTV;
private List<Fragment> fragmentList=new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_vp);
chatView=findViewById(R.id.view_chat);
contactView=findViewById(R.id.view_contact);
friendsView=findViewById(R.id.view_friends);
viewPager= (ViewPager) findViewById(R.id.main_vp);
contactTV= (TextView) findViewById(R.id.tv_contact);
chatTV= (TextView) findViewById(R.id.tv_chat);
friendsTV= (TextView) findViewById(R.id.tv_friends);
contactTV.setOnClickListener(this);
chatTV.setOnClickListener(this);
friendsTV.setOnClickListener(this);
chatView.setBackgroundColor(Color.BLUE);
contactFragment=new ContactFragment();
friendsFragment=new FriendsFragment();
chatFragment=new ChatFragment();
fragmentList.add(contactFragment);
fragmentList.add(friendsFragment);
fragmentList.add(chatFragment);
MyPagerAdapter adapter=new MyPagerAdapter(getSupportFragmentManager(),fragmentList);
viewPager.setAdapter(adapter);
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
chatView.setBackgroundColor(Color.GRAY);
contactView.setBackgroundColor(Color.GRAY);
friendsView.setBackgroundColor(Color.GRAY);
switch (position){
case 0:
chatView.setBackgroundColor(Color.BLUE);
break;
case 1:
contactView.setBackgroundColor(Color.BLUE);
break;
case 2:
friendsView.setBackgroundColor(Color.BLUE);
break;
default:
break;
}
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.tv_chat:
viewPager.setCurrentItem(0);
break;
case R.id.tv_contact:
viewPager.setCurrentItem(1);
break;
case R.id.tv_friends:
viewPager.setCurrentItem(2);
break;
default:
break;
}
}
}
- 效果图如下:
5.Fragment生命周期
Fragment生命周期和Activity生命周期对比,两者还是有很多相似的地方,比如都有onCreate(),onStart(),onPause(),onDestroy() 等等,因为Fragment是被托管到Activity中的,所以多了两个onAttach()和onDetach()。
与Activity生命周期不一样的方法:
onAttach()
Fragment和Activity建立关联的时候调用,被附加到Activity中去。
onCreate()
系统会在创建Fragment时调用此方法。可以初始化一段资源文件等等。
onCreateView()
系统会在Fragment首次绘制其用户界面时调用此方法。 要想为Fragment绘制 UI,从该方法中返回的 View 必须是Fragment布局的根视图。如果Fragment未提供 UI,您可以返回 null。
onViewCreated()
在Fragment被绘制后,调用此方法,可以初始化控件资源。
onActivityCreated()
当onCreate(),onCreateView(),onViewCreated()方法执行完后调用,也就是Activity被渲染绘制出来后。
onPause()
系统将此方法作为用户离开Fragment的第一个信号(但并不总是意味着此Fragment会被销毁)进行调用。 通常可以在此方法内确认在当前用户会话结束后仍然有效的任何更改(因为用户可能不会返回)。
onDestroyView()
Fragment中的布局被移除时调用。
onDetach()
Fragment和Activity解除关联的时候调用。
但需要注一点是:除了onCreateView,其他的所有方法如果你重写了,必须调用父类对于该方法的实现。
还有一般在启动Fragment的时候,它的生命周期就会执行这几个方法。
5.Fragment通信
Fragment 可以通过 getActivity() 访问 Activity实例,并轻松地执行在 Activity 布局中查找视图等任务。如:
View listView = getActivity().findViewById(R.id.list);
同样地,Activity 也可以使用 findFragmentById() 或 findFragmentByTag(),通过从 FragmentManager 获取对 Fragment 的引用来调用Fragment中的方法。例如:
ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);
创建对 Activity 的事件回调
在某些情况下,可能需要通过与 Activity 共享事件。执行此操作的一个好方法是,在Fragment 内定义一个回调接口,并要求宿主 Activity 实现它。 当 Activity 通过该接口收到回调时,可以根据需要与布局中的其他Fragment共享这些信息。
例如,如果一个新闻应用的 Activity 有两个Fragment ,一个用于显示文章列表(Fragment A),另一个用于显示文章(Fragment B)—,那么Fragment A必须在列表项被选定后告知 Activity,以便它告知Fragment B 显示该文章。 在本例中,OnArticleSelectedListener 接口在片段 A 内声明:
public static class FragmentA extends ListFragment {
public interface OnArticleSelectedListener {
public void onArticleSelected(Uri articleUri);
}
}
然后,该Fragment的宿主 Activity 会实现 OnArticleSelectedListener 接口并替代 onArticleSelected(),将来自Fragment A 的事件通知Fragment B。为确保宿主 Activity 实现此界面,Fragment A 的 onAttach() 回调方法(系统在向 Activity 添加Fragment时调用的方法)会通过转换传递到 onAttach() 中的 Activity 来实例化 OnArticleSelectedListener 的实例:
public static class FragmentA extends ListFragment {
OnArticleSelectedListener mListener;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (OnArticleSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener");
}
}
}
如果 Activity 未实现界面,则片段会引发 ClassCastException。实现时,mListener 成员会保留对 Activity 的 OnArticleSelectedListener 实现的引用,以便Fragment A 可以通过调用 OnArticleSelectedListener 界面定义的方法与 Activity 共享事件。例如,如果Fragment A 是 ListFragment 的一个扩展,则用户每次点击列表项时,系统都会调用Fragment中的 onListItemClick(),然后该方法会调用 onArticleSelected() 以与 Activity 共享事件:
public static class FragmentA extends ListFragment {
OnArticleSelectedListener mListener;
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
Uri noteUri = ContentUris.withAppendedId(ArticleColumns.CONTENT_URI, id);
mListener.onArticleSelected(noteUri);
}
}