通过博客《ViewFlipper简单学习笔记 》可以知道ViewFlipper的工作原理,我们可以调用showNext()或者showPrevious()两个方法来实现页面的切换,但是这个组件并没有对事件没有做处理,不像ViewPager这个组件那样重写了onInterceptTouchEvent(主要负责事件的拦截)和onTouchEvent(主要负责事件的处理)方法,可以让用户手指滑动的时候是在页面滚动翻页的效果。(关于Android的事件的处理机制可以参考博主的另外两篇博客《 android事件拦截处理机制详解》和《从源码角度分析android事件分发处理机制 》来加深理解)。在Android事件原理的基础上所以ViewFlippler响应手指事件来翻页的第一个简单版(v.1.0)本就出炉了,关键代码如下:
//该方法位于MyViewFlipper extends ViewFlipper或者ViewAnimator(ViewAnimator为ViewFlipper的父类)
@Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
showNext();
break;
}
return true;
}
//当点击导航页面的时候,显示导航页的下一页
viewFlipper.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
viewFlipper.showNext();
}
});
private Context ctx;//记录Context对象
public MyViewFlipper(Context context) {
super(context);
ctx = context;
// TODO Auto-generated constructor stub
}
public MyViewFlipper(Context context, AttributeSet attrs) {
super(context, attrs);
ctx = context;
// TODO Auto-generated constructor stub
}
private float mLastMotionX;
//这个变量解决手指一直移动的时候,连续翻页的问题
private boolean flag = false;
@Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
mLastMotionX = event.getX();
break;
case MotionEvent.ACTION_MOVE:
final float x = event.getX();
final float dx = x - mLastMotionX;
if (dx > 0) {// 手指从左到右滑动
if (!flag && getDisplayedChild() != 0) {
setInAnimation(AnimationUtils.loadAnimation(ctx,
R.anim.push_right_in));
setOutAnimation(AnimationUtils.loadAnimation(ctx,
R.anim.push_right_out));
showPrevious();
}
} else {// 手指从右到左滑动
if (!flag && getDisplayedChild() != getChildCount() - 1) {
setInAnimation(AnimationUtils.loadAnimation(ctx,
R.anim.push_left_in));
setOutAnimation(AnimationUtils.loadAnimation(ctx,
R.anim.push_left_out));
showNext();
}
}
if (dx != 0) {
flag = true;
}
break;
case MotionEvent.ACTION_UP:
flag = false;
break;
}
return true;
}
1)判断手指滑动的距离,当移动距离是负值的时候,就显示下一页;当移动的距离是正直的时候就显示上一页。注意因为ViewFlipper是循环显示的,也就是说当一直调用showNext()方法显示下一页当显示到最后一页的时候如果再次调用showNext()方法的话就会显示第一页了,所以在处理翻页的时候要特别判断一下。当然调用showPrevious方法显示上一页也是一样的道理。
2)这里有一个变量flag(真不知道该为此变量如何命名,就简单的写成flag)很重要,在满足滑动条件的时候设为true,有isFlipping的意思或者说是否正在进行翻页的动作的意思,如果为false,说明还没有进行翻页操作,就进行翻页。设置此变量主要是用来解决随着手指的滑动一直不停的调用showNext或者showPrevious方法造成的页面闪烁问题。需要注意的是在手指抬起的时候这个flag要重新设置成false。
3)在这里用一个ctx变量来存储Context,供AnimationUtils.loadAnimation使用,其实也可以用View自带的getContext()方法,因为在Android解析xml文件创建View对象的时候已经为这个View或者ViewGroup创建了初始化了Context对象。
当然上面的写法有个缺点,就是把翻页动画写死了,这种很明显扩展性不是很好,为此我把上述代码修改为如下方式:
public interface TurnPageAnimation{
/**
* 显示下一页的动画
*/
void showNextAnimation();
/**
* 显示上一页的动画
*/
void showPreviousAnimation();
}
private TurnPageAnimation mTurnPageAnimation;
private float mLastMotionX;
private boolean flag = false;
@Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
mLastMotionX = event.getX();
break;
case MotionEvent.ACTION_MOVE:
final float x = event.getX();
final float dx = x - mLastMotionX;
if (dx > 0) {// 手指从左到右滑动
if (!flag && getDisplayedChild() != 0) {
if(mTurnPageAnimation!=null) {
mTurnPageAnimation.showPreviousAnimation();
}
showPrevious();
}
} else {// 手指从右到左滑动
if (!flag && getDisplayedChild() != getChildCount() - 1) {
if(mTurnPageAnimation!=null) {
mTurnPageAnimation.showNextAnimation();
}
showNext();
}
}
if (dx != 0) {
flag = true;
}
break;
case MotionEvent.ACTION_UP:
flag = false;
break;
}
return true;
}
public void setTurnPageAnimation(TurnPageAnimation mTurnPageAnimation) {
this.mTurnPageAnimation = mTurnPageAnimation;
}
public void showAnimation(Animation in,Animation out) {
if(in!=null) {
setInAnimation(in);
}
if(out !=null) {
setOutAnimation(out);
}
}
viewFlipper = (MyViewFlipper)findViewById(R.id.viewFlipper);
viewFlipper.setTurnPageAnimation(new TurnPageAnimation() {
@Override
public void showPreviousAnimation() {
viewFlipper.showAnimation(AnimationUtils.loadAnimation(viewFlipper.getContext(),
R.anim.push_right_in), AnimationUtils.loadAnimation(viewFlipper.getContext(),
R.anim.push_right_out));
}
@Override
public void showNextAnimation() {
viewFlipper.showAnimation(AnimationUtils.loadAnimation(viewFlipper.getContext(),
R.anim.push_left_in), AnimationUtils.loadAnimation(viewFlipper.getContext(),
R.anim.push_left_out));
}
});
事实上网上看的一些关于导航页的例子,往往都会由翻页导航条或则几个圆点表明当前位于哪一页。比如实现下面的效果:
比如上图,当位于第一页的时候,该导航圆点的样式和别的不一样来区别此时位于哪一页。当然这个用ViewPager很简单就能实现这种效果的导航页,本篇博客的目的主要是改造一下ViewFlipper,来体会一下Android自定义空间的乐趣,功能不一定很强大,贵在理解其中的原理。言归正传,下面开始简单的由ViewFlipper实现这种效果的方式(该版本暂且为V3.0,在V2.0(上面的demo)的基础上做了简单修改,包括xml配置文件也做了简单修改)
实现上面的功能,我们需要知道什么时候翻页,以及当前页的索引;所以LZ模仿了ViewPager,也为自己的ViewFlipper提供了OnPageChangeListener接口,来监听分页事件:
public interface OnPageChangeListener{
/**
* 当翻页的时候调用这个方法
* @param pageIndex 当前页索引
*/
public void onPageSelected(int pageIndex);
}
private OnPageChangeListener mOnPageChanageListener;
public void setOnPagerChanagerListener(OnPageChangeListener pageChanagerListener) {
this.mOnPageChanageListener = pageChanagerListener;
}
如上面的博文所说在手指移动的距离!=0的时候说明开始执行showNext或者showPrevious方法开始翻页,同时ViewFliper本身也提供了getDisplayedChild()方法来表明当前页是第几页,所以我们在如下代码中最翻页动作做了监听(详见上文):
if (dx != 0) {
if(!flag) {
if(mOnPageChanageListener!=null) {
mOnPageChanageListener.onPageSelected(getDisplayedChild());
}
}
flag = true;
}
下面就可以再你的页面中初始化图例所示的圆点点了:
viewGroup = (ViewGroup) findViewById(R.id.viewGroup);
imageViews = new ImageView[viewFlipper.getChildCount()];
for (int i = 0; i < imageViews.length; i++) {
imageView = new ImageView(this);
imageView.setLayoutParams(new LayoutParams(40, 40));
imageView.setPadding(20, 0, 20, 0);
imageViews[i] = imageView;
if (i == 0) {
// 默认选中第一张图片
imageViews[i]
.setBackgroundResource(R.drawable.page_indicator_focused);
} else {
imageViews[i].setBackgroundResource(R.drawable.page_indicator);
}
viewGroup.addView(imageViews[i]);
}
viewFlipper.setOnPagerChanagerListener(new OnPageChangeListener() {
@Override
public void onPageSelected(int pageIndex) {
for (int i = 0; i < imageViews.length; i++) {
imageViews[pageIndex]
.setBackgroundResource(R.drawable.page_indicator_focused);
if (pageIndex != i) {
imageViews[i]
.setBackgroundResource(R.drawable.page_indicator);
}
}
}
});