我学习Android都是结合源代码去学习,这样比较直观,非常清楚的看清效果,觉得很好,今天的学习源码是网上找的源码 百度搜就知道很多下载的地方 网上源码的名字叫:android gif模式和图片展现模式 图片展现神器.zip 我的博客写的比较乱,如果本篇文章没有看懂,请先看上篇文章,地址:http://blog.csdn.net/u014737138/article/details/40795303
关于ViewPager控件,就是实现翻页的效果,类似于我们现在的手机打开可以左右滑动,或者有些APP菜单的滑动效果 ,今天要学习的就是这样一个控件,标题是实现ViewPager的步骤,我觉得还是先直接给出步骤来,然后慢慢解释
1.要确保有依赖包存在:android-support-v4.jar 一般在 创建项目的时候就会自动出现在libs文件下面,如果没有
请到网上下载最新版本的放在libs文件下面,然后配置下
2.在布局文件在使用这个控件,也就是在xml文件中使用这个控件,定义一个id
3.定义一个适配器,主要的工作就是将数据显示在ViewPager上,思路类似于ListView需要一个Adapter
4.在布局文件对应的activity类中先定义必要的变量
5.对ViewPager控件进行初始化工作处理
6.实现翻页的监听事务处理
做完上面的6步基本就能够实现了ViewPager的控件效果
如果你需要做到菜单的滑动处理,你还需要增加一些TextView 或者图片,然后给这些控件添加事务处理,也就是点击事件,在点击事件里面启动这个ViewPager的监听事件就可以了。
接下来我们一步一步的去实现这个翻页的效果:
1.确定依赖包的存在:
2.在XML文件中定义ViewPager
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<android.support.v4.view.ViewPager
android:id="@+id/contentPager"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="center"
android:flipInterval="30"
android:persistentDrawingCache="animation" />
这里我只提供了这一点核心的布局code,完整的请下载源码对着看,文章标题提示了哪里有下载
注意这个组件是用来显示左右滑动的界面的,如果不加载xml布局文件,他是不会显示内容的。
3.定义一个适配器 PagerAdapter
import java.util.List;
import android.os.Parcelable;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.View;
public class BookFisherPagerAdapter extends PagerAdapter {
private List<View> views;
public BookFisherPagerAdapter(List<View> views){
this.views=views;
}
@Override
public void destroyItem(View arg0, int arg1, Object arg2) {
// TODO Auto-generated method stub
((ViewPager) arg0).removeView(views.get(arg1));
}
@Override
public void finishUpdate(View arg0) {
// TODO Auto-generated method stub
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return views.size();
}
@Override
public Object instantiateItem(View arg0, int arg1) {
// TODO Auto-generated method stub
((ViewPager) arg0).addView(views.get(arg1), 0);
return views.get(arg1);
}
@Override
public boolean isViewFromObject(View arg0, Object arg1) {
// TODO Auto-generated method stub
return arg0 == (arg1);
}
@Override
public void restoreState(Parcelable arg0, ClassLoader arg1) {
// TODO Auto-generated method stub
}
@Override
public Parcelable saveState() {
// TODO Auto-generated method stub
return null;
}
@Override
public void startUpdate(View arg0) {
// TODO Auto-generated method stub
}
}
这里有必要介绍下PagerAdapter必须要重载的5个函数的功能分别是什么:
有一点要注意,这个继承类中我们定义了一个成员变量:
private List<View> views;
1)构造函数
public BookFisherPagerAdapter(List<View> views){
this.views=views;
}
2)删除一个指定的页卡:就是从ViewPager控件上删除指定的页卡
@Override
public void destroyItem(View arg0, int arg1, Object arg2) {
// TODO Auto-generated method stub
((ViewPager) arg0).removeView(views.get(arg1));
}
3)实例化一个指定的页卡:就是向ViewPager控件上添加指定的页卡
@Override
public Object instantiateItem(View arg0, int arg1) {
// TODO Auto-generated method stub
((ViewPager) arg0).addView(views.get(arg1), 0);
return views.get(arg1);
}
4)返回页卡的数量
@Override
public int getCount() {
// TODO Auto-generated method stub
return views.size();
}
5)官网指定必须要做的一个函数,目前我也不知道这个函数是啥作用,从名字应该能猜出来
@Override
public boolean isViewFromObject(View arg0, Object arg1) {
// TODO Auto-generated method stub
return arg0 == (arg1);
}
4.在实现该布局文件的activity类中定义必要的变量
这里的变量就非常的多了,我们一个一个来说了
1)必须要构造出视图的集合,也就是一个个视图,填充在ViewPager控件上的
我们这里展示的是利用图片做视图,一个视图就是一个图片,
A.图片的资源id
private int[] ids = { R.drawable.a1, R.drawable.a2, R.drawable.a3,
R.drawable.a4, R.drawable.a5, R.drawable.a6, R.drawable.a7,
R.drawable.a8, R.drawable.a9, R.drawable.a10, R.drawable.a11,
R.drawable.a12, R.drawable.a13, R.drawable.a14, R.drawable.a15,
R.drawable.a16, R.drawable.a17, R.drawable.a18, R.drawable.a19,
R.drawable.a20, R.drawable.a21, R.drawable.a22 };
B.当我们知道了这些图片的资源ID之后,我们就可以用这些id去构造一个一个视图了
for (int i = 0; i < ids.length; i++) {
ImageView iv = new ImageView(this);
iv.setImageBitmap(readBitMap(getApplicationContext(), ids[i]));
// iv.setImageResource();
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.FILL_PARENT,
ViewGroup.LayoutParams.FILL_PARENT);
iv.setLayoutParams(params);
iv.setScaleType(ScaleType.CENTER_CROP);
//guides.add(iv);
}
这个代码中的guides.add(iv)先可以不用看。注意了这里定义批量视图的方法是怎么样的?而不是利用XML布局来实现的。
其中有个函数readBitMap(),我们来看看代码:
public static Bitmap readBitMap(Context context, int resId) {
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inPreferredConfig = Bitmap.Config.RGB_565;
opt.inPurgeable = true;
opt.inInputShareable = true;
// 获取资源图片
InputStream is = context.getResources().openRawResource(resId);
return BitmapFactory.decodeStream(is, null, opt);
}
C.当这些视图定义完了,我们就需要把它们放在一个数组中,因为我们的Adpater的构造参数是一个数组。
private List<View> guides = new ArrayList<View>();
放在数组中的操作就是上面的 guides.add(iv);
这样一来需要填充的数据就准备好了,放在guides数组中了
2)找到布局xml文件中对应的ViewPager控件的对象
private ViewPager pager;
找到这个控件,并设置他的监控事件
pager = (ViewPager) findViewById(R.id.contentPager);
pager.setAdapter(adapter);
3.偏移量参数
private int offset;// 位移量
4.动画图片
private ImageView curDot;
3和4两个参数是紧密相连的,我这里先给出做法,就是当我们要滑动到右边(下一页)的时候,我们需要记住当前的视图的宽度,这个宽度就是从当前的视图到下一个视图的偏移量。
这里我们的做法就是利用 视图树观察者 对象 来监听这样的事件 ViewTreeObserver
curDot.getViewTreeObserver().addOnPreDrawListener(
new OnPreDrawListener() {
public boolean onPreDraw() {
// TODO Auto-generated method stub
offset = curDot.getWidth();
return true;
}
});
上面的代码是首先得到一个视图树观察者对象,然后调用它的监听函数:addOnPreDrawListener
这个监听事件是处理一个回调函数的,就是说当有这样的事件发生的时候,它需要去做后面的函数里面的代码:
onPreDraw()
这样的事件叫什么呢?就是当视图树观察者发现有一个新的视图需要画出来之前调用这个回调函数处理
Since: API Level 1
Register a callback to be invoked when the view tree is about to be drawn
listener | The callback to add |
---|
IllegalStateException | If isAlive() returns false |
---|
5.处理翻页效果的事务处理(主要的就是如何实现向右滑动的效果)
</pre><pre name="code" class="java"> pager.setOnPageChangeListener(new OnPageChangeListener() {
public void onPageSelected(int arg0) {
// TODO Auto-generated method stub
moveCursorTo(arg0);
curPos = arg0;
}
public void onPageScrolled(int arg0, float arg1, int arg2) {
// TODO Auto-generated method stub
}
public void onPageScrollStateChanged(int arg0) {
// TODO Auto-generated method stub
}
});
这里有必要把这三个回调的接口函数进行学习下,搞清楚他们到底是干嘛的?是什么时候被调用的?参数都代表什么意义,我们才能更好地去学习它:
A.状态改变的时候回调函数
public void onPageScrollStateChanged(int arg0) {
// TODO Auto-generated method stub
}
参数:arg0 int类型:int有三种值,我们就是根据int来确定干什么的
有三种状态(0,1,2)。
arg0 ==1的时候表示正在滑动,
arg0==2的时候表示滑动完毕,
arg0==0的时候表示什么都没做。
当页面开始滑动的时候,三种状态的变化顺序为(1,2,0)
switch (arg0) {
case 1:
break;
case 2:
break;
case 0:
break;
}
B.页面在滑动的时候调用的回调函数
public void onPageScrolled(int arg0, float arg1, int arg2) {
// TODO Auto-generated method stub
}
其中三个参数的含义分别为: arg0 :当前页面,及你点击滑动的页面 arg1:当前页面偏移的百分比 arg2:当前页面偏移的像素位置
注意:当页面在滑动的时候会调用此方法,在滑动被停止之前,此方法回一直得到。如果你没有业务需要的话就不用写
C.页面跳转之后调用的方法(也即是比较常用需要处理的回调函数,当我们向右滑动的完毕之后需要调用的方法)
public void onPageSelected(int arg0) {
// TODO Auto-generated method stub
moveCursorTo(arg0);
curPos = arg0;
}
参数: arg0是你当前选中的页面的Position(位置编号)
弄懂了这三个需要重载的函数的意义,接下来就来分析下我们需要做的东西了:
当我们的页面向右滑动的时候,我们需要去重载onPageSelected(int arg0)这个函数,这个函数的参数是表示当前的页面
当我们手指触摸向右滑动,就是需要做这样的一个操作,把下页视图给我显示出来(这里也就是显示下一页的图片)
这个业务处理就是在这个函数
onPageSelected(int arg0)里面进行处理的,看到这行代码没有:moveCursorTo(arg0),它做的事情就是从当前页把动画移动到下页
那么接下来我们要处理的就是怎么去移动到下页去呢?我们接下来看看这个函数是怎么处理的:
private void moveCursorTo(int position) {
// 使用绝对位置
TranslateAnimation anim = new TranslateAnimation(offset * curPos,
offset * position, 0, 0);
anim.setDuration(300);
anim.setFillAfter(true);
curDot.startAnimation(anim);
}
这个函数里面使用了一个 移动动画 对象:TranslateAnimation anim;这个对象的构造参数是:
offset*curPos:偏移量乘以当前的位置:动画开始的X坐标
offset*postion:偏移量乘以当前的页面:动画结束的X坐标
0:动画开始的Y坐标
0:动画结束的Y坐标
我们先来看看官网的API:
Since: API Level 1
Constructor to use when building a TranslateAnimation from code
fromXDelta | Change in X coordinate to apply at the start of the animation |
---|---|
toXDelta | Change in X coordinate to apply at the end of the animation |
fromYDelta | Change in Y coordinate to apply at the start of the animation |
toYDelta | Change in Y coordinate to apply at the end of the animation |
float fromXDelta:这个参数表示动画开始的点离当前View X坐标上的差值; float toXDelta, 这个参数表示动画结束的点离当前View X坐标上的差值; float fromYDelta, 这个参数表示动画开始的点离当前View Y坐标上的差值; float toYDelta)这个参数表示动画开始的点离当前View Y坐标上的差值;
如果view在A(x,y)点 那么动画就是从B点(x+fromXDelta, y+fromYDelta)点移动到C 点(x+toXDelta,y+toYDelta)点
看到这里我们再来解释下上面的代码是什么意思了:
TranslateAnimation anim = new TranslateAnimation(offset * curPos,offset * position, 0, 0);
我们把ViewPager看成是一个大的View 它的宽度是W,所有的图片也就是每一个视图都在上面滑动,
我们这里把每个图片的宽度作为偏移量,图片都是存在数组里面的,数组的下标也就代表了当前是第几个图片,
根据第几个图片乘以宽度就可以得出当前的图片在W上面的宽度,也就是只需要改变X坐标了,Y坐标都是同一个高度,不需要变化
anim.setDuration(300);动画时长,0.3秒播放完毕
anim.setFillAfter(true); 图片动画播放完了,如果我没有动的话,就应该让动画停留在当前的页面
curDot.startAnimation(anim);curDot是当前的视图树监控的对象,也就是当前被展现出来的视图,需要在这个视图上显示这个图片动画
到这里,一个ViewPager的原理就搞清楚了 但是还有很多问题没有问题:
比如:我们需要去根据我们移动的距离去确定需要移动到下一页,假如是偏移量值达到120,就应该马上进入下一页,
等等问题,请继续看我的下一篇文章。