我们都知道,在iOS里面有一种控件------滚筒控件(Wheel View),这通常用于设置时间/日期,非常方便,但Android SDK并没有提供类似的控件。这里介绍一下如何Android实现WheelView。
源码下载请点我
先来看一看iOS中的WheelView的效果图:
这个效果不错吧,我们应该如何实现呢?
那在Android如果也要实现这样一个效果,应该怎么做呢?
我们从网上找到了一个开源的代码,它也实现了这样的效果,而且效果也不错,大家可以用SVN来checkout:
http://android-wheel.googlecode.com/svn/trunk
它这个Demo最本质是自己写布局,好像是利用一个LinearLayout来布局child,然后调用LinearLayout.draw(canvas)方法,把child绘制在指定的canvas上面。它同时还提供了类似AdapterView的访问方式,用户可以设置Adapter来提供数据。我在这里主要不是讲解这个Demo的结构,如果大家感兴趣,可以自己下载代码研究。
由于我之前修改过Gallery的源代码,可以使其循环滚动,并且第一个child可以排列在最左端,所以,我在想,如果我能把Gallery修改成竖的(垂直排列),那这个不就是OK了吗?基于这样的想法,我就准备修改代码了。
我们这里需要把Gallery的源码复制到我们的工程中,然后修改,保证能编译通过。
与Gallery相关的的几个文件如下所示,它们都是放在widget文件夹和res/value文件夹下面。
修改的过程比较麻烦,我这里不详细说明(要细说的话,内容太多了),在修改之后,我们的Gallery提供了一个方法:setOrientation(int),你可以让这个Gallery水平滑动,也可以垂直滑动。
我们还应该提供以下几个核心方法:
package com.nj1s.lib.widget; import android.content.Context; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.GradientDrawable.Orientation; import android.util.AttributeSet; import android.view.Gravity; import android.view.View; import com.nj1s.lib.R; public class WheelView extends TosGallery { private Drawable mSelectorDrawable = null; private Rect mSelectorBound = new Rect(); private GradientDrawable mTopShadow = null; private GradientDrawable mBottomShadow = null; private static final int[] SHADOWS_COLORS = { 0xFF111111, 0x00AAAAAA, 0x00AAAAAA }; public WheelView(Context context) { super(context); initialize(context); } public WheelView(Context context, AttributeSet attrs) { super(context, attrs); initialize(context); } public WheelView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initialize(context); } private void initialize(Context context) { this.setVerticalScrollBarEnabled(false); this.setSlotInCenter(true); this.setOrientation(TosGallery.VERTICAL); this.setGravity(Gravity.CENTER_HORIZONTAL); this.setUnselectedAlpha(1.0f); // This lead the onDraw() will be called. this.setWillNotDraw(false); // The selector rectangle drawable. this.mSelectorDrawable = getContext().getResources().getDrawable(R.drawable.wheel_val); this.mTopShadow = new GradientDrawable(Orientation.TOP_BOTTOM, SHADOWS_COLORS); this.mBottomShadow = new GradientDrawable(Orientation.BOTTOM_TOP, SHADOWS_COLORS); // The default background. this.setBackgroundResource(R.drawable.wheel_bg); } @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); // After draw child, we do the following things: // +1, Draw the center rectangle. // +2, Draw the shadows on the top and bottom. drawCenterRect(canvas); drawShadows(canvas); } /** * setOrientation */ @Override public void setOrientation(int orientation) { if (TosGallery.HORIZONTAL == orientation) { throw new IllegalArgumentException("The orientation must be VERTICAL"); } super.setOrientation(orientation); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); int galleryCenter = getCenterOfGallery(); View v = this.getChildAt(0); int height = (null != v) ? v.getMeasuredHeight() : 50; int top = galleryCenter - height / 2; int bottom = top + height; mSelectorBound.set( getPaddingLeft(), top, getWidth() - getPaddingRight(), bottom); } private void drawCenterRect(Canvas canvas) { if (null != mSelectorDrawable) { mSelectorDrawable.setBounds(mSelectorBound); mSelectorDrawable.draw(canvas); } } private void drawShadows(Canvas canvas) { int height = (int)(2.0 * mSelectorBound.height()); mTopShadow.setBounds(0, 0, getWidth(), height); mTopShadow.draw(canvas); mBottomShadow.setBounds(0, getHeight() - height, getWidth(), getHeight()); mBottomShadow.draw(canvas); } }
// 设置listener mDateWheel.setOnEndFlingListener(mListener); // 设置滑动时的声音 mDateWheel.setSoundEffectsEnabled(true); // 设置adapter mDateWheel.setAdapter(new WheelTextAdapter(this)); // Adapter的实现 protected class WheelTextAdapter extends BaseAdapter { ArrayList<TextInfo> mData = null; int mWidth = ViewGroup.LayoutParams.MATCH_PARENT; int mHeight = 50; Context mContext = null; public WheelTextAdapter(Context context) { mContext = context; } public void setData(ArrayList<TextInfo> data) { mData = data; this.notifyDataSetChanged(); } public void setItemSize(int width, int height) { mWidth = width; mHeight = height; } @Override public int getCount() { return (null != mData) ? mData.size() : 0; } @Override public Object getItem(int position) { return null; } @Override public long getItemId(int position) { return 0; } @Override public View getView(int position, View convertView, ViewGroup parent) { TextView textView = null; if (null == convertView) { convertView = new TextView(mContext); convertView.setLayoutParams(new TosGallery.LayoutParams(mWidth, mHeight)); textView = (TextView)convertView; textView.setGravity(Gravity.CENTER); textView.setTextSize(26); textView.setTextColor(Color.BLACK); } if (null == textView) { textView = (TextView)convertView; } TextInfo info = mData.get(position); textView.setText(info.mText); textView.setTextColor(info.mColor); return convertView; } }