扩展ViewFlipper做导航页(一)

来源:互联网 发布:vscode搭建rails 编辑:程序博客网 时间:2024/06/06 04:28

通过博客《ViewFlipper简单学习笔记 》可以知道ViewFlipper的工作原理,我们可以调用showNext()或者showPrevious()两个方法来实现页面的切换,但是这个组件并没有对事件没有做处理,不像ViewPager这个组件那样重写了onInterceptTouchEvent(主要负责事件的拦截)和onTouchEvent(主要负责事件的处理)方法,可以让用户手指滑动的时候是在页面滚动翻页的效果。(关于Android的事件的处理机制可以参考博主的另外两篇博客《 android事件拦截处理机制详解》和《从源码角度分析android事件分发处理机制 》来加深理解)。在Android事件原理的基础上所以ViewFlippler响应手指事件来翻页的第一个简单版(v.1.0)本就出炉了,关键代码如下:

//该方法位于MyViewFlipper extends ViewFlipper或者ViewAnimator(ViewAnimator为ViewFlipper的父类)@Overridepublic boolean onTouchEvent(MotionEvent event) {// TODO Auto-generated method stubint action = event.getAction();switch (action) {case MotionEvent.ACTION_DOWN: showNext();break;}      return true;}

该版本很简单,就是在重写onTouchEvent,该方法很简单,就是简单的响应down事件:用户点击手机屏幕的时候调用父类ViewFlipper的showNext方法,自动显示下一页;其实换种思路,如果只是响应down事件来显示来显示下一页的话,完全不用重新定义ViewFlipper或者ViewAnimator,设置ViewFlipper或者ViewAnimator的onClickListener完全可以实现同样的效果:

//当点击导航页面的时候,显示导航页的下一页viewFlipper.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {viewFlipper.showNext();}});

简单的功能肯定不能适应各种情况,比如这个该版本就只能查看下一页而没办法查看上一页,所以需要继续扩展功能,代码还需要修改,在做了简单的修改之后onTouchEvent代码如下:

     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;@Overridepublic boolean onTouchEvent(MotionEvent event) {// TODO Auto-generated method stubint 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;}

如此,简单的左右滑动实现左右翻页的I效果实现了,而且都添加了左右滑动的动画效果。实现思路很简单:

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;@Overridepublic boolean onTouchEvent(MotionEvent event) {// TODO Auto-generated method stubint 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的时候就可以这样设置:

viewFlipper = (MyViewFlipper)findViewById(R.id.viewFlipper);viewFlipper.setTurnPageAnimation(new TurnPageAnimation() {@Overridepublic void showPreviousAnimation() {viewFlipper.showAnimation(AnimationUtils.loadAnimation(viewFlipper.getContext(),R.anim.push_right_in), AnimationUtils.loadAnimation(viewFlipper.getContext(),R.anim.push_right_out));}@Overridepublic void showNextAnimation() {viewFlipper.showAnimation(AnimationUtils.loadAnimation(viewFlipper.getContext(),R.anim.push_left_in), AnimationUtils.loadAnimation(viewFlipper.getContext(),R.anim.push_left_out));}});

到此位置简单的ViewFlipper就算全程了,只是简单的写了重写了onTouchEvent事件,其实这也是个自定义View,在有些人看来自定义View很神秘很复杂,其实只要弄清需求,扩展相应的功能就是了,有的需要重新写onMeasrue有的需重写onLayout甚至onDraw不一而足,当然有的确实很复杂LZ也不知道怎么实现,正在一点点模仿和积累。(demo点击此处下载)。

事实上网上看的一些关于导航页的例子,往往都会由翻页导航条或则几个圆点表明当前位于哪一页。比如实现下面的效果:

比如上图,当位于第一页的时候,该导航圆点的样式和别的不一样来区别此时位于哪一页。当然这个用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之后就可以为ViewFlipper初始化翻页监听,代码如下:

viewFlipper.setOnPagerChanagerListener(new OnPageChangeListener() {@Overridepublic 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);}}}});

到此为止,上图中所示的导航效果就实现完毕了,(demo代码点此);虽然说功能不是很强大,而且很多地方还有扩展的地方,比如翻页体验来说就没有ViewPager实现的效果好,当然这和ViewPager的滚动原理是分不开的,这点以后会继续深入研究,敬请期待吧,当然如果本篇博文有什么不正确的地方,还请指正,共同学习。





0 0
原创粉丝点击