Android学习第十二篇——Fragment基础学习

原创 2016年08月31日 11:11:22

今天要讲的是Fragment(意思是“碎片片段”),这是在Android3.0中引入的,主要目的是用在大屏幕设备上——例如平板电脑上,支持更加动态和灵活的UI设计。平板电脑的屏幕要比手机的大得多,有更多的控件来放更多的UI组件,并且这些组件之间会产生更多的交互。


通过这张图片我们就可以明显的对比一下了。


接下来分四部分进行学习!


Part 1. 静态加载Fragment


所谓的静态加载就是我们直接在一个布局文件中添加<fragment/>标签,例如下面代码

pageone.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="vertical" >

    <fragment
        android:id="@+id/frag_one"
        android:name="com.example.fragmentdemo.Fragment1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>
当我们这样使用<fragment/>的时候,必须添加android:name=""这个属性,而该属性的值就是我们自己创建的一个类(继承了Fragment的类)

我们来看一下这个类

Fragment1.java

package com.example.fragmentdemo;

import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class Fragment1 extends Fragment {

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		
		View view = inflater.inflate(R.layout.fragment_one, container,false);
		TextView tv_one = (TextView) view.findViewById(R.id.tv_one);
		tv_one.setText("靜態加載Fragment");
		return view;
	}
}
在这个类中我们只实现了一个方法onCreateView()方法,通过这个方法,每次创建都会绘制Fragment的View组件时回调该方法。我们具体来看一下该方法内部的代码,看到第一行代码的时候,我们发现,这里我们居然又一次出现一个布局,这个布局是干嘛的呢??

我们先把问题放一下,之前讲到过,Fragment的意思是碎片和片段,这个是相对于整个Activity来说是零碎的一部分,但是自己内部又是一个完整的,举个例子:Activity好比是一套房,Fragment好比是这套房里的某个房间,一套房存在许多房间,而一个房间里又可以存放很多东西,所有的房间+房间内的东西 = 一套房子。想必讲到这里,前面的问题就应该明白了吧!最开始的时候,我们只是给了一个房间,现在我们是在给房间里添东西。

fragment_one.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="vertical" >

    <TextView
        android:id="@+id/tv_one"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <Button
        android:id="@+id/btn_one"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>

看完这些小的以后,我们回到大的来看MainActivity

activity_main.xml(MainActivity的布局文件)

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    
    <Button 
        android:id="@+id/btn1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="静态加载Fragment"/>
    <Button 
        android:id="@+id/btn2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="动态加载Fragment"/>
    <Button 
        android:id="@+id/btn3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Fragment生命周期"/>
    <Button 
        android:id="@+id/btn4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Fragment传值通信"/>
    
</LinearLayout>


MainActivity.java

@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		button1 = (Button) findViewById(R.id.btn1);
		button1.setOnClickListener(new btn1Click());
	}
	
	class btn1Click implements OnClickListener{

		@Override
		public void onClick(View v) {
			// TODO Auto-generated method stub
			Intent intent = new Intent(MainActivity.this,PageOne.class);
			startActivity(intent);
		}
	}
	

我们点击了第一个按钮后跳转到PageOne.class页面上,这个页面就是我们之前存放Fragment的页面

PageOne.java

package com.example.fragmentdemo;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class PageOne extends Activity {

	Button btn_one;
	TextView tv_one;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		setContentView(R.layout.pageone);
		btn_one = (Button) findViewById(R.id.btn_one);
		btn_one.setText("改变TextView的内容");
		tv_one = (TextView) findViewById(R.id.tv_one);
		btn_one.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				tv_one.setText("TextView改变了!");
			}
		});
	}
}

在这个代码中我们可以发现,我们居然可以在这里直接通过findViewById()获取到fragment_one.xml上的组件,而不需要通过view.findViewById()这样,这是因为我们先加载了整个布局文件,也就是说我们已经加载了<fragmeng/>,因此我们可以直接访问fragment_one.xml中的组件,这个与fragment的生命周期有关,我们后面讲到生命周期的时候可以自己去看看。


效果显示



可能我上面讲的比较凌乱,没有一个调理,这里我把一个流程捋一捋。


首先创建项目 -->在主布局上放置四个Button -->在创建一个类PageOne.java(继承Activity,记得添加到AndroidManifest中)--> 为刚才的类创建布局pageone.xml,在这个布局中放置<fragmeng/>--> 因为这个布局中需要一个name属性,因此我们需要在创建一个类Fragment1.java(继承自Fragment),然后实现OnCreateView()方法--> 这里我们有需要创建一个布局fragment_one.xml--> 这些好了以后把之前的name属性补全,然后把页面跳转代码和按钮点击事件代码完成就差不多了。


Part 2. 动态加载Fragment


动态添加是指我们不再把Fragment固定的写在XML布局中,而是我们通过FragmentManager和FragmentTransaction来动态创建。

同样的,我们需要一个Fragment2.java(可以直接复制之前的Fragment1.java)

button2 = (Button) findViewById(R.id.btn2);
		button2.setOnClickListener(new btn2Click());//放在MainActivity的OnCreate中
……
class btn2Click implements OnClickListener{

		@Override
		public void onClick(View v) {
			Fragment2 frag = new Fragment2();
			FragmentManager fragmentManager = getFragmentManager();
			FragmentTransaction beginTransaction = fragmentManager.beginTransaction();
			//把我们的Fragment放到一个容器中(放到一个布局中去)
			beginTransaction.add(R.id.container, frag);
			//使得我们的后退点击后,可以看到之前的状态,放到一个后退栈中
			beginTransaction.addToBackStack(null);
			//最后必须提交事务(Transaction)
			beginTransaction.commit();
		}
	}





在这里我有一个疑问还没解决,就是之前我们看到在PageOne中,我们可以直接获取那些组件,在这里我们该怎么获取上面的组件呢??



Part 3. Fragment生命周期


我们先来看一下在在开发者文档中给出的生命周期图

                                       


查看生命周期的方法就是通过打印log日志,在控制台中查看输出

MainActivity

class btn3Click implements OnClickListener{

		@Override
		public void onClick(View v) {
			// TODO Auto-generated method stub
			Intent intent = new Intent(MainActivity.this,PageTwo.class);
			startActivity(intent);
		}
	}

PageTwo

package com.example.fragmentdemo;

import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class PageTwo extends Activity {

	private Button button;
	private Fragment frag;
	private boolean flag = true;

	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		setContentView(R.layout.pagetwo);
		init();
		button = (Button) findViewById(R.id.button);
		button.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				FragmentManager fragmentManager = getFragmentManager();
				FragmentTransaction beginTransaction = fragmentManager
						.beginTransaction();
				
				if (flag) {
					Fragment4 frag4=new Fragment4();
					beginTransaction.replace(R.id.layout, frag4);
					flag=false;

				} else {
					Fragment3 frag3=new Fragment3();
					beginTransaction.replace(R.id.layout, frag3);
					flag=true;

				}
				
			   beginTransaction.commit();
			}
		});

	}

	private void init() {
		// TODO Auto-generated method stub

		FragmentManager fragmentManager = getFragmentManager();
		FragmentTransaction beginTransaction = fragmentManager
				.beginTransaction();
		Fragment3 frag3 = new Fragment3();
		beginTransaction.add(R.id.layout,frag3);
		beginTransaction.commit();
	}

}


Fragment3.java

package com.example.fragmentdemo;

import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class Fragment3 extends Fragment {

	private TextView tv;

	// 启动Fragment——>屏幕锁屏——>屏幕解锁——>
	//切换到其他的Fragment——>回到桌面——>回到应用——>退出Fragment
	/**
	 * 每次创建都会绘制Fragment的View组件时回调该方法
	 */
	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		View view = inflater.inflate(R.layout.fragment2, container, false);
		TextView tv = (TextView) view.findViewById(R.id.text);
		tv.setText("第一个Fragment");
		Log.i("Main", "Fragment1---onCreateView()");
		return view;
	}

	/**
	 * 当Fragment被添加到Activity时候会回调这个方法,并且只调用一次
	 */
	@Override
	public void onAttach(Activity activity) {
		// TODO Auto-generated method stub
		super.onAttach(activity);
		Log.i("Main", "Fragment1---onAttach()");

	}

	/**
	 * 创建Fragment时会回调,只会调用一次
	 */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		Log.i("Main", "Fragment1---onCreate()");

	}

	/**
	 * 当Fragment所在的Activty启动完成后调用
	 */
	@Override
	public void onActivityCreated(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onActivityCreated(savedInstanceState);
		Log.i("Main", "Fragment1---onActivityCreated()");

	}

	/**
	 * 启动Fragment
	 * 
	 */
	@Override
	public void onStart() {
		// TODO Auto-generated method stub
		super.onStart();
		Log.i("Main", "Fragment1---onStart()");

	}

	/**
	 * 恢复Fragment时会被回调,调用onStart()方法后面一定会调用onResume()方法
	 */
	@Override
	public void onResume() {
		// TODO Auto-generated method stub
		super.onResume();
		Log.i("Main", "Fragment1---onResume()");

	}

	/**
	 * 暂停Fragment
	 */
	@Override
	public void onPause() {
		// TODO Auto-generated method stub
		super.onPause();
		Log.i("Main", "Fragment1---onPause()");

	}

	/**
	 * 停止Fragment
	 */
	@Override
	public void onStop() {
		// TODO Auto-generated method stub
		super.onStop();
		Log.i("Main", "Fragment1---onStop()");

	}

	/**
	 * 销毁Fragment所包含的View组件时
	 */
	@Override
	public void onDestroyView() {
		// TODO Auto-generated method stub
		super.onDestroyView();
		Log.i("Main", "Fragment1---onDestroyView()");

	}

	/**
	 * 销毁Fragment时会被回调
	 */
	@Override
	public void onDestroy() {
		// TODO Auto-generated method stub
		super.onDestroy();
		Log.i("Main", "Fragment1---onDestroy()");

	}

	/**
	 * Fragment从Activity中删除时会回调该方法,并且这个方法只会调用一次
	 */
	@Override
	public void onDetach() {
		// TODO Auto-generated method stub
		super.onDetach();
		Log.i("Main", "Fragment1---onDetach()");
	}
}

Fragment4.java

package com.example.fragmentdemo;

import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class Fragment4 extends Fragment {

	private TextView tv;

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		View view = inflater.inflate(R.layout.fragment2, container, false);
		TextView tv = (TextView) view.findViewById(R.id.text);
		tv.setText("第二个Fragment");
		Log.i("Main", "Fragment2---onCreateView()");
		return view;
	}

	@Override
	public void onAttach(Activity activity) {
		// TODO Auto-generated method stub
		super.onAttach(activity);
		Log.i("Main", "Fragment2---onAttach()");
	
	}

	@Override
	public void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		Log.i("Main", "Fragment2---onCreate()");

	}

	@Override
	public void onActivityCreated(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onActivityCreated(savedInstanceState);
		Log.i("Main", "Fragment2---onActivityCreated()");
	
	}

	@Override
	public void onStart() {
		// TODO Auto-generated method stub
		super.onStart();
		Log.i("Main", "Fragment2---onStart()");
	
	}

	@Override
	public void onResume() {
		// TODO Auto-generated method stub
		super.onResume();
		Log.i("Main", "Fragment2---onResume()");
		
	}
	
	@Override
	public void onPause() {
		// TODO Auto-generated method stub
		super.onPause();
		Log.i("Main", "Fragment2---onPause()");
	
	}
	
	@Override
	public void onStop() {
		// TODO Auto-generated method stub
		super.onStop();
		Log.i("Main", "Fragment2---onStop()");
		
	}
	
	@Override
	public void onDestroyView() {
		// TODO Auto-generated method stub
		super.onDestroyView();
		Log.i("Main", "Fragment2---onDestroyView()");

	}
	
	@Override
	public void onDestroy() {
		// TODO Auto-generated method stub
		super.onDestroy();
		Log.i("Main", "Fragment2---onDestroy()");
	
	}
	
	@Override
	public void onDetach() {
		// TODO Auto-generated method stub
		super.onDetach();
		Log.i("Main", "Fragment2---onDetach()");
	}
}


接下来依据这样的步骤来查看生命周期


启动Fragment(点击主布局的第三个按钮)——>屏幕锁屏——>屏幕解锁——>切换到其他的Fragment——>回到桌面——>回到应用——>退出Fragment










Part 4. Fragment和Activity的传值通信


Activity -->  Fragment : 在Activity中创建Bundle数据包,并且调用Fragment的setArguments(Bundle bundle)方法


Fragment  -->  Activity : 需要在Fragment中定义一个内部回调接口,让包含该Fragment的Activity实现该回调接口,这样Fragment可调用该回调方法将数据传递给Activity


一下这是开发文档中的两句话

All Fragment-to-Fragment communication is done through the associated Activity. Two Fragments should never communicate directly.

Fragment和Fragment之间的通信需要通过Activity的协助,两个Fragment之间不能直接通信。


Fragment -->  Activity的实现方法

To allow a Fragment to communicate up to its Activity, you can define an interface in the Fragment class and implement it within the Activity. The Fragment captures the interface implementation during its onAttach() lifecycle method and can then call the Interface methods in order to communicate with the Activity.


要使得Fragment能够和Activity通信,我们可以在Fragment类中声明一个接口,然后让Activity实现这个接口,Fragment可以捕捉这个接口在它的onAttach()方法中实现,调用这个接口方法完成和Activity的通信。


接下来我们按照下面的流程来实现上面的通信

第一步:Activity发送信息给Fragment(动态添加Fragment)

第二步:Fragment接受信息并且显示在TextView中

第三步:Fragment发送确认收到信息并且给Activity发送感谢

第四步:Activity吐司表示“不客气”

第五步:静态在Activity中添加Fragment,并且给Fragment发送信息(吐司显示信息)


最后我们来看一下代码(我把所有的代码都贴出了,看的时候可以根据上面的步骤来看)

在MainActivity中知识添加了一个按钮的监听事件,实现跳转,不多讲。


先看布局文件pagethird.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/third"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <EditText
        android:id="@+id/etText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <Button
        android:id="@+id/btnSend"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="发送" />

    <fragment
        android:id="@+id/fragthird"
        android:name="com.example.fragmentdemo.Fragment1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>


PageThird.java

public class PageThird extends Activity implements MyListener {

	private EditText etText;
	private Button btnSend;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		setContentView(R.layout.pagethrid);
		
		etText = (EditText) findViewById(R.id.etText);
		btnSend = (Button) findViewById(R.id.btnSend);
		
		btnSend.setOnClickListener(new OnClickListener() {
			//这部分是动态添加Fragment
			@Override
			public void onClick(View v) {
				String message = etText.getText().toString();
				Bundle bundle = new Bundle();
				bundle.putString("message", message);
				Fragment55 fragment = new Fragment55();
				fragment.setArguments(bundle);
				
				FragmentManager fragmentManager = getFragmentManager();
				FragmentTransaction beginTransaction = fragmentManager.beginTransaction();
				beginTransaction.add(R.id.third, fragment, "fragment5");
				beginTransaction.commit();
				Toast.makeText(PageThird.this, "向Fragment发送数据" + message,
						Toast.LENGTH_SHORT).show();
			}
		});
		
		
		/*
		 * 这部分是静态添加Fragment
		 */
		FragmentManager fragmentManager = getFragmentManager();
		Fragment findFragmentById = fragmentManager.findFragmentById(R.id.fragthird);
		Fragment1 frag = (Fragment1) findFragmentById;
		frag.setAaa("fragment静态传值");
	
	}
	
	//实现Fragment55中的接口
	@Override
	public void thank(String code) {
		// TODO Auto-generated method stub
		Toast.makeText(PageThird.this, "已成功接收到" + code + ",客气了!",
				Toast.LENGTH_SHORT).show();
	}
}

Fragment55.java

public class Fragment55 extends Fragment {

	private String code="Thank you,Activity!";
	
	public interface MyListener{
		public void thank(String code); 
	}
	
	public MyListener listener;
	@Override
	public void onAttach(Activity activity) {
		// TODO Auto-generated method stub
		listener = (MyListener) activity;
		super.onAttach(activity);
	}
	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		View view = inflater.inflate(R.layout.fragment2, container, false);
		TextView tv = (TextView) view.findViewById(R.id.text);
		String message = getArguments().getString("message");
		tv.setText(message);
		Toast.makeText(getActivity(), "已成功接收到"+message, Toast.LENGTH_SHORT).show();
		Toast.makeText(getActivity(), "向Activity发送"+code, Toast.LENGTH_SHORT).show();
		listener.thank(code);
		return view;
	}
}

我们还会对之前的Fragment1.java做一些修改

public class Fragment1 extends Fragment {

	private String aaa;

	public String getAaa() {
		return aaa;
	}

	public void setAaa(String aaa) {
		this.aaa = aaa;
	}

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {

		View view = inflater.inflate(R.layout.fragment_one, container, false);
		TextView tv_one = (TextView) view.findViewById(R.id.tv_one);
		Button button = (Button) view.findViewById(R.id.btn_one);
		tv_one.setText("静态加载Fragment");
		button.setText("获取内容");
		button.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				String value = getAaa();
				Toast.makeText(getActivity(), "value=" + value,
						Toast.LENGTH_SHORT).show();
			}
		});
		return view;
	}
}


我在弄的时候出现了这么一个异常:android.view.InflateException: Binary XML file line #19: Error inflating class fragment

后来找了一下代码,发现在Fragment1.java中的Button的id获取错了,所以大家在编写的时候一定要仔细



Demo资源下载




版权声明:本文为博主原创文章,未经博主允许不得转载。

#Android学习#Fragment简单基础——Tabbed Activity使用

Android Studio提供了很多建立不同Activity的方法,为我们开发带来了极大的方便。 今天学习了Fragment的使用方法,Fragment相对于Activity来说,切换起来会比较方便...
  • cyfloel
  • cyfloel
  • 2016年03月02日 17:52
  • 4026

Android UI开发第十二篇——动画效果Animation

Android框架本身就使用了大量的动画效果,比如Activity切换的动画效果,Dialog弹出和关闭时的渐变动画效果以及Toast显示信息时的淡入淡出效果等等。Android系统框架为我们提供了一...
  • xyz_lmn
  • xyz_lmn
  • 2011年10月26日 13:22
  • 15051

Android基础部分再学习---全面理解Fragment

fragment的学习主要是理解他到底起什么作用,然后是如何动态的切换fragment不出错,也就是管理fragment 作用就是:fragment可以当做view使用,更好的特点就是这个(fr...

android基础之fragment的学习

fragment是嵌在activity内部的模块,其有自己的生命周期,但其生命周期必须依赖于activity的存在而存在,在API 11之前每个fragment必须与父FragmentActivity...

Pro Android学习笔记(三九):Fragment(4):基础小例子-续

Step 3:实现简介显示类DetailFragment 在Activity的布局xml中,对DetailFragment并没有指定class属性,故在setContentView()中不会自动调用该...

Pro Android学习笔记(三七):Fragment(2):基础小例子

小例子运行效果 这是一个书名和书简介的例子。运行如下图。Activity由左右两个Fragment组成,左边显示书名列表,右边显示书的简介。用户点击左边的书名,右边fragment则自动显示该书的简介...

Android基础学习之Fragment(碎片)

fragment不是view只是占位符。 1.xml布局视图,此布局是Activity的视图,添加了一个碎片 ...

android基础学习之Fragment

android、Fragment

Android编程权威指南(第二版)学习笔记(十)—— 第10章 使用 fragment argument

本章主要介绍了如何使用 fragment 的 arguments 来传递信息
  • kniost
  • kniost
  • 2016年10月21日 11:07
  • 511

android学习笔记——Fragment(碎片)

这位大神的博客很值得大家好好读读。http://blog.csdn.net/lmj623565791/article/details/37970961 碎片的简单用法 使用静态的方式来添加两个碎片...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android学习第十二篇——Fragment基础学习
举报原因:
原因补充:

(最多只允许输入30个字)