Fragment以及懒加载
阅读原文时间:2023年07月10日阅读:1

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


<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" />

2)通过编码将fragment添加到已存在的ViewGroup中


<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>

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();  
}  

}

Fragment家族常用的API Fragment常用的三个类:

android.app.Fragment    主要用于定义Fragment

android.app.FragmentManager 主要用于在Activity中操作

Fragment android.app.FragmentTransaction 保证一些列Fragment操作的原子性,熟悉事务这个词,一定能明白~

a、获取FragmentManage的方式: getFragmentManager() // v4中,getSupportFragmentManager

b、主要的操作都是FragmentTransaction的方法 FragmentTransaction transaction = fm.benginTransatcion();//开启一个事务

  • 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()
    提交一个事务

commit方法一定要在Activity.onSaveInstance()之前调用。

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;  
}  

}


<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>  

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


<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" />  

触发事件的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


<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="地点" />

要改变值的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


<TextView  
    android:id="@+id/tvValue"  
    android:layout\_width="match\_parent"  
    android:layout\_height="match\_parent"  
    android:gravity="center"  
    android:textSize="30dp"  
    android:text="Hello" />

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();  
}

}