Android应用中使用ViewPager实现类似QQ的界面切换效果

时间:2022-12-05 13:08:17

这几天在研究viewpager,简单的写一下如何使用viewpager实现类似于qq的“最近联系人、好友、群组”的界面切换(不知道他们是不是用这个方法实现的)。
viewpager已经在android-sdk中加入了,具体的位置在%android_sdk_home%\android-compatibility\v4,%android_sdk_home%是你的android-sdk-windows目录。

好,下面放一张效果图:

Android应用中使用ViewPager实现类似QQ的界面切换效果

步骤一:新建一个工程,我的是viewpager
步骤二:导入jar包
具体如下:
右键工程--properties--单击左侧java build path--单击右侧libraries标签--单击右侧add external jars--选择上面描述位置的jar包,保存

步骤三:编写一个主界面,加入android.support.v4.view.viewpager组件

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:orientation="vertical" >
 
  <textview
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="@string/hello" />
   
   
  <android.support.v4.view.viewpager
    android:id="@+id/mviewpager"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    />
 
   
</linearlayout>

 

步骤四:编写三个要添加进来的界面
界面都很简单,随便写的
界面1

?
1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:background="#ff0000"
  android:orientation="vertical" >
 
  <button
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="layout1" />
 
</linearlayout>

界面2

?
1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:background="#00ff00"
  android:orientation="vertical" >
 
  <textview
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="layout2" />
 
</linearlayout>

 

界面3

?
1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:background="#0000ff"
  android:orientation="vertical" >
 
  <textview
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="layout3" />
 
</linearlayout>

 

步骤五:编写主类,我的名字是叫viewpageractivity

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package com.carter.viewpager;
 
import java.util.arraylist;
import java.util.list;
import java.util.zip.inflater;
 
import android.app.activity;
import android.app.activitygroup;
import android.content.intent;
import android.os.bundle;
import android.support.v4.view.pageradapter;
import android.support.v4.view.viewpager;
import android.support.v4.view.viewpager.onpagechangelistener;
import android.util.log;
import android.view.layoutinflater;
import android.view.view;
import android.view.viewgroup;
 
public class viewpageractivity extends activitygroup {
   
  /**
   * 能够支持滑动的组件
   */
  viewpager mviewpager;
   
  /**
   * viewpager 需要设置该adapter才能实现添加界面,
   * 该adapter继承自pageradapter
   */
  mypageradapter adapter;
   
  /**
   * 用于保存三个界面
   */
  list<view> list; 
   
  /**
   * 用于生成界面
   */
  layoutinflater minflater;
   
   
   
   
  /** called when the activity is first created. */
  @override
  public void oncreate(bundle savedinstancestate) {
    super.oncreate(savedinstancestate);
    setcontentview(r.layout.main);
     
    minflater = this.getlayoutinflater();
     
    /**
     * 添加进来三个界面到list,用于初始化pageradapter
     */
    list = new arraylist<view>();
    list.add(this.getlocalactivitymanager().startactivity("layout1", new intent(viewpageractivity.this, layout1activity.class)).getdecorview());
    list.add(minflater.inflate(r.layout.layout2, null));
    list.add(minflater.inflate(r.layout.layout3, null));
     
    mviewpager = (viewpager)findviewbyid(r.id.mviewpager);
    adapter = new mypageradapter(list);
    mviewpager.setadapter(adapter);
     
    /**
     * 为viewpager添加pagechangelistener,在回调的方法中可以完成其他的事情,例如处理一些界面标签的转换之类的,如果你有标签栏的话
     */
    mviewpager.setonpagechangelistener(new onpagechangelistener() {
       
      @override
      public void onpageselected(int arg0) {
        // todo auto-generated method stub
        log.i("carter", "onpageselected");
      }
       
      @override
      public void onpagescrolled(int arg0, float arg1, int arg2) {
        // todo auto-generated method stub
         
      }
       
      @override
      public void onpagescrollstatechanged(int arg0) {
        // todo auto-generated method stub
         
      }
    });
     
  }
   
   
   
   
  class mypageradapter extends pageradapter
  {
    list<view> mlist;
     
    public mypageradapter(list<view> list){
      mlist = list;
    }
 
    @override
    public int getcount() {
      // todo auto-generated method stub
      return mlist.size();
    }
 
    @override
    public boolean isviewfromobject(view arg0, object arg1) {
      // todo auto-generated method stub
      return arg0 == (arg1);
    }
 
    @override
    public void destroyitem(view container, int position, object object) {
      // todo auto-generated method stub
      ((viewpager)container).removeview(mlist.get(position));
    }
 
    @override
    public object instantiateitem(view container, int position) {
      // todo auto-generated method stub
      ((viewpager)container).addview(mlist.get(position), 0);
      return mlist.get(position);
    }    
 
    @override
    public void finishupdate(viewgroup container) {
      // todo auto-generated method stub
      super.finishupdate(container);
    }
  }
}

 

代码解读:
1、在上面重写了pageradapter,在里面最重要的方法就是

?
1
2
3
4
5
6
@override
    public object instantiateitem(view container, int position) {
      // todo auto-generated method stub
      ((viewpager)container).addview(mlist.get(position), 0);
      return mlist.get(position);
    

具体为什么每次都是addview到0的位置,目前我还不知道,这个也是从网上找了一个方法看到的。

2、在初始化list的时候,里面包含的内容是一个个的view元素,在我的了解范围内有两种方法
第一种就是通过添加window的getdecorview()来得到一个view;
第二种就是通过layoutinflater,从xml中生成一个view。
需要注意的是,第一种方法,需要主类继承自activitygroup,这样才能在添加时使用getlocalactivitymanager以及之后的方法,需要生成的activity,例如本例中的layout1activity,要在androidmanifest.xml中声明。

?
1
2
3
4
5
6
7
/**
 * 添加进来三个界面到list,用于初始化pageradapter
 */
list = new arraylist<view>();
list.add(this.getlocalactivitymanager().startactivity("layout1", new intent(viewpageractivity.this, layout1activity.class)).getdecorview());
list.add(minflater.inflate(r.layout.layout2, null));
list.add(minflater.inflate(r.layout.layout3, null));

3、如果只是从xml生成view来添加的话,完全可以移除继承activitygroup。
4、如果使用startactivity的方法来启动,需要添加进来的几个activity,是一起在加入时就初始化的,可以通过在oncreate()方法中打log看出来。

ps:如何解决android下viewpager和pageradapter中调用notifydatasetchanged失效的问题
具体讲解如下:
google在android 3.0sdk中推出的viewpager控件很大程度上满足了开发者开发页面左右移动切换的功能,使用非常方便。但是使用中发现,在删除或者修改数据的时候,pageradapter无法像baseadapter那样仅通过notifydatasetchanged方法通知刷新view。
最基本的方法:
针对于child view比较简单的情况(例如仅有textview、imageview等,没有listview等展示数据的情况),可以在自己的adapter中加入代码:

?
1
2
3
4
@override
public int getitemposition(object object) { 
  return position_none; 

这样既可达到一般情况下要求的效果。
存在的问题:
这不是pageradapter中的bug,通常情况下,调用notifydatasetchanged方法会让viewpager通过adapter的getitemposition方法查询一遍所有child view,这种情况下,所有child view位置均为position_none,表示所有的child view都不存在,viewpager会调用destroyitem方法销毁,并且重新生成,加大系统开销,并在一些复杂情况下导致逻辑问题。特别是对于只是希望更新child view内容的时候,造成了完全不必要的开销。
更有效地方法:
更为靠谱的方法是因地制宜,根据自己的需求来实现notifydatasetchanged的功能,比如,在仅需要对某个view内容进行更新时,在instantiateitem()时,用view.settag方法加入标志,在需要更新信息时,通过findviewwithtag的方法找到对应的view进行更新即可。