viewPager与Fragment的组合方式是可以说是当前App开发的标配,所以放在一篇博客中进行描述。
Fragment
Fragment是Acttivity界面一个组成部分,基本上目前使用到多个页面的情况下都会用到,其实Fragment最大的作用是分担Activity的代码压力,为了后期维护方便,不把所有的代码罗列到一个Activity中。
构建Fragment
1.Activity布局文件为Fragment设置容身之处
<FrameLayout
android:id="@+id/main_myfragment"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</FrameLayout >
-
自定义类继承Fragment,一般继承android.app包下的Fragment,但是android.app包下的只支持安卓11版本以上的, 当向下兼容时,android-supper-v4.jar包中的。
当继承supper-v4包中的Fragment时候,获取管理器的方法getFragmentManager()是Activity的方法,他返回的是android.app包下的Fragment,这个时候需要换继承类,把原本继承Activity的主类去继承FragmentActivity。这样获取管理器的方法更改为getSupportFragmentManager() - 复写onCreateView方法,用来初始化Fragment,每一个Fragment都可以填充布局文件,一般我们都会为每一个Fragment提供一个布局文件。
//用来初始化Fragment
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// 返回填充的View对象,第一个参数为我们自定义的布局文件
View view = inflater.inflate(R.layout.fragment1 , null);
return view ;
}
- MainActivity中构建需要显示的Fragment,利用容器
//创建出需要显示的Fragment对象
Myfragment f1 = new Myfragment();
//获取管理器
FragmentManager fm = getFragmentManager();
//开启事务
FragmentTransaction ft = fm.beginTransaction();
//显示容器中显示那个Fragment
ft.replace(R.id.main_myfragment, f1);
//提交
ft.commit();
Activity与Fragment之间的信息交互
首先我没弄过明确一点是Fragment就是存在于Activity当中的,只是填充Fragment的布局文件不存在于Activity中,所以我们在当前Activity中可以拿到Frament的引用
-
Activity中获取Fragment中的信息
Fragment中提供方法给Activity调用即可,例如我们Fragment中定义了一个公共方法,Activity只需要调用
//在Fragment中定义方法
public void setText(String msg) {
TextView tv = (TextView) view.findViewById(R.id.tv_fg1);
tv.setText(msg);
}
//Activity中调用即可
myFragment.setText("hello!");
-
Fragment获取Activity 的信息
同样是Activity提供方法给Fragment调用,但是Fragment需要先获取Activity对象,其API为:getActivity()
例如Activity中提供了setText()方法,我们在Fragment中定义以下方法,获取到EditText输入的信息,然后获取Activity对象之后调用该方法。
public void setText(String msg) {
et_fg = (EditText) inflate.findViewById(R.id.et_fg );
Button bt = (Button) inflate.findViewById(R.id.bt );
bt.setOnClickListener( new OnClickListener(){
@Override
public void onClick(View v) {
String text = et_fg.getText().toString();
((MainActivity) getActivity()).setText(text);
}
});
}
Fragment生命周期
-
Fragment的从创建到显示在当前Activity中共经历了6个方法:
-
onAttach(Context context)
Fragment第一次和它的Activity建立关联,从该方法开始,就可以通过Fragment.getActivity方法获取与Fragment关联的窗口对象,但因为Fragment的控件未初始化,所以不能够操作控件。 -
onCreate(Bundle savedInstanceState)
Fragment创建时调用,在onAttach之后,在onCreateView之前,可以在Bundle对象中获取一些在Activity中传过来的数据。通常会在该方法中读取保存的状态,获取或初始化一些数据。在该方法中不要进行耗时操作,不然窗口不会显示。 -
onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState)
必须覆盖此方法,调用此方法实例化Fragment的用户界面,inflater是用来装载布局文件的,container是Fragment父级别的对应对象,savedInstanceState参数可以获取Fragment保存的状态,如果未保存那么就为null -
onViewCreated(View view,Bundle savedInstanceState)
Fragment的Activity创建了,并且Fragment的显示内容完毕,在Activity的onCreate方法执行完之后,Android系统会立刻调用该方法,表示窗口已经初始化完成,从这一个时候开始,就可以在Fragment中使用getActivity().findViewById(Id);来操控Activity中的view了。 -
onStart()
当Fragment可见时调用,与Activity的onStart绑定,当系统调用该方法的时候,fragment已经显示在ui上,但还不能进行互动,因为onResume方法还没执行完。 -
onResume()
当Fragment 对用户可见并且处于Running状态,当Fragment加载之后就不再执行该方法
-
onAttach(Context context)
-
从显示状态到销毁需要5个方法
-
onPause()
当Fragment不再处于resumed状态,通常可以在这个方法中保存一些需要临时暂停的工作,如保存音乐播放进度 -
onStop()
当onStop返回的时候,fragment将从屏幕上消失。 -
onDestoryView()
当onCreateView创建的视图与fragment解除关联时调用,下次Fragment要显示,需要重新创建View -
onDestroy()
Android在Fragment不再使用时会调用该方法,要注意的是这时Fragment还和Activity相关联的!并且可以获得Fragment对象,但无法对获得的Fragment进行任何操作。 -
onDetach()
为Fragment生命周期中的最后一个方法,当该方法执行完后,Fragment与Activity不再有关联。
-
注意点:
-
当按home键退出之后,调用onPause和onStop方法,而再次进入调用onStart和onResume。
-
当一个Activity对应多个Fragment时,当切换不同Fragment时,旧的销毁,新的创建。
ViewPager
关于ViewPager最常见的就是大多数App首页的广告banner,各种轮播图,ViewPager不是android.jar包中,而是support.v4的类。他可以实现界面的左右滑动。
首先说下ViewPager生成界面和销毁界面原理
- 判断屏幕显示的是否是0位置,如果不是说明左边要有界面,调用isViewFromObject判断左边是否已存在界面,如不存在则调用instantiateItem方法来生成界面
- 判断屏幕显示的是否是getCount – 1位置,如果不是说明右边要有界面,调用isViewFromObject判断右边是否已存在界面,如不存在则调用instantiateItem方法来生成界面
- 超出范围的界面调用destroyItem销毁
构建ViewPager实现轮播图
- 布局xml:
<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=".MainActivity">
<android.support.v4.view.ViewPager
android:id="@+id/vp_poster"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignBottom ="@id/vp_poster"
android:background="@android:color/darker_gray"
android:gravity="center"
android:orientation="vertical" >
<TextView
android:id="@+id/tv_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="界面描述"
android:textColor="@android:color/background_dark"
android:textSize="16sp" />
</LinearLayout >
</RelativeLayout>
- 获取数据源,当前从本地获取图片与文字
/** 设置数据模型 */
private int[] imageResIds = { R.drawable.a, R.drawable.b, R.drawable.c,
R.drawable. d, R.drawable.e, };
private String[] descs = { "大吉大利" , "招财纳福" ,"万事如意" , "阖家团圆" , };
- 获取控件,设置适配器
vpPoster = (ViewPager) findViewById(R.id.vp_poster);
//设置适配器
PosterAdaper mPagerAdapter = new PosterAdaper(imageResIds );// 适配器
vpPoster.setAdapter(mPagerAdapter );
- 适配器设置
public class PosterAdaper extends PagerAdapter {
private int[] imageResIds ;
public PosterAdaper( int[] imageResIds) {
this.imageResIds = imageResIds;
}
/**
* 获取全部的布局数量,一般要写一个非常大的数,而且保证可以一直循环下去
*
* @return
*/
@Override
public int getCount() {
return imageResIds .length * 100000;
}
/**
* 实例化化一个布局文件
*
* @param container
* @param position
* @return
*/
@Override
public Object instantiateItem(ViewGroup container, int position) {
position = position % imageResIds.length ;
// 我们要填充一张图片
ImageView ivPoster = new ImageView(container.getContext());
ivPoster.setBackgroundResource( imageResIds[position]);
// 注意要手动添加到该组中
container.addView(ivPoster);
return ivPoster;
}
/**
* 销毁一个布局文件
*
* @param container
* @param position
* @param object
*/
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
// container.removeViewAt(position);
container.removeView((View) object);
}
/**
* 是否复用缓存
*
* @param view
* @param object
* @return
*/
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
}
- 设置图片的左右无限(相对而言)次的滑动
/* 1.在getCount 方法返回一个比较大的页数: imageSize * 10000 * 100;
2.在所有使用position的地方( instantiateItem、 onPageSelected)先对这个position 取余:
position = position % imageSize;,取余之后再使用position,确保 position不会导致脚标越界. */
//设置ViewPager从页数为一半的情况开始滑动,这样就左右都可以循环滑动
vpPoster.setCurrentItem(vpPoster .getAdapter().getCount()/2);
- 监听viewpage页面切换,赋值数据,正常情况下,文字数据应该传入到Adapter中统一赋值,但此处为了演示方便,写在了Adapter外面
private ViewPager.OnPageChangeListener vpListener = new ViewPager.OnPageChangeListener() {
/** 当某一页被选中的时候 */
@Override
public void onPageSelected(int position) {
changeDesc(position);
}
/** 当页面滑动 */
@Override
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
}
/** 当页面滑动状态发生改变时 */
@Override
public void onPageScrollStateChanged(int state) {
}
};
//图片消息填充
private void changeDesc(int position) {
position = position % descs.length;
tvDesc.setText(descs [position]);
}
- 设置定时轮转图片,利用handler的轮转查询和延时发送消息可以做到:
public static final int SHOW_NEXT_PAGE = 0;
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
if (msg.what == SHOW_NEXT_PAGE) {
nextPage();
}
}
};
public void nextPage(){
vpPoster.setCurrentItem(vpPoster .getCurrentItem()+1);
//发消息给handler,延时3秒后重新回来调用此方法
handler.sendEmptyMessageDelayed(SHOW_NEXT_PAGE,5000);
}
//注意需要在oncreate方法中开启一下handler
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
vpPoster = findViewById(R.id.vp_poster);
tvDesc = findViewById(R.id.tv_desc);
vpPoster.addOnPageChangeListener(vpListener);
tvDesc.setText(descs[0]);
//设置适配器
PosterAdaper mPagerAdapter = new PosterAdaper(imageResIds);
vpPoster.setAdapter(mPagerAdapter);
//设置默认初始图片与描述文字
vpPoster.setCurrentItem(vpPoster .getAdapter().getCount()/2);
changeDesc(0);
//开启轮播
handler.sendEmptyMessageDelayed(SHOW_NEXT_PAGE, 3000);
}
在onDestory方法中解除handler的死循环链,释放该Activity
@Override
protected void onDestroy() {
super.onDestroy();
handler.removeCallbacksAndMessages(null);
}