Fragment
要讨论fragment首先我们得知道什么是fragment,fragment有什么用,我们为什么要使用fragment,在安卓中,fragment有着相当重要的作用,那么,我们今天就来浅谈一下fragment
Fragment是什么
相信大家对QQ肯定不陌生,那么我们都知道,QQ上有以下几个按钮
对应的分别是联系人,空间,群组和消息,而当我们点击其中一个时,就会切换到对应的页面(当然,空间标记除外),其实这其中就使用了fragment,fragment是一个碎片,只能依附在activity页面才能显示,就像我们刚刚所说,切换的每一个页面就是一个fragment,fragment的诞生是最初是为了适应平板,fragment的诞生解决了屏幕适配问题,因为fragment是一个局部的碎片,在局部刷新方面也有着独特的优势,说了这么多,那么,到底什么是fragment呢?
Fragment也可以叫为“片段”,它可以表示Activity中的行为或用户界面部分。我们可以在一个Activity中用多个Fragment组合来构建多窗格的UI,以及在多个Activity中重复使用某个Fragment。它有自己的生命周期,能接受自己的输入,并且可以在 Activity 运行时添加或删除Fragment(有点像在不同 Activity 中重复使用的“子 Activity”)。
简单来说,Fragment其实可以理解为一个具有自己生命周期的控件,只不过这个控件又有点特殊,它有自己的处理输入事件的能力,有自己的生命周期,又必须依赖于Activity,能互相通信和托管。
Fragment的静态加载
知道了什么是fragment,那么我们当然得知道fragment的两种加载方式
首先,我们来看一看fragment的静态加载,fragment的静态加载,说白了就是直接将fragment放到activity中使用,相比较起来较为容易使用,那么,我们来创建一个fragment碎片(以Android Studio为例):
我创了一个fragment的包,为了看的更清楚,按照我选择的步骤就可以创出一个简单的fragment
这里要注意
这两个选项本来是默认选上的,我们得把它去掉,不然后面的代码很可能会报错,创好的fragment其实也相当于一个activity,有布局页面和java代码页面,在这两个页面中可以进行我们自己的一些操作,这里我们主要来说说fragment的静态加载的方法,其实很简单,我们只需要在一个布局中直接加入fragment就行:
<fragment
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.example.asus.mytest2.fragment.ExampleFragment"
android:id="@+id/fragment_example"/>
这里我们说下fragment标签的一个重要属性
name:这个属性用来选择你所需要放置在activity中的fragment碎片
这里我们说一下,id必须得写
fragment的静态加载基本上就是这样,这样我们在碎片上写的内容就可以在activity被运行时显示出来
Fragment的动态加载
有静态加载就肯定会有动态加载,比起静态加载来,fragment的动态加载会有更多需要注意的地方
同样,我们先创建一个fragment的碎片,这是创好的fragment中activity的代码:
package com.example.asus.mytest2.fragment;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.example.asus.mytest2.R;
/**
* A simple {@link Fragment} subclass.
*/
public class ExampleFragment extends Fragment {
public ExampleFragment() {
// 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_example, container, false);
}
}
当然,我们的重点是看上面的包,是不是发现了一个v4包
import android.support.v4.app.Fragment;
这是我们创建fragment默认加载的包,而fragment的动态加载需要的是app包,这里我们得去掉这个包重新导一个包
import android.app.Fragment;
这是新导入的包,对于动态加载,我们则需要新引入一个标签FrameLayout
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/fragment"
android:layout_above="@id/ll_fragment"
></FrameLayout>
使用这个标签来进行fragment的动态加载,在这里我们先说说关于动态加载几个包
android.app.Fragment 主要用于定义Fragment
android.app.FragmentManager 主要用于在Activity中操作Fragment
android.app.FragmentTransaction 保证一些列Fragment操作的原子性
这里我们说下动态加载的两个属性
获取FragmentManage的方式有:android.app.Fragment:getFragmentManager() ;
FragmentTransaction transaction = fm.benginTransatcion()用来开启一个事务
这里为了方便大家理解,我们来写个例子,我们先创建三个fragment碎片HelloFragment,ListFragment,WxFragment,在activity中加入FrameLayout标签:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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.asus.mytest2.FragmentActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_alignParentBottom="true"
android:id="@+id/ll_fragment">
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="联系人"
android:id="@+id/btn1_fragment"/>
<Button
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="动态"
android:id="@+id/btn2_fragment"/>
<Button
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="微信"
android:id="@+id/btn3_fragment"/>
</LinearLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/fragment"
android:layout_above="@id/ll_fragment"
></FrameLayout>
</RelativeLayout>
这里,我们就要用到刚刚所说的FragmentManager和FragmentTransaction,附上activity代码:
public class FragmentActivity extends AppCompatActivity implements View.OnClickListener {
private Button btn1, btn2, btn3;
private FragmentManager fragmentManager;
private FragmentTransaction fragmentTransaction;
private HelloFragment helloFragment;
private ListFragment listFragment;
private WxFragment wxFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment);
btn1 = (Button) findViewById(R.id.btn1_fragment);
btn2 = (Button) findViewById(R.id.btn2_fragment);
btn3 = (Button) findViewById(R.id.btn3_fragment);
fragmentManager = getFragmentManager();
btn1.setOnClickListener(this);
btn2.setOnClickListener(this);
btn3.setOnClickListener(this);
}
@Override
public void onClick(View v) {
fragmentTransaction = fragmentManager.beginTransaction();
switch (v.getId()) {
case R.id.btn1_fragment:
if (helloFragment == null) {
helloFragment = new HelloFragment();
}
fragmentTransaction.replace(R.id.fragment, helloFragment);
break;
case R.id.btn2_fragment:
if (listFragment == null) {
listFragment = new ListFragment();
}
fragmentTransaction.replace(R.id.fragment, listFragment);
break;
case R.id.btn3_fragment:
if (wxFragment == null) {
wxFragment = new WxFragment();
}
fragmentTransaction.replace(R.id.fragment, wxFragment);
break;
default:
break;
}
fragmentTransaction.commit();
}
}
这里要注意的是要用到的包是app包,导错包的话就会报错的,附上结果截图一张
我在这边因为设置了按钮的单机事件,所以点击不同的按钮就会跳转不同的fragment中
ViewPager+Fragment实现页卡滑动
下面,我们来做一个关于页卡滑动的实例,这里因为ViewPager是属于四包,所以我们需要的fragment就不用修改成app包了,同样是创建三个fragment碎片,这里我就不放fragment的代码了,直接上布局代码
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
android:orientation="vertical"
tools:context="com.example.asus.mytest2.ViewPagerActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_alignParentBottom="true"
android:id="@+id/ll_viewpager">
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="大笨妞"
android:id="@+id/btn1_viewpager"/>
<Button
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="大傻妞"
android:id="@+id/btn2_viewpager"/>
<Button
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="大蠢妞"
android:id="@+id/btn3_viewpager"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_above="@id/ll_viewpager"
android:id="@+id/ll">
<View
android:layout_width="0dp"
android:layout_height="2dp"
android:layout_weight="1"
android:background="#000000"
android:id="@+id/view1"/>
<View
android:layout_width="0dp"
android:layout_height="2dp"
android:layout_weight="1"
android:id="@+id/view2"/>
<View
android:layout_width="0dp"
android:layout_height="2dp"
android:layout_weight="1"
android:id="@+id/view3"/>
</LinearLayout>
<android.support.v4.view.ViewPager
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/viewPager"
android:layout_above="@id/ll">
</android.support.v4.view.ViewPager>
</RelativeLayout>
这里说一下,关于ViewPager的使用我们这需要用到适配器,具体可以去看看我写的适配器博客,这里我就直接发出代码
public class ViewAdapter extends FragmentPagerAdapter {
private List<Fragment>fragmentList;
public ViewAdapter(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代码了
public class ViewPagerActivity extends AppCompatActivity implements View.OnClickListener {
private ViewPager viewPager;
private BenFragment benFragment;
private ShaFragment shaFragment;
private ChunFragment chunFragment;
private Button btn1, btn2, btn3;
private View view1, view2, view3;
private List<Fragment> fragmentList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_pager);
viewPager = (ViewPager) findViewById(R.id.viewPager);
view1 = findViewById(R.id.view1);
view2 = findViewById(R.id.view2);
view3 = findViewById(R.id.view3);
btn1 = (Button) findViewById(R.id.btn1_viewpager);
btn2 = (Button) findViewById(R.id.btn2_viewpager);
btn3 = (Button) findViewById(R.id.btn3_viewpager);
view1.setBackgroundColor(Color.BLACK);
benFragment = new BenFragment();
shaFragment = new ShaFragment();
chunFragment = new ChunFragment();
fragmentList.add(benFragment);
fragmentList.add(shaFragment);
fragmentList.add(chunFragment);
ViewAdapter adapter = new ViewAdapter(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) {
view1.setBackgroundColor(Color.WHITE);
view2.setBackgroundColor(Color.WHITE);
view3.setBackgroundColor(Color.WHITE);
switch (position) {
case 0:
view1.setBackgroundColor(Color.BLACK);
break;
case 1:
view2.setBackgroundColor(Color.BLACK);
break;
case 2:
view3.setBackgroundColor(Color.BLACK);
break;
default:
break;
}
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
btn1.setOnClickListener(this);
btn2.setOnClickListener(this);
btn3.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn1_viewpager:
viewPager.setCurrentItem(0);
break;
case R.id.btn2_viewpager:
viewPager.setCurrentItem(1);
break;
case R.id.btn3_viewpager:
viewPager.setCurrentItem(2);
break;
default:
break;
}
}
}
public void onPageSelected:用来获取选中页面时的光标变色
viewPager.setCurrentItem(0):用来实现点击跳转到对应的fragment页面
附上结果截图一张
Fragment的生命周期
这里先放上一张关于fragment生命周期的图片
可以看到Fragment比Activity多了几个额外的生命周期回调方法:
onAttach(Activity)
当Fragment与Activity发生关联时调用。
onCreateView(LayoutInflater, ViewGroup,Bundle)
创建该Fragment的视图
onActivityCreated(Bundle)
当Activity的onCreate方法返回时调用
onDestoryView()
与onCreateView想对应,当该Fragment的视图被移除时调用
onDetach()
与onAttach相对应,当Fragment与Activity关联被取消时调用
其实这张图已经说明了activity的生命周期,但是在这里需要的注意的是,
(1)onCreat是activity正在被创建,也就是说此时的UI操作不会更新UI,比如setText操作,所以此时在子线程调用setText不会报线程错误。详解可见Android子线程更新View的探索,在这个方法内我们可以做一些初始化工作。
(2)onRestart需要注意的是:activity正在重新启动,一般情况下,activity从不可见状态到可见状态,onRestart才会被调用,但是一定要注意的是一般来说这是用户行为导致activity不可见的时候,此时变为可见的时候才会调用onRestart,这里所说的用户行为就是用户按home键,或者进入“新”的activity。这样的操作会使activity先执行onPause,后执行onStop,这样回到这个activity会调用onRestart。为什么我这里强调说用户行为导致的不可见状态,等下我会说。。。。
(3)onStart的时候,activity才可见,但是没有出现在前台,无法与用户交互
(4)onResume的时候,activity已经可见,并且出现在前台开始活动,与onStart相比,activity都已经可见,但是onStart的时候activity还在后台,onResume才显示在前台
(5)onPause主要注意的是:此时的activity正在被停止,接下来马上调用onStop。特殊情况下快速回到该activity,onStop不会执行,会去执行onResume。
一般在这个生命周期内做存储数据、停止动画工作,但不能太耗时。
为什么特殊强调呢,因为该activity的onPause执行完了,才回去执行新的activity的onResume,一旦耗时,必然会拖慢新的activity的显示。