安卓基础之Fragment与ViewPager

  • Post author:
  • Post category:其他


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 >
  1. 自定义类继承Fragment,一般继承android.app包下的Fragment,但是android.app包下的只支持安卓11版本以上的, 当向下兼容时,android-supper-v4.jar包中的。

    当继承supper-v4包中的Fragment时候,获取管理器的方法getFragmentManager()是Activity的方法,他返回的是android.app包下的Fragment,这个时候需要换继承类,把原本继承Activity的主类去继承FragmentActivity。这样获取管理器的方法更改为getSupportFragmentManager()
  2. 复写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 ;
 }
  1. 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的引用

  1. 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!");
  1. 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生命周期对比

  • 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加载之后就不再执行该方法
  • 从显示状态到销毁需要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实现轮播图

  1. 布局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>
  1. 获取数据源,当前从本地获取图片与文字
/** 设置数据模型 */
private int[] imageResIds = { R.drawable.a, R.drawable.b, R.drawable.c,
        R.drawable. d, R.drawable.e, };

private String[] descs = { "大吉大利" , "招财纳福" ,"万事如意" , "阖家团圆" , };
  1. 获取控件,设置适配器
vpPoster = (ViewPager) findViewById(R.id.vp_poster);
//设置适配器
PosterAdaper mPagerAdapter = new PosterAdaper(imageResIds );// 适配器
vpPoster.setAdapter(mPagerAdapter );
  1. 适配器设置
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. 设置图片的左右无限(相对而言)次的滑动
/* 1.在getCount 方法返回一个比较大的页数: imageSize * 10000 * 100;
2.在所有使用position的地方( instantiateItem、 onPageSelected)先对这个position 取余:
position = position % imageSize;,取余之后再使用position,确保 position不会导致脚标越界. */
//设置ViewPager从页数为一半的情况开始滑动,这样就左右都可以循环滑动
vpPoster.setCurrentItem(vpPoster .getAdapter().getCount()/2);
  1. 监听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]);
}
  1. 设置定时轮转图片,利用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);
}



版权声明:本文为wygsqsj原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。