Fragment
Fragment 可以有自己独立的事件处理和生命周期 , fragment最初是为了大屏幕显示而设计, 有了fragment后手机和平板兼容就容易了, 比如平板上 Activity A 包含
FragmentA 和 FragmentB , 手机上就可以ActivityA 包含FragmentA ; ActivityB包含FragmentB ; 就不用重新在手机上大改布局文件, 只需要小改… fragment 可以包含到多个Activity中
使用fragment需要先定义一个类, 继承Fragment 并实现其onCreateView()方法 , Fragment第一次绘制其用户界面的时候,系统会调用该方法, 为了绘制fragment的UI该方法必须返回一个View, 如果不显示UI, 返回null即可
静态和动态加载
Fragment静态加载: 就是在layout的xml文件中有定义 标签
在Activity的layout文件中声明Fragment , fragment的name属性, 指定了layout实例化的Fragment类
必须有标示, id属性唯一id 或 tag属性唯一字符串
Fragment动态加载: Activity的layout布局文件中没有定义fragment控件, 而是在Activity中用代码加载
但无论是静态还是动态 , 都需要有自定义的fragment类和这个fragment对应的layout文件
并且这个fragment类里面需要重写onCreateView方法, 返回view , 一般是把layout转换为view返回
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment1, container);
TextView tv = (TextView) view.findViewById(R.id.id_fragment1_tv);
tv.setText("fragment 1 text");
Button btn = (Button) view.findViewById(R.id.id_fragment1_btn);
btn.setText("btnOfFragment1");
return view;
}
有了fragment类后, 动态加载需要fragment事务来加载到Activity
对fragment进行添加移除替换和其他动作, 提交给Activity的每一套变化称为事务
另外, 动态加载fragment最终还是把fragment加载到某个指定ID的layout
//动态加载fragment到ID为id_fram的LinearLayout中
Fragment fragment = new MyFragment2();
FragmentManager manager = getFragmentManager();
FragmentTransaction beginTransaction = manager.beginTransaction();//开启事务
beginTransaction.add(R.id.id_frame, fragment); //把fragment加载到指定ID的layout中
beginTransaction.addToBackStack(null);//fragment添加到返回栈
beginTransaction.commit();
每个事务都是一套变化 , 包括add() remove() replace() 然后提交给Activity, 必须调用commit() 方法
如果允许用户通过按下back返回到前一个fragment状态, 调用commit之前可以加入addToBackStack()方法
Fragment和Activity通信
1 fragment可以调用getActivity方法获取它所在的Activity
2 Activity可以调用FragmentManager的findFragmentById() 或findFragmentByTag()方法获取Fragment
Activity –> Fragment :
在Activity中创建Bundle数据包, 并调用Fragment的setArguments(Bundle bundle) 方法
Fragment –> Activity :
需要在
Fragment中定义一个内部回调接口, 再让包含该Fragment的Activity实现该回调接口. 这样在Fragment类中可以调用该回调方法将数据传递给Activity
Activity 传值给 Fragment :
//Activity端
@Override
public void onClick(View v) {
String data = tv_data.getText().toString();
//Activity中准备bundle数据
MyFragment4 myFragment4 = new MyFragment4();
Bundle bundle = new Bundle();
bundle.putString("data", data);
//加载fragment前, 调用fragment对象的setArguments方法传递数据
myFragment4.setArguments(bundle);
//和往常一样动态加载fragment到某个控件
FragmentManager manager = getFragmentManager();
FragmentTransaction beginTransaction = manager.beginTransaction();
beginTransaction.replace(R.id.id_activity4_layout,myFragment4);
beginTransaction.addToBackStack(null);
beginTransaction.commit();
Toast.makeText(Activity4.this, "Activity 发送数据给Fragment4:" + data, Toast.LENGTH_SHORT).show();
}
});
//Fragment类中onCreateView方法中获取数据
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// inflater将布局文件转换为view
View view = inflater.inflate(R.layout.fragment4, container, false);
...
....
//获取Activity传递的数据;
Bundle bundle = getArguments();
String txt = bundle.get("data").toString() ;
Toast.makeText(getActivity(), "fragment 接收到数据:"+txt, Toast.LENGTH_SHORT).show();
return view;
}
Fragment传值给Activity
fragment中定义接口, 然后activity实现接口方法,
fragment中onAttach 重写的时候, 把activity转换为接口实现对象,
fragment中在合适的地方调用activity强转接口对象中的方法 , 并传值, 于是实现fragment传递数据给activity
Fragment 类中:
public class MyFragment4 extends Fragment {
//定义接口
public interface MyListener {
public void thankyou (String str);
}
//定义接口对象
MyListener mListener;
...
//复写onAttach方法时将实现了接口方法的activity强转并赋值给接口对象
@Override
public void onAttach(Activity activity) {
mListener = (MyListener) activity;
super.onAttach(activity);
}
//在需要调用接口方法, 传值的时候调用接口方法
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
View view = inflater.inflate(R.layout.fragment4, container, false);
...
Bundle bundle = getArguments();
String txt = bundle.get("data").toString() ;
Toast.makeText(getActivity(), "fragment 接收到数据:"+txt, Toast.LENGTH_SHORT).show();
//比如收到数据后, 返回一条确认字符串
mListener.thankyou(code);
return view;
}
//Activity中:
public class Activity4 extends Activity implements MyListener{
...
...
...
//通过实现接口方法, 让fragment调用接口方法来实现fragment向Activity传值
@Override
public void thankyou(String str) {
Toast.makeText(Activity4.this, "Activity 接收到数据" + str, Toast.LENGTH_SHORT).show();
}
}
activity传值给静态加载的fragment
Activity 的layout文件中有fragment字段, 静态加载MyFragment这个类的话
Fragment类中:
我们在MyFragment类中, 对需要传值的字段设置好getter , setter
public class MyFragment1 extends Fragment {
private String testField;
public String getTestField() {
return testField;
}
public void setTestField(String testField) {
this.testField = testField;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment1, container);
TextView tv = (TextView) view.findViewById(R.id.id_fragment1_tv);
tv.setText("fragment 1 text 静态加载");
Button btn = (Button) view.findViewById(R.id.id_fragment1_btn);
btn.setText("btnOfFragment1");
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String data = getTestField();
Toast.makeText(getActivity(), "testField 现在的值为:" + data, Toast.LENGTH_SHORT).show();
}
});
return view;
}
}
Activity中:
FragmentManager fragmentManager = getFragmentManager();
Fragment fragment = fragmentManager.findFragmentById(R.id.id_activity4_staticFrag);
MyFragment1 myfragment1 = (MyFragment1) fragment;
myfragment1.setTestField("测试传值给fragment");
Fragment类中 调用 getXxx() 方法即可得到Activity传递过来的数据
ViewPager
类: android.support.v4.view.ViewPager
加载显示的页卡, 页卡可以使view对象也可以使fragment对象
将layout布局文件转换为view对象的方法:
1 LayoutInflater
LayoutInflater lf = getLayoutInflater().from(this);
lf.inflate(resource,root);
2 View.inflate
View.inflate(context,resource,root);
配置Adapter
数据源可以使view也可以使fragment
1. PagerAdapter 数据源List
2. FragmentPagerAdapter 数据源 List
3. FragmentStatePagerAdapter 数据源 List
view 作为页卡 , 最简单的viewPager
//main.xml
<RelativeLayout 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"
tools:context="${relativePackage}.${activityClass}" >
<android.support.v4.view.ViewPager
android:id="@+id/id_pager"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
</android.support.v4.view.ViewPager>
</RelativeLayout>
Activity
//MainActivity.java
public class MainActivity extends Activity {
private List <View> viewList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
viewList = new ArrayList<View>();
/***
- view 对象作为数据源
*/
View view1 = View.inflate(this, R.layout.view1, null);
View view2 = View.inflate(this, R.layout.view2, null);
View view3 = View.inflate(this, R.layout.view3, null);
View view4 = View.inflate(this, R.layout.view4, null);
viewList.add(view1);
viewList.add(view2);
viewList.add(view3);
viewList.add(view4);
//创建pager对象
ViewPager pager = (ViewPager) findViewById(R.id.id_pager);
//加载适配器
MyPagerAdapter adapter = new MyPagerAdapter(viewList);
pager.setAdapter(adapter);
}
}
//MyPagerAdapter
package com.example.imooc_viewpager;
import java.util.List;
import android.support.v4.view.PagerAdapter;
import android.view.View;
import android.view.ViewGroup;
public class MyPagerAdapter extends PagerAdapter {
List<View> mViewList ;
public MyPagerAdapter(List<View> viewList) {
mViewList = viewList;
}
/**
- 返回页卡数量
*/
@Override
public int getCount() {
// TODO Auto-generated method stub
return mViewList.size();
}
/***
- 判断view 是否来自对象
*/
@Override
public boolean isViewFromObject(View arg0, Object arg1) {
// TODO Auto-generated method stub
return arg0 == arg1;
}
/**
- 实例化一个页卡 , viewpager一般是前, 后, 当前页卡, 一共3个页卡为单位的去操作
*/
@Override
public Object instantiateItem(ViewGroup container, int position) {
// return super.instantiateItem(container, position);
container.addView(mViewList.get(position));
return mViewList.get(position);
}
/***
- 销毁一个页卡 , 需要从viewGroup中移除一个view
*/
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView(mViewList.get(position));
}
}
加入页卡标题
1 XML文件中 , 加入PagerTabStrip 控件
<android.support.v4.view.ViewPager
android:id="@+id/id_pager"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<android.support.v4.view.PagerTabStrip
android:id="@+id/id_pager_tabStrip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top" > //设置tabStrip在顶端显示
</android.support.v4.view.PagerTabStrip>
</android.support.v4.view.ViewPager>
2 Activity中准备标题数据
private List <String> titleList;
titleList = new ArrayList<String> ();
titleList.add("第一页");
titleList.add("第二页");
titleList.add("第三页");
titleList.add("第四页");
3 自定义的MyPagerAdapter 类中实现getPageTitle 方法
//构造函数中传递titleList
List<View> mViewList ;
List<String> mTitleList;
public MyPagerAdapter(List<View> viewList , List<String> titleList) {
mViewList = viewList;
mTitleList = titleList;
}
......
/**
* 设置页卡标题
*/
@Override
public CharSequence getPageTitle(int position) {
return mTitleList.get(position);
}
4 设置属性
//为tabStrip 设置属性
tabStrip = (PagerTabStrip) findViewById(R.id.id_pager_tabStrip);
tabStrip.setBackgroundColor(Color.YELLOW); //背景色
tabStrip.setTextColor(Color.RED); //文字颜色
tabStrip.setDrawFullUnderline(false); //去掉长的下划线
tabStrip.setTabIndicatorColor(Color.GREEN); //设置当前单项粗点的线的颜色
类似PagerTabStrip的还有PagerTitleStrip
<android.support.v4.view.PagerTitleStrip
android:id="@+id/id_pager_titleStrip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom" >
</android.support.v4.view.PagerTitleStrip>
使用Fragment作为Pager数据源
1 XML文件中 , 加入PagerTabStrip 控件
<android.support.v4.view.ViewPager
android:id="@+id/id_pager"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<android.support.v4.view.PagerTabStrip
android:id="@+id/id_pager_tabStrip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top" > //设置tabStrip在顶端显示
</android.support.v4.view.PagerTabStrip>
</android.support.v4.view.ViewPager>
2 创建Fragment类 , 注意使用的包 android.support.v4.app.Fragment; 兼容3.0以下版本
public class Fragment1 extends Fragment {
@Override
@Nullable
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.view1, container, false);
}
}
定义4个这样的fragment分别加载4个不同的layout文件
3 自定义FragmentPagerAdapter类
import java.util.List;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
/***
* FragmentPagerAdapter 和 PagerAdapter加载的时候不同的是,
* FragmentPagerAdapter 会一次把所有的fragment都加载进来, 而不是三个三个地加载
* @author hendry-code
*
*/
public class MyFragmentPagerAdapter extends FragmentPagerAdapter {
private List<Fragment> mFragList;
private List<String> mTitleList;
public MyFragmentPagerAdapter(FragmentManager fm, List<Fragment> fragList,
List<String> titleList) {
super(fm);
mFragList = fragList;
mTitleList = titleList;
}
@Override
public Fragment getItem(int arg0) {
// TODO Auto-generated method stub
return mFragList.get(arg0);
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return mFragList.size();
}
@Override
public CharSequence getPageTitle(int position) {
return mTitleList.get(position);
}
}
4 修改Activity文件, 使用fragmentAdapter
private List<Fragment> fragList;
...
/**
* Fragment 作为数据源
*/
fragList = new ArrayList<Fragment>();
fragList.add(new Fragment1());
fragList.add(new Fragment2());
fragList.add(new Fragment3());
fragList.add(new Fragment4());
titleList = new ArrayList<String> ();
titleList.add("第一页");
titleList.add("第二页");
titleList.add("第三页");
titleList.add("第四页");
//创建pager对象
pager = (ViewPager) findViewById(R.id.id_pager);
//为tabStrip 设置属性
tabStrip = (PagerTabStrip) findViewById(R.id.id_pager_tabStrip);
tabStrip.setBackgroundColor(Color.YELLOW);
tabStrip.setTextColor(Color.RED);
tabStrip.setDrawFullUnderline(false); //去掉长的下划线
tabStrip.setTabIndicatorColor(Color.GREEN); //设置当前单项粗点的线的颜色
MyFragmentPagerAdapter fragmentAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager(), fragList, titleList);
pager.setAdapter(fragmentAdapter);
由于使用Fragment 是v4兼容包的Fragment, 所以 , FragmentPagerAdapter 的构造函数中需要一个fragmentManager 不能通过 getFragmentManager()来获得, 我们需要getSupportFragmentManager() ; 为了使用这个兼容包, 我们还需要把Activity改为继承自FragmentActivity.
使用FragmentStatePagerAdapter
FragmentPagerAdapter 是一次性加载所有的页卡, 没有动态添加销毁的功能 , 验证方法 , 在第四个fragment 销毁的时候打印LOG, 然后测试切换, 发现LOG没有打印
public class Fragment4 extends Fragment {
@Override
@Nullable
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.view4, container, false);
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
Log.i("main","Fragment4 destoryed....");
}
}
所以如果页卡很多的话FragmentPagerAdapter就不适合了, 我们需要另外一个有动态添加销毁功能的 FragmentStatePagerAdapter
把adapter的定义头部改一下, 并复写创建和销毁方法 , 但这两个方法不用去改什么, 原样就可以了
public class MyFragmentPagerAdapter2 extends FragmentStatePagerAdapter {
...
...
@Override
public Object instantiateItem(ViewGroup arg0, int arg1) {
// TODO Auto-generated method stub
return super.instantiateItem(arg0, arg1);
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
// TODO Auto-generated method stub
super.destroyItem(container, position, object);
}
...
}
Activity文件中:
MyFragmentPagerAdapter2 fragmentAdapter2 = new MyFragmentPagerAdapter2(getSupportFragmentManager(), fragList, titleList);
pager.setAdapter(fragmentAdapter2);
然后再模拟器上测试, 就发现从第三页切换到第二页的时候, 第四页的销毁LOG就被打印出来了, 说明FragmentStatePagerAdapter适配器是会动态维护创建销毁的
ViewPager 监听器 OnPageChangeListener
1 类定义的头文件
public class MainActivity extends FragmentActivity implements OnPageChangeListener {
2 完成接口方法, 主要是onPageSelected
@Override
public void onPageScrollStateChanged(int arg0) {
// TODO Auto-generated method stub
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
// TODO Auto-generated method stub
}
@Override
public void onPageSelected(int arg0) {
// TODO Auto-generated method stub
Toast.makeText(this, "当前是第" + (arg0 + 1) + "页面",Toast.LENGTH_SHORT).show();
}
3 OnCreate方法中 绑定Listener , pager.setOnPageChangeListener(this);
因为Fragment有完整的生命周期, 更好控制, 一般使用fragment + viewPager组合比较多…..