Android 实现 WheelView

分类:Android 1061人阅读 评论(6) 收藏 举报 wheel view

目录(?)[+]

我们都知道,在iOS里面有一种控件------滚筒控件(Wheel View),这通常用于设置时间/日期,非常方便,但Android SDK并没有提供类似的控件。这里介绍一下如何Android实现WheelView。

先来看一看iOS中的WheelView的效果图:


这个效果不错吧,我们应该如何实现呢?

那在Android如果也要实现这样一个效果,应该怎么做呢?

1.Android WheelView效果图


上图是我实现的DEMO的运行效果图。


2.网上的开源代码

我们从网上找到了一个开源的代码,它也实现了这样的效果,而且效果也不错,大家可以用SVN来checkout:

http://android-wheel.googlecode.com/svn/trunk

它这个Demo最本质是自己写布局,好像是利用一个LinearLayout来布局child,然后调用LinearLayout.draw(canvas)方法,把child绘制在指定的canvas上面。它同时还提供了类似AdapterView的访问方式,用户可以设置Adapter来提供数据。我在这里主要不是讲解这个Demo的结构,如果大家感兴趣,可以自己下载代码研究。


3.实现思路

由于网上的Demo也是提供了类似于AdapterView的访问方式,所以,我在想,我们能不能换一种方式来实现,试想,如果这个滚筒是横着的,那么我们就可以利用Gallery来实现,Gallery的特点跟WheelView有相似之处,比如:选中的项始终在View中间,只不过它是横着布局的。

由于我之前修改过Gallery的源代码,可以使其循环滚动,并且第一个child可以排列在最左端,所以,我在想,如果我能把Gallery修改成竖的(垂直排列),那这个不就是OK了吗?基于这样的想法,我就准备修改代码了。

我们这里需要把Gallery的源码复制到我们的工程中,然后修改,保证能编译通过。

与Gallery相关的的几个文件如下所示,它们都是放在widget文件夹和res/value文件夹下面。

  • AbsSpinner.java
  • AdapterView.java
  • Gallery.java
  • attr.xml

修改的过程比较麻烦,我这里不详细说明(要细说的话,内容太多了),在修改之后,我们的Gallery提供了一个方法:setOrientation(int),你可以让这个Gallery水平滑动,也可以垂直滑动。

我们还应该提供以下几个核心方法:

  • setOnEndFlingListener ------ 当Gallery停止滑动时的回调用,这样调用者可以在停止滑动时来得到当前选中的项。
  • setOrientation(int) ------ 支持布局方向:HORIZONTAL和VERTICAL。
  • setScrollCycle(boolean) ------ 是否支持循环滑动。
  • setSlotInCenter(boolean) ------ 是否让Gallery选中的项居中。

4. 扩展Gallery

在修改完Gallery后,我们就可以来使用它了,还得做一些事情,就是先要扩展Gallery,实现一个WheelView,在这个类里面,我们要去绘制中间选择的矩形、背景图片、上下阴影等。 这个WheelView扩展了Gallery,同时还应该提供设置背景图片,选择矩形的图片和上下阴影的图片等功能。 WheelView的完整实现代码如下:
[java] view plain copy
  1. packagecom.nj1s.lib.widget;
  2. importandroid.content.Context;
  3. importandroid.graphics.Canvas;
  4. importandroid.graphics.Rect;
  5. importandroid.graphics.drawable.Drawable;
  6. importandroid.graphics.drawable.GradientDrawable;
  7. importandroid.graphics.drawable.GradientDrawable.Orientation;
  8. importandroid.util.AttributeSet;
  9. importandroid.view.Gravity;
  10. importandroid.view.View;
  11. importcom.nj1s.lib.R;
  12. publicclassWheelViewextendsTosGallery
  13. {
  14. privateDrawablemSelectorDrawable=null;
  15. privateRectmSelectorBound=newRect();
  16. privateGradientDrawablemTopShadow=null;
  17. privateGradientDrawablemBottomShadow=null;
  18. privatestaticfinalint[]SHADOWS_COLORS=
  19. {
  20. 0xFF111111,
  21. 0x00AAAAAA,
  22. 0x00AAAAAA
  23. };
  24. publicWheelView(Contextcontext)
  25. {
  26. super(context);
  27. initialize(context);
  28. }
  29. publicWheelView(Contextcontext,AttributeSetattrs)
  30. {
  31. super(context,attrs);
  32. initialize(context);
  33. }
  34. publicWheelView(Contextcontext,AttributeSetattrs,intdefStyle)
  35. {
  36. super(context,attrs,defStyle);
  37. initialize(context);
  38. }
  39. privatevoidinitialize(Contextcontext)
  40. {
  41. this.setVerticalScrollBarEnabled(false);
  42. this.setSlotInCenter(true);
  43. this.setOrientation(TosGallery.VERTICAL);
  44. this.setGravity(Gravity.CENTER_HORIZONTAL);
  45. this.setUnselectedAlpha(1.0f);
  46. //ThisleadtheonDraw()willbecalled.
  47. this.setWillNotDraw(false);
  48. //Theselectorrectangledrawable.
  49. this.mSelectorDrawable=
  50. getContext().getResources().getDrawable(R.drawable.wheel_val);
  51. this.mTopShadow=
  52. newGradientDrawable(Orientation.TOP_BOTTOM,SHADOWS_COLORS);
  53. this.mBottomShadow=
  54. newGradientDrawable(Orientation.BOTTOM_TOP,SHADOWS_COLORS);
  55. //Thedefaultbackground.
  56. this.setBackgroundResource(R.drawable.wheel_bg);
  57. }
  58. @Override
  59. protectedvoiddispatchDraw(Canvascanvas)
  60. {
  61. super.dispatchDraw(canvas);
  62. //Afterdrawchild,wedothefollowingthings:
  63. //+1,Drawthecenterrectangle.
  64. //+2,Drawtheshadowsonthetopandbottom.
  65. drawCenterRect(canvas);
  66. drawShadows(canvas);
  67. }
  68. /**
  69. *setOrientation
  70. */
  71. @Override
  72. publicvoidsetOrientation(intorientation)
  73. {
  74. if(TosGallery.HORIZONTAL==orientation)
  75. {
  76. thrownewIllegalArgumentException("TheorientationmustbeVERTICAL");
  77. }
  78. super.setOrientation(orientation);
  79. }
  80. @Override
  81. protectedvoidonLayout(booleanchanged,intl,intt,intr,intb)
  82. {
  83. super.onLayout(changed,l,t,r,b);
  84. intgalleryCenter=getCenterOfGallery();
  85. Viewv=this.getChildAt(0);
  86. intheight=(null!=v)?v.getMeasuredHeight():50;
  87. inttop=galleryCenter-height/2;
  88. intbottom=top+height;
  89. mSelectorBound.set(
  90. getPaddingLeft(),
  91. top,
  92. getWidth()-getPaddingRight(),
  93. bottom);
  94. }
  95. privatevoiddrawCenterRect(Canvascanvas)
  96. {
  97. if(null!=mSelectorDrawable)
  98. {
  99. mSelectorDrawable.setBounds(mSelectorBound);
  100. mSelectorDrawable.draw(canvas);
  101. }
  102. }
  103. privatevoiddrawShadows(Canvascanvas)
  104. {
  105. intheight=(int)(2.0*mSelectorBound.height());
  106. mTopShadow.setBounds(0,0,getWidth(),height);
  107. mTopShadow.draw(canvas);
  108. mBottomShadow.setBounds(0,getHeight()-height,getWidth(),getHeight());
  109. mBottomShadow.draw(canvas);
  110. }
  111. }

上面代码没有什么特别的东西,只是有几点需要注意:
[1] 不要重写onDraw(),为什么呢?因为onDraw()是绘制自己,如果你在onDraw()中来绘制阴影的话,那么最后的效果可能是Child在上面,阴影在下面。因此,我们应该是在绘制完Child之后,再绘制阴影,怎么做呢?请看第二步。
[2] 重写dispatchDraw(),如果对这个方法不明白的话,请自己看文档,这里不解释,总之,这个方法是用来绘制Child的,因此,重写这个方法,先调用super.dispatchDraw()方法,然后再绘制阴影,OK,万事大吉。
[3] 你可以调用#setScrollCycle(boolean)来指定这个WheelView是否可以循环滑动。

5. 如何使用

关于如何使用,其实很简单,就跟使用GridView/ListView一样,通过Adapter来提供View。
[java] view plain copy
  1. //设置listener
  2. mDateWheel.setOnEndFlingListener(mListener);
  3. //设置滑动时的声音
  4. mDateWheel.setSoundEffectsEnabled(true);
  5. //设置adapter
  6. mDateWheel.setAdapter(newWheelTextAdapter(this));
  7. //Adapter的实现
  8. protectedclassWheelTextAdapterextendsBaseAdapter
  9. {
  10. ArrayList<TextInfo>mData=null;
  11. intmWidth=ViewGroup.LayoutParams.MATCH_PARENT;
  12. intmHeight=50;
  13. ContextmContext=null;
  14. publicWheelTextAdapter(Contextcontext)
  15. {
  16. mContext=context;
  17. }
  18. publicvoidsetData(ArrayList<TextInfo>data)
  19. {
  20. mData=data;
  21. this.notifyDataSetChanged();
  22. }
  23. publicvoidsetItemSize(intwidth,intheight)
  24. {
  25. mWidth=width;
  26. mHeight=height;
  27. }
  28. @Override
  29. publicintgetCount()
  30. {
  31. return(null!=mData)?mData.size():0;
  32. }
  33. @Override
  34. publicObjectgetItem(intposition)
  35. {
  36. returnnull;
  37. }
  38. @Override
  39. publiclonggetItemId(intposition)
  40. {
  41. return0;
  42. }
  43. @Override
  44. publicViewgetView(intposition,ViewconvertView,ViewGroupparent)
  45. {
  46. TextViewtextView=null;
  47. if(null==convertView)
  48. {
  49. convertView=newTextView(mContext);
  50. convertView.setLayoutParams(newTosGallery.LayoutParams(mWidth,mHeight));
  51. textView=(TextView)convertView;
  52. textView.setGravity(Gravity.CENTER);
  53. textView.setTextSize(26);
  54. textView.setTextColor(Color.BLACK);
  55. }
  56. if(null==textView)
  57. {
  58. textView=(TextView)convertView;
  59. }
  60. TextInfoinfo=mData.get(position);
  61. textView.setText(info.mText);
  62. textView.setTextColor(info.mColor);
  63. returnconvertView;
  64. }
  65. }
如果大家感兴趣,可以给我发邮件要源代码,我的邮箱是: [email protected]

更多相关文章

  1. Android Studio中隐藏状态栏和标题栏的方法
  2. android 去掉ScrollVIew拉到尽头时再拉的阴影效果
  3. 如何编写高效的android代码(1)
  4. android软键盘弹出,会把原来的界面挤上去的问题 处理方法
  5. Android 上层界面到内核代码的完整的流程分析,以alarm为例子
  6. Flex与Android共享Amfphp SERVICES下自己的代码

随机推荐

  1. [Android]基本概念
  2. Android软键盘-弹起时布局向上拉-多表单
  3. Android 3.0 SDK(R10)及ADT10.0.0正式发布
  4. Android 之 Window、WindowManager 与窗
  5. android 实现表格上下滑,左右滑,并且标题也
  6. 璋锋瓕锛氭墍鏈夋柊娆綜hromebook閮藉皢
  7. Android的背景
  8. 谈谈android中的MVC,MVP和MVVM MVC
  9. Android(安卓)View绘制及实践
  10. Android实现简易版弹钢琴效果