欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android中的ViewPager视图滑动切换类的入门实例教程

程序员文章站 2024-03-03 15:57:28
viewpager引入示例 首先让大家有个全局的认识,直接上个项目,看看仅仅通过这几行代码,竟然就能完成如此强悍的功能。 效果图: 实现了三个view间的相互滑动。...

viewpager引入示例
首先让大家有个全局的认识,直接上个项目,看看仅仅通过这几行代码,竟然就能完成如此强悍的功能。
效果图:
实现了三个view间的相互滑动。
第一个view向第二个view滑动、第二个view向第三个view滑动

Android中的ViewPager视图滑动切换类的入门实例教程

Android中的ViewPager视图滑动切换类的入门实例教程

一、新建项目,引入viewpager控件
viewpager。它是google sdk中自带的一个附加包的一个类,可以用来实现屏幕间的切换。

1.在主布局文件里加入

<relativelayout xmlns:android="http://schemas.android.com/apk/res/android" 
  xmlns:tools="http://schemas.android.com/tools" 
  android:layout_width="fill_parent" 
  android:layout_height="fill_parent" 
  tools:context="com.example.testviewpage_1.mainactivity" > 
 
<android.support.v4.view.viewpager 
  android:id="@+id/viewpager" 
  android:layout_width="wrap_content" 
  android:layout_height="wrap_content" 
  android:layout_gravity="center" /> 
 
</relativelayout> 

其中 <android.support.v4.view.viewpager /> 是viewpager对应的组件,要将其放到想要滑动的位置

2、新建三个layout,用于滑动切换的视图
从效果图中也可以看到,我们的三个视图都非常简单,里面没有任何的控件,大家当然可以往里添加各种控件,但这里是个demo,只详解原理即可,所以我这里仅仅用背景来区别不用layout布局。

布局代码分别如下:

layout1.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:background="#ffffff" 
  android:orientation="vertical" > 
   
 
</linearlayout> 

layout2.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:background="#ffff00" 
  android:orientation="vertical" > 
   
 
</linearlayout> 

layout3.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:background="#ff00ff" 
  android:orientation="vertical" > 
   
 
</linearlayout><span style="color:#660000;"> 
</span> 

二、代码实战
先上整体代码,然后逐步讲解。

package com.example.testviewpage_1; 
import java.util.arraylist; 
import java.util.list; 
import java.util.zip.inflater; 
 
import android.app.activity; 
import android.os.bundle; 
import android.support.v4.view.pageradapter; 
import android.support.v4.view.viewpager; 
import android.view.layoutinflater; 
import android.view.view; 
import android.view.viewgroup; 
 
 
public class mainactivity extends activity { 
 
  private view view1, view2, view3; 
  private viewpager viewpager; //对应的viewpager 
   
  private list<view> viewlist;//view数组 
   
   
  @override 
  protected void oncreate(bundle savedinstancestate) { 
    super.oncreate(savedinstancestate); 
    setcontentview(r.layout.activity_main); 
     
    viewpager = (viewpager) findviewbyid(r.id.viewpager); 
    layoutinflater inflater=getlayoutinflater(); 
    view1 = inflater.inflate(r.layout.layout1, null); 
    view2 = inflater.inflate(r.layout.layout2,null); 
    view3 = inflater.inflate(r.layout.layout3, null); 
     
    viewlist = new arraylist<view>();// 将要分页显示的view装入数组中 
    viewlist.add(view1); 
    viewlist.add(view2); 
    viewlist.add(view3); 
     
     
    pageradapter pageradapter = new pageradapter() { 
       
      @override 
      public boolean isviewfromobject(view arg0, object arg1) { 
        // todo auto-generated method stub 
        return arg0 == arg1; 
      } 
       
      @override 
      public int getcount() { 
        // todo auto-generated method stub 
        return viewlist.size(); 
      } 
       
      @override 
      public void destroyitem(viewgroup container, int position, 
          object object) { 
        // todo auto-generated method stub 
        container.removeview(viewlist.get(position)); 
      } 
       
      @override 
      public object instantiateitem(viewgroup container, int position) { 
        // todo auto-generated method stub 
        container.addview(viewlist.get(position)); 
         
         
        return viewlist.get(position); 
      } 
    }; 
     
     
    viewpager.setadapter(pageradapter); 
     
  } 
 
 
} 

代码量很小,全部放在了oncreate()函数中。
1、先看声明的变量的意义:

private view view1, view2, view3; 
private list<view> viewlist;//view数组 
private viewpager viewpager; //对应的viewpager 

首先viewpager对应 <android.support.v4.view.viewpager/>控件。

view1,view2 ,view3对应我们的三个layout,即layout1.xml,layout2.xml,layout3.xml
viewlist是一个view数组,盛装上面的三个view

2、接下来是他们的初始化过程:

viewpager = (viewpager) findviewbyid(r.id.viewpager); 
layoutinflater inflater=getlayoutinflater(); 
view1 = inflater.inflate(r.layout.layout1, null); 
view2 = inflater.inflate(r.layout.layout2,null); 
view3 = inflater.inflate(r.layout.layout3, null); 
 
viewlist = new arraylist<view>();// 将要分页显示的view装入数组中 
viewlist.add(view1); 
viewlist.add(view2); 
viewlist.add(view3); 

初始化过程难度不大,就是将资源与变量联系起来布局,最后将实例化的view1,view2,view3添加到viewlist中
3、pageadapter——pageview的适配器
适配器这个东东想必大家都不莫生,在listview中也有适配器,listview通过重写getview()函数来获取当前要加载的item。而pageadapter不太相同,毕竟pageadapter是单个view的合集。

pageadapter 必须重写的四个函数:

(1)boolean isviewfromobject(view arg0, object arg1)
(2)int getcount()
(3)void destroyitem(viewgroup container, int position,object object)
(4)object instantiateitem(viewgroup container, int position)
先看看各个函数,我们上面都做了什么吧:

@override 
public int getcount() { 
  // todo auto-generated method stub 
  return viewlist.size(); 
} 

getcount():返回要滑动的view的个数

@override 
public void destroyitem(viewgroup container, int position, 
    object object) { 
  // todo auto-generated method stub 
  container.removeview(viewlist.get(position)); 
} 

destroyitem():从当前container中删除指定位置(position)的view;

@override 
public object instantiateitem(viewgroup container, int position) { 
  // todo auto-generated method stub 
    container.addview(viewlist.get(position)); 
     
     
    return viewlist.get(position); 
  } 
}; 

instantiateitem():做了两件事,第一:将当前视图添加到container中,第二:返回当前view

@override 
public boolean isviewfromobject(view arg0, object arg1) { 
  // todo auto-generated method stub 
  return arg0 == arg1; 
}

 
isviewfromobject():对于这个函数就先不做讲解,大家目前先知道它要这样重写就行了,后面我们会对它进行改写。
下面将仔细讲解这几个函数的意义,与有关key的知识。

viewpager的四大函数
一、官方说明
pageradapter比adapterview的使用更加普通.viewpager使用回调函数来表示一个更新的步骤,而不是使用一个视图回收机制。在需要的时候pageradapter也可以实现视图的回收或者使用一种更为巧妙的方法来管理视图,比如采用可以管理自身视图的fragment。

viewpager不直接处理每一个视图而是将各个视图与一个键联系起来。这个键用来跟踪且唯一代表一个页面,不仅如此,该键还独立于这个页面所在adapter的位置。当pageradapter将要改变的时候他会调用startupdate函数,接下来会调用一次或多次的instantiateitem或者destroyitem。最后在更新的后期会调用finishupdate。当finishupdate返回时 instantiateitem返回的对象应该添加到父viewgroup destroyitem返回的对象应该被viewgroup删除。methodisviewfromobject(view, object)代表了当前的页面是否与给定的键相关联。
 
对于非常简单的pageradapter或许你可以选择用page本身作为键,在创建并且添加到viewgroup后instantiateitem方法里返回该page本身即可
destroyitem将会将该page从viewgroup里面移除。isviewfromobject方法里面直接可以返回view == object。
 
pageradapter支持数据集合的改变,数据集合的改变必须要在主线程里面执行,然后还要调用notifydatasetchanged方法。和baseadapter非常相似。数据集合的改变包括页面的添加删除和修改位置。viewpager要维持当前页面是活动的,所以你必须提供getitemposition方法。

二、解析
viewpager不直接处理每一个视图而是将各个视图与一个键联系起来。这个键用来跟踪且唯一代表一个页面,不仅如此,该键还独立于这个页面所在adapter的位置。当pageradapter将要改变的时候他会调用startupdate函数,接下来会调用一次或多次的instantiateitem或者destroyitem。最后在更新的后期会调用finishupdate。当finishupdate返回时 instantiateitem返回的对象应该添加到父viewgroup destroyitem返回的对象应该被viewgroup删除。methodisviewfromobject(view, object)代表了当前的页面是否与给定的键相关联。

对于非常简单的pageradapter或许你可以选择用page本身作为键,在创建并且添加到viewgroup后instantiateitem方法里返回该page本身即可destroyitem将会将该page从viewgroup里面移除。isviewfromobject方法里面直接可以返回view == object。

对于上面两段话,我这里有两点要着重讲一下:

1、第一段说明了,键(key)的概念,首先这里要清楚的一点是,每个滑动页面都对应一个key,而且这个key值是用来唯一追踪这个页面的,也就是说每个滑动页面都与一个唯一的key一一对应。大家先有这个概念就好,关于这个key是怎么来的,下面再讲。

2、第二段简单讲了一个应用,即将当前页面本身的view作为key。其实这个应用就是我们前一章讲的例子应用。不太理解?没关系,下面细讲。下面我们讲讲key的问题。

三、关于key
现在我带着大家看看几个方法的官方文档:

1.首先,destroyitem():

该方法实现的功能是移除一个给定位置的页面。适配器有责任从容器中删除这个视图。这是为了确保在finishupdate(viewgroup)返回时视图能够被移除。
在引入部分的例子中我们是这样做的:

@override 
public void destroyitem(viewgroup container, int position, 
    object object) { 
  // todo auto-generated method stub 
  container.removeview(viewlist.get(position)); 
} 

果不其然,我们将给定位置的视图从container中移除了……

2.然后看getcount ():

public abstract int getcount ()

return the number of views available.

返回当前有效视图的个数。然后我们还:

@override 
public int getcount() { 
  // todo auto-generated method stub 
  return viewlist.size(); 
} 

返回了当前要滑动视图的个数,与官方说明一致。

四、最难的两个来了

1.instantiateitem (viewgroup container, int position)
这个函数的实现的功能是创建指定位置的页面视图。适配器有责任增加即将创建的view视图到这里给定的container中,这是为了确保在finishupdate(viewgroup)返回时this is be done!
返回值:返回一个代表新增视图页面的object(key),这里没必要非要返回视图本身,也可以这个页面的其它容器。其实我的理解是可以代表当前页面的任意值,只要你可以与你增加的view一一对应即可,比如position变量也可以做为key(最后我们举个例子试试可不可行)

心得 :

(1)从说明中可以看到,在代码中,我们的责任是将指定position的视图添加到conatiner中

(2)key的问题:从这个函数就可以看出,该函数返回值就是我们根据参数position增加到conatiner里的view的所对应的key!!!!!!!

(3)“it only must ensure this is done by the time it returns fromfinishupdate(viewgroup).”这句话在destroyitem()的函数说明中同样出现过,这说明在 finishupdate(viewgroup)执行完后,有两个操作,一个是原视图的移除(不再显示的视图),另一个是新增显示视图(即将显示的视图)

还是一开始引入的例子中,我们是这样做的:

@override 
public object instantiateitem(viewgroup container, int position) { 
  // todo auto-generated method stub 
    container.addview(viewlist.get(position)); 
     
     
    return viewlist.get(position); 
  } 
}; 

在这里,我们做了两件事
第一:将参数里给定的position的视图,增加到conatiner中,供其创建并显示、。

第二:返回当前position的view做为此视图的key。还记得api官方文档中下面这段话么?
对于非常简单的pageradapter或许你可以选择用page本身作为键,在创建并且添加到viewgroup后instantiateitem方法里返回该page本身即可destroyitem将会将该page从viewgroup里面移除。isviewfromobject方法里面直接可以返回view == object。

这里就把当前的view当作key传过出去!!!!

2.isviewfromobject (view view, object object)
功能:该函数用来判断instantiateitem(viewgroup, int)函数所返回来的key与一个页面视图是否是代表的同一个视图(即它俩是否是对应的,对应的表示同一个view)
返回值:如果对应的是同一个view,返回true,否则返回false。

在上面的例子中,我们这样做的:

@override 
public boolean isviewfromobject(view arg0, object arg1) { 
  // todo auto-generated method stub 
  return arg0 == arg1; 
} 

由于在instantiateitem()中,我们作为key返回来的是当前的view,所以在这里判断时,我们直接将key与view看是否相等来判断是否是同一个view。

五、自定义key实例
经过上面的讲解,想必大家给key的概念应该有个清楚的理解,下面举个例子来说明key与view的关系,由于key与view要一一对应,所以我把每个视图所处的位置position作为key,在上章例子的基础上更改的,下面先看全部代码,然后看部分讲解:

package com.example.testviewpage_2; 
import java.util.arraylist; 
import java.util.list; 
import android.app.activity; 
import android.os.bundle; 
import android.support.v4.view.pageradapter; 
import android.support.v4.view.viewpager; 
import android.view.layoutinflater; 
import android.view.view; 
import android.view.viewgroup; 
 
public class mainactivity extends activity { 
 
  private view view1, view2, view3; 
  private list<view> viewlist;// view数组 
  private viewpager viewpager; // 对应的viewpager 
   
  @override 
  protected void oncreate(bundle savedinstancestate) { 
    super.oncreate(savedinstancestate); 
    setcontentview(r.layout.activity_main); 
    viewpager = (viewpager) findviewbyid(r.id.viewpager); 
    layoutinflater inflater = getlayoutinflater(); 
    view1 = inflater.inflate(r.layout.layout1, null); 
    view2 = inflater.inflate(r.layout.layout2, null); 
    view3 = inflater.inflate(r.layout.layout3, null); 
 
    viewlist = new arraylist<view>();// 将要分页显示的view装入数组中 
    viewlist.add(view1); 
    viewlist.add(view2); 
    viewlist.add(view3); 
 
    pageradapter pageradapter = new pageradapter() { 
 
      @override 
      public boolean isviewfromobject(view arg0, object arg1) { 
        // todo auto-generated method stub 
        //根据传来的key,找到view,判断与传来的参数view arg0是不是同一个视图 
        return arg0 == viewlist.get((int)integer.parseint(arg1.tostring())); 
      } 
 
      @override 
      public int getcount() { 
        // todo auto-generated method stub 
        return viewlist.size(); 
      } 
 
      @override 
      public void destroyitem(viewgroup container, int position, 
          object object) { 
        // todo auto-generated method stub 
        container.removeview(viewlist.get(position)); 
      } 
 
      @override 
      public object instantiateitem(viewgroup container, int position) { 
        // todo auto-generated method stub 
        container.addview(viewlist.get(position)); 
 
        //把当前新增视图的位置(position)作为key传过去 
        return position; 
      } 
    }; 
 
    viewpager.setadapter(pageradapter); 
 
  } 
 
} 

在这里更改了两个地方:
1、先看key的产生的位置instantiateitem()

@override 
public object instantiateitem(viewgroup container, int position) { 
  // todo auto-generated method stub 
  container.addview(viewlist.get(position)); 
 
  //把当前新增视图的位置(position)作为key传过去 
  return position; 
} 

 
我们在上讲也讲了在这个函数中key是作为返回值与当前装入container中的视图对应起来的。所以在这里我们返回postion与container.addview(viewlist.get(position));里的viewlist.get(position)这个视图对应起来。
2、isviewfromobject ()

@override 
public boolean isviewfromobject(view arg0, object arg1) { 
  // todo auto-generated method stub 
  //根据传来的key,找到view,判断与传来的参数view arg0是不是同一个视图 
  return arg0 == viewlist.get((int)integer.parseint(arg1.tostring())); 
} 

判断从instantiateitem()返回来的key与当前的view是否能对应起来,我们知道从instantiateitem传过来的其实是position,所以我们要根据position找到view,然后跟参数中的view arg0判断。

但在真正操作时出现了问题,我们要先将obect对应转换为int类型:(int)integer.parseint(arg1.tostring());然后再根据position找到对应的view;

效果图:三个view之间的滑动切换

Android中的ViewPager视图滑动切换类的入门实例教程

Android中的ViewPager视图滑动切换类的入门实例教程

这里只所以与上章不一样,仅仅只有上部分一部分的地方才有滑动切换,是因为我更改了布局文件:

<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="com.example.testviewpage_2.mainactivity" > 
 
  <android.support.v4.view.viewpager 
    android:id="@+id/viewpager" 
    android:layout_width="wrap_content" 
    android:layout_height="200dip" 
    android:layout_gravity="center" /> 
 
</relativelayout> 

这里将layout_height更改为200dip,只所以这么做,是为了告诉大家,只要在想要实现滑动切换的地方添加上<android.support.v4.view.viewpager />就可以实现切换,无所谓位置和大小,跟普通控件一样!!!!!!