android Picker选择器

本文详细介绍了如何在Android中实现PickerView,包括如何定义一个通用的BaseAdapter,以及使用这个Adapter来创建和定制PickerView的选择器功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

定义了一个通用的base adapter

package com.whuthm.picker;

import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;

/**
 * 基类adapter
 * 
 * @author whuthm
 * @date 2015-6-25
 */
public abstract class BaseAdapter<T> extends android.widget.BaseAdapter {
    protected Context context;
    protected List<T> list = null;
    List<BaseAdapter.ViewHolder<T>> holders = new ArrayList<BaseAdapter.ViewHolder<T>>();
    
    public BaseAdapter(Context context, List<T> list) {
        this.context = context;
        this.list = list;
    }
    
    @Override
    public int getCount() {
        return list != null ? list.size() : 0;
    }
    
    @Override
    public Object getItem(int position) {
        return list != null && position >= 0 && position < list.size() ? list
                .get(position) : null;
    }
    
    @Override
    public long getItemId(int position) {
        return position;
    }
    
    @SuppressWarnings("unchecked")
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder<T> holder;
        if (convertView == null) {
            convertView = createView(context, parent);
            holder = createViewHolder();
            convertView.setTag(holder);
            holder.init(context, convertView);
            holders.add(holder);
        }
        else {
            holder = (ViewHolder<T>) convertView.getTag();
        }
        T data = list.get(position);
        holder.position = position;
        holder.update(context, data);
        return convertView;
    }
    
    public abstract View createView(Context context, ViewGroup parent);
    
    public abstract ViewHolder<T> createViewHolder();
    
    /**
     * 保存当前Item的view
     */
    public static abstract class ViewHolder<T> {
        public int position;
        
        public abstract void init(Context context, View convertView);
        
        public abstract void update(Context context, T data);
    }
}


PickerView的实现

package com.whuthm.picker;

import java.util.ArrayList;
import java.util.List;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.database.DataSetObserver;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.PorterDuff;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.PorterDuffXfermode;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;
import android.widget.ListAdapter;
import android.widget.Scroller;

public class PickerView extends FrameLayout {

	/**
	 * The coefficient by which to adjust (divide) the max fling velocity.
	 */
	private static final int SELECTOR_MAX_FLING_VELOCITY_ADJUSTMENT = 8;

	/**
	 * The the duration for adjusting the selector wheel.
	 */
	private static final int SELECTOR_ADJUSTMENT_DURATION_MILLIS = 500;

	private int mMinScrollY;
	private int mMaxScrollY;

	private int mSelectorIndex = 0;
	private int mSelectorCount = 0;
	private int mFixedWidth = 0;
	private int mFixedHeight = 0;

	private PickerInnerView mInnerView;

	private ListAdapter mAdapter;

	private int mVisibleStartIndex = -1;
	private int mVisibleEndIndex = -1;

	private int mPosition = -1;

	private SparseArray<View> mVisibleViews = new SparseArray<View>();
	private List<View> mScrapViews = new ArrayList<View>();

	private Drawable mSelectorForeground;

	/**
	 * Listener to be notified upon scroll state change.
	 */
	private OnScrollListener mOnScrollListener;

	private Scroller mScroller;

	/**
	 * The Y position of the last down event.
	 */
	private float mLastDownEventY;

	/**
	 * The Y position of the last down or move event.
	 */
	private float mLastDownOrMoveEventY;

	/**
	 * Determines speed during touch scrolling.
	 */
	private VelocityTracker mVelocityTracker;

	/**
	 * @see ViewConfiguration#getScaledTouchSlop()
	 */
	private int mTouchSlop;
	/**
	 * @see ViewConfiguration#getScaledMinimumFlingVelocity()
	 */
	private int mMinimumFlingVelocity;

	/**
	 * @see ViewConfiguration#getScaledMaximumFlingVelocity()
	 */
	private int mMaximumFlingVelocity;

	/**
	 * The current scroll state of the number picker.
	 */
	private int mScrollState = OnScrollListener.SCROLL_STATE_IDLE;

	/**
	 * Interface to listen for the picker scroll state.
	 */
	public interface OnScrollListener {
		/**
		 * The view is not scrolling.
		 */
		public static int SCROLL_STATE_IDLE = 0;

		/**
		 * The user is scrolling using the key, or adjust to right position
		 */
		public static int SCROLL_STATE_SCROLL = 2;

		/**
		 * The user is scrolling using touch, and his finger is still on the
		 * screen.
		 */
		public static int SCROLL_STATE_TOUCH_SCROLL = 2;

		/**
		 * The user had previously been scrolling using touch and performed a
		 * fling.
		 */
		public static int SCROLL_STATE_FLING_SCROLL = 3;

		public void onScrollStateChange(PickerView view, int scrollState);
	}

	public interface OnPickerSelectedListener {
		public void onPickerSelected(int position);
	}

	private OnPickerSelectedListener mPickerSelectedListener;

	private Shader mShader;
	private Paint mPaint;
	private Matrix mMatrix;

	public PickerView(Context context) {
		this(context, null);
	}

	public PickerView(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public PickerView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);

		setWillNotDraw(false);

		final Resources res = getResources();

		final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PickerView);
		mFixedWidth = a.getDimensionPixelSize(R.styleable.PickerView_fixedWidth,
				res.getDimensionPixelSize(R.dimen.picker_item_default_fixed_width));
		mFixedHeight = a.getDimensionPixelSize(R.styleable.PickerView_fixedHeight,
				res.getDimensionPixelSize(R.dimen.picker_item_default_fixed_height));
		mSelectorForeground = a.getDrawable(R.styleable.PickerView_selectorForeground);
		if (mSelectorForeground == null) {
			mSelectorForeground = res.getDrawable(R.drawable.picker_selector_foreground);
		}
		mSelectorCount = a.getInteger(R.styleable.PickerView_selectorCount, 0);
		mSelectorCount = Math.max(0, mSelectorCount);
		mSelectorIndex = a.getInteger(R.styleable.PickerView_selectorIndex, 0);
		mSelectorIndex = Math.min(mSelectorCount - 1, mSelectorIndex);
		mSelectorIndex = Math.max(0, mSelectorIndex);
		a.recycle();

		// initialize constants
		ViewConfiguration configuration = ViewConfiguration.get(context);
		mTouchSlop = configuration.getScaledTouchSlop();
		mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity();
		mMaximumFlingVelocity = configuration.getScaledMaximumFlingVelocity() / SELECTOR_MAX_FLING_VELOCITY_ADJUSTMENT;

		mScroller = new Scroller(getContext(), new DecelerateInterpolator(2.5f));

		mMatrix = new Matrix();
		mPaint = new Paint();
		mShader = new LinearGradient(0, 0, 0, 1, 0xbb000000, 0x66000000, Shader.TileMode.CLAMP);
		mPaint.setShader(mShader);
		mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));

	}

	private DataSetObserver mDataSetObserver = new DataSetObserver() {

		@Override
		public void onChanged() {
			notifyDataChange();
		}
	};

	public void setOnScrollListener(OnScrollListener onScrollListener) {
		mOnScrollListener = onScrollListener;
	}

	public void setOnPickerSelectedListener(OnPickerSelectedListener l) {
		mPickerSelectedListener = l;
	}

	public void setAdapter(ListAdapter adapter) {
		mAdapter = adapter;
		adapter.registerDataSetObserver(mDataSetObserver);

		mVisibleStartIndex = -1;
		mVisibleEndIndex = -1;
		mVisibleViews.clear();
		mScrapViews.clear();
		if (mInnerView != null) {
			mInnerView.removeAllViews();
		}
		removeAllViewsInLayout();

		mInnerView = new PickerInnerView(getContext());
		LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
		lp.gravity = Gravity.CENTER_HORIZONTAL;
		addView(mInnerView, lp);

		final int count = adapter.getCount();
		mMinScrollY = 0;
		mMaxScrollY = (count - 1) * mFixedHeight;
		mMaxScrollY = Math.max(0, mMaxScrollY);

		scrollTo(0, mPosition * mFixedHeight);
		handleScroll(0, getScrollY(), 0, 0);
		requestLayout();
	}

	public void notifyDataChange() {
		if (mFixedHeight <= 0 || mInnerView == null) {
			return;
		}
		int oldCount = mMaxScrollY / mFixedHeight;
		final int count = mAdapter.getCount();

		if (oldCount > count) {
			for (int i = count; i < oldCount; i++) {
				View view = mVisibleViews.get(i);
				if (view != null) {
					mVisibleViews.remove(i);
					mInnerView.removeView(view);
					mInnerView.removeDetachedView(view);
					mScrapViews.add(view);
				}
			}
		}

		mMinScrollY = 0;
		mMaxScrollY = (count - 1) * mFixedHeight;
		mMaxScrollY = Math.max(0, mMaxScrollY);

		scrollTo(0, mPosition * mFixedHeight);
		handleScroll(0, getScrollY(), 0, 0);
		mInnerView.requestLayout();
	}

	public int getPickerCount() {
		return mAdapter != null ? mAdapter.getCount() : 0;
	}

	public void setCurrentItem(int position) {
		if (isDataInvalid()) {
			return;
		}
		int count = mAdapter.getCount();
		if (position <= 0) {
			position = 0;
		} else if (position >= count) {
			position = count - 1;
		}
		scrollTo(0, position * mFixedHeight);
		mInnerView.requestLayout();
	}

	@Override
	protected void dispatchDraw(Canvas canvas) {
		if (isDataInvalid()) {
			super.dispatchDraw(canvas);
		} else {
			final int width = getWidth();
			final int scrollX = getScrollX();
			final int scrollY = getScrollY();
			int saveCount = canvas.getSaveCount();

			int length = Math.max(mSelectorIndex * mFixedHeight, (mSelectorCount - 1 - mSelectorIndex) * mFixedHeight);
			int left = scrollX + getPaddingLeft();
			int top = scrollY + mSelectorIndex * mFixedHeight - length - getPaddingTop();
			int right = left + width - getPaddingLeft() - getPaddingRight();
			int bottom = top + 2 * length + mFixedHeight;

			boolean drawTop = mSelectorIndex > 0;
			boolean drawBottom = mSelectorIndex < mSelectorCount - 1;

			final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
			if (drawTop) {
				canvas.saveLayer(left, top, right, top + length, null, flags);
			}

			if (drawBottom) {
				canvas.saveLayer(left, bottom - length, right, bottom, null, flags);
			}

			super.dispatchDraw(canvas);

			if (drawTop) {
				mMatrix.setScale(1, length);
				mMatrix.postTranslate(left, top);
				mShader.setLocalMatrix(mMatrix);
				mPaint.setShader(mShader);
				canvas.drawRect(left, top, right, top + length, mPaint);
			}

			if (drawBottom) {
				mMatrix.setScale(1, length);
				mMatrix.postRotate(180);
				mMatrix.postTranslate(left, bottom);
				mShader.setLocalMatrix(mMatrix);
				mPaint.setShader(mShader);
				canvas.drawRect(left, bottom - length, right, bottom, mPaint);
			}
			canvas.restoreToCount(saveCount);

			if (mSelectorForeground != null) {
				canvas.save();
				mSelectorForeground.setBounds(scrollX, scrollY + mSelectorIndex * mFixedHeight, width, scrollY
						+ mSelectorIndex * mFixedHeight + mFixedHeight);
				mSelectorForeground.draw(canvas);
				canvas.restore();
			}
		}
	}

	@Override
	protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) {
		ViewGroup.LayoutParams lp = child.getLayoutParams();

		int childWidthMeasureSpec;
		int childHeightMeasureSpec;

		childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, getPaddingLeft() + getPaddingRight(),
				lp.width);
		childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);

		child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
	}

	@Override
	protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed,
			int parentHeightMeasureSpec, int heightUsed) {
		final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

		final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, getPaddingLeft()
				+ getPaddingRight() + lp.leftMargin + lp.rightMargin + widthUsed, lp.width);
		final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.topMargin + lp.bottomMargin,
				MeasureSpec.UNSPECIFIED);

		child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
		int newWidthMeasureSpec = widthMeasureSpec;
		int newHeightMeasureSpec = MeasureSpec.makeMeasureSpec(mFixedHeight * mSelectorCount + getPaddingTop()
				+ getPaddingBottom(), MeasureSpec.EXACTLY);
		if (widthSpecMode != MeasureSpec.EXACTLY) {
			newWidthMeasureSpec = MeasureSpec.makeMeasureSpec(mFixedWidth + getPaddingLeft() + getPaddingRight(),
					MeasureSpec.EXACTLY);
		}
		super.onMeasure(newWidthMeasureSpec, newHeightMeasureSpec);
	}

	private boolean isDataInvalid() {
		return mFixedWidth <= 0 || mFixedHeight <= 0 || mAdapter == null || mAdapter.getCount() <= 0;
	}

	@Override
	public boolean onInterceptTouchEvent(MotionEvent event) {
		if (!isEnabled() || isDataInvalid()) {
			return false;
		}
		final int action = event.getActionMasked();
		switch (action) {
		case MotionEvent.ACTION_DOWN:
			mLastDownOrMoveEventY = mLastDownEventY = event.getY();
			if (!mScroller.isFinished()) {
				mScroller.forceFinished(true);
			}
			onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
			return true;
		}
		return false;
	}

	@SuppressLint("ClickableViewAccessibility")
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		if (!isEnabled() || isDataInvalid()) {
			return false;
		}
		if (mVelocityTracker == null) {
			mVelocityTracker = VelocityTracker.obtain();
		}
		mVelocityTracker.addMovement(event);
		int action = event.getActionMasked();
		switch (action) {
		case MotionEvent.ACTION_MOVE:
			float currentMoveY = event.getY();
			if (mScrollState != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
				int deltaDownY = (int) Math.abs(currentMoveY - mLastDownEventY);
				if (deltaDownY > mTouchSlop) {
					onScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
				}
			} else {
				int deltaMoveY = (int) ((currentMoveY - mLastDownOrMoveEventY));
				scrollBy(0, -deltaMoveY);
				invalidate();
			}
			mLastDownOrMoveEventY = currentMoveY;
			break;
		case MotionEvent.ACTION_UP:
			if (mScrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
				VelocityTracker velocityTracker = mVelocityTracker;
				velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
				int initialVelocity = (int) velocityTracker.getYVelocity();
				// 当速度不够(小于mMinimumFlingVelocity)时,调整至适当的Value
				// 当速度足够时,fling
				if (Math.abs(initialVelocity) >= mMinimumFlingVelocity) {
					fling(initialVelocity);
				} else {
					ensureScrollWheelAdjusted();
				}
			} else {
				ensureScrollWheelAdjusted();
			}
			mVelocityTracker.recycle();
			mVelocityTracker = null;
			break;
		}
		return true;
	}

	// **************************************************************************************
	/**
	 * 三种非Touch 滚动 1 滑动后有扔的动作(fling) 2
	 * 滑动后没有扔的动作,自动调整到最近的位置(adjustToClosestPosition) 3
	 * 点击选中某一位置:上下按键,已经Touch选中某一项(moveToSelectedPosition)
	 */

	/**
	 * Flings the selector with the given <code>velocityY</code>.
	 */
	private void fling(int velocityY) {
		if (!isDataInvalid()) {
			final int curScrollY = getScrollY();
			final int fixedHeight = mFixedHeight;
			if (velocityY > 0) {
				mScroller.fling(0, 0, 0, velocityY, 0, 0, 0, Integer.MAX_VALUE);
			} else {
				mScroller.fling(0, Integer.MAX_VALUE, 0, velocityY, 0, 0, 0, Integer.MAX_VALUE);
			}
			int duration = mScroller.getDuration();
			int distance = mScroller.getStartY() - mScroller.getFinalY();
			int finalY = distance + curScrollY;
			int halfFixedHeight = fixedHeight / 2;
			int remainder = finalY % fixedHeight;
			if (remainder == 0) {
				// do nothing
			} else if (remainder > halfFixedHeight) {
				finalY += fixedHeight - remainder;
			} else if (-remainder > halfFixedHeight) {
				finalY += -remainder - fixedHeight;
			} else {
				finalY += -remainder;
			}

			finalY = Math.min(mMaxScrollY, Math.max(mMinScrollY, finalY));
			mScroller.startScroll(0, getScrollY(), 0, finalY - curScrollY, duration);

			if (mScroller.getStartY() != mScroller.getFinalY()) {
				invalidate();
				onScrollStateChange(OnScrollListener.SCROLL_STATE_FLING_SCROLL);
			} else {
				mScroller.abortAnimation();
				ensureScrollWheelAdjusted();
			}
		}
	}

	private void ensureScrollWheelAdjusted() {
		if (!isDataInvalid()) {
			final int curScrollY = getScrollY();
			final int fixedHeight = mFixedHeight;
			int halfFixedHeight = fixedHeight / 2;
			int remainder = curScrollY % mFixedHeight;
			int deltaY;
			if (remainder == 0) {
				// do nothing
				deltaY = 0;
			} else if (remainder > halfFixedHeight) {
				deltaY = fixedHeight - remainder;
			} else if (-remainder > halfFixedHeight) {
				deltaY = -remainder - fixedHeight;
			} else {
				deltaY = -remainder;
			}
			if (deltaY != 0) {
				mScroller.startScroll(0, curScrollY, 0, deltaY, SELECTOR_ADJUSTMENT_DURATION_MILLIS);
				onScrollStateChange(OnScrollListener.SCROLL_STATE_SCROLL);
				invalidate();
			} else {
				onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
			}
		}
	}

	private void onPickerSelectedChange(int position) {
		if (mPosition == position) {
			return;
		}
		mPosition = position;
		if (mPickerSelectedListener != null) {
			mPickerSelectedListener.onPickerSelected(position);
		}
	}

	/**
	 * Handles transition to a given <code>scrollState</code>
	 */
	private void onScrollStateChange(int scrollState) {
		if (mScrollState == scrollState) {
			return;
		}
		mScrollState = scrollState;
		if (mOnScrollListener != null) {
			mOnScrollListener.onScrollStateChange(this, scrollState);
		}
	}

	@Override
	protected void onScrollChanged(int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
		super.onScrollChanged(scrollX, scrollY, oldScrollX, oldScrollY);
		handleScroll(scrollX, scrollY, oldScrollX, oldScrollY);
	}

	@Override
	public void scrollTo(int x, int y) {
		super.scrollTo(x, Math.min(mMaxScrollY, Math.max(mMinScrollY, y)));
		if (!isDataInvalid()) {
			onPickerSelectedChange(getScrollY() / mFixedHeight);
		}
	}

	@Override
	public void computeScroll() {
		if (mScroller.computeScrollOffset()) {
			int oldX = getScrollX();
			int oldY = getScrollY();
			int x = mScroller.getCurrX();
			int y = mScroller.getCurrY();

			if (oldX != x || oldY != y) {
				scrollTo(x, y);
			}
			postInvalidate();
		} else {
			onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
		}
	}

	private void handleScroll(int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
		if (isDataInvalid()) {
			return;
		}
		final int count = mAdapter.getCount();
		int multipleCount = scrollY / mFixedHeight;
		int remainderCount = scrollY % mFixedHeight;
		int newStartIndex = multipleCount - mSelectorIndex;
		int newEndIndex = remainderCount > 0 ? newStartIndex + mSelectorCount : newStartIndex + mSelectorCount - 1;
		newStartIndex = Math.max(newStartIndex, 0);
		newEndIndex = Math.min(newEndIndex, count - 1);
		if (mVisibleStartIndex != newStartIndex || mVisibleEndIndex != newEndIndex) {
			mVisibleStartIndex = newStartIndex;
			mVisibleEndIndex = newEndIndex;
			mInnerView.requestLayout();
		}
	}

	@Override
	protected void onDetachedFromWindow() {
		super.onDetachedFromWindow();
		mPickerSelectedListener = null;
		if (mAdapter != null && mDataSetObserver != null) {
			mAdapter.unregisterDataSetObserver(mDataSetObserver);
			mDataSetObserver = null;
		}
		mAdapter = null;
		mVisibleStartIndex = -1;
		mVisibleEndIndex = -1;
		mVisibleViews.clear();
		mScrapViews.clear();
	}

	private class PickerInnerView extends ViewGroup {

		public PickerInnerView(Context context) {
			this(context, null);
		}

		public PickerInnerView(Context context, AttributeSet attrs) {
			this(context, attrs, 0);
		}

		public PickerInnerView(Context context, AttributeSet attrs, int defStyle) {
			super(context, attrs, defStyle);
		}

		@Override
		protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
			int newWidth = 0;
			int newHeight = 0;
			int count = mAdapter.getCount();
			if (mFixedWidth > 0 && mFixedHeight > 0 && count > 0 && mSelectorCount > 0) {
				newWidth = mFixedWidth;
				newHeight = mFixedHeight * (count + mSelectorCount - 1);
			}
			setMeasuredDimension(newWidth, newHeight);
		}

		@Override
		protected void onLayout(boolean changed, int l, int t, int r, int b) {
			int count = mAdapter.getCount();
			int left = 0;
			int top = mSelectorIndex * mFixedHeight;
			for (int i = 0; i < count; i++) {
				View view = mVisibleViews.get(i);
				if (i >= mVisibleStartIndex && i <= mVisibleEndIndex) {
					if (view == null) {
						int size = mScrapViews.size();
						if (size > 0) {
							view = mScrapViews.remove(0);
						}
						view = mAdapter.getView(i, view, this);
						addView(view);
						view.measure(MeasureSpec.makeMeasureSpec(mFixedWidth, MeasureSpec.EXACTLY),
								MeasureSpec.makeMeasureSpec(mFixedHeight, MeasureSpec.EXACTLY));
						mVisibleViews.put(i, view);
					}
					view.layout(left, top, left + mFixedWidth, top + mFixedHeight);
				} else {
					if (view != null) {
						mVisibleViews.remove(i);
						removeView(view);
						removeDetachedView(view, false);
						mScrapViews.add(view);
					}
				}
				top += mFixedHeight;
			}

		}

		void removeDetachedView(View view) {
			removeDetachedView(view, false);
		}
	}

}
DatePicker实现日期选择器

package com.whuthm.picker;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
import com.whuthm.picker.PickerView.OnPickerSelectedListener;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;

public class DatePicker extends LinearLayout {

	enum DateType {
		YEAR, MONTH, DAY
	}

	private PickerView mYearPicker;
	private PickerView mMonthPicker;
	private PickerView mDayPicker;

	private int mYear;
	private int mMonth;
	private int mDay;

	private int mMinYear;
	private int mMaxYear;

	private List<Integer> mDayDats = new ArrayList<Integer>();

	public DatePicker(Context context) {
		this(context, null);
	}

	public DatePicker(Context context, AttributeSet attrs) {
		super(context, attrs);
		setOrientation(HORIZONTAL);
		final LayoutInflater inflater = LayoutInflater.from(context);
		LayoutParams lp;

		mYearPicker = (PickerView) inflater.inflate(R.layout.view_date_picker, this, false);
		lp = new LayoutParams(0, LayoutParams.WRAP_CONTENT, 1);
		addView(mYearPicker, lp);
		mYearPicker.setOnPickerSelectedListener(mYearSelectedListener);

		mMonthPicker = (PickerView) inflater.inflate(R.layout.view_date_picker, this, false);
		lp = new LayoutParams(0, LayoutParams.WRAP_CONTENT, 1);
		addView(mMonthPicker, lp);
		mMonthPicker.setOnPickerSelectedListener(mMonthSelectedListener);

		mDayPicker = (PickerView) inflater.inflate(R.layout.view_date_picker, this, false);
		lp = new LayoutParams(0, LayoutParams.WRAP_CONTENT, 1);
		addView(mDayPicker, lp);
		mDayPicker.setOnPickerSelectedListener(mDaySelectedListener);
	}

	public void setDate(int year, int minYear, int maxYear) {
		setDate(year, 1, 1, minYear, maxYear);
	}

	public void setDate(int year, int month, int day, int minYear, int maxYear) {
		if (maxYear < minYear) {
			throw new RuntimeException("maxYear < minYear");
		}
		mMaxYear = maxYear;
		mMinYear = minYear;
		if (year < minYear) {
			mYear = minYear;
		} else if (year > maxYear) {
			mYear = maxYear;
		} else {
			mYear = year;
		}
		mMonth = month;
		mDay = day;
		
		mDayPicker.setAdapter(createDayAdapter(year, month));
		mDayPicker.setCurrentItem(day - 1);
		
		mMonthPicker.setAdapter(createMonthAdapter());
		mMonthPicker.setCurrentItem(month - 1);

		mYearPicker.setAdapter(createYearAdapter());
		mYearPicker.setCurrentItem(year - mMinYear);

	}

	public int getYear() {
		return mYear;
	}

	public int getMonth() {
		return mMonth;
	}

	public int getDay() {
		return mDay;
	}

	private OnPickerSelectedListener mYearSelectedListener = new OnPickerSelectedListener() {

		@Override
		public void onPickerSelected(int position) {
			mYear = position + mMinYear;
			int monthDays = getMonthDays(mYear, mMonth);
			Log.e("whuthm", "mYearSelectedListener " + mYear + "  monthDays = " + monthDays);
			if (monthDays != mDayPicker.getPickerCount()) {
				setupDayDatas(monthDays);
				mDayPicker.notifyDataChange();
				mDayPicker.setCurrentItem(mDay - 1);
			}
		}
	};

	private OnPickerSelectedListener mMonthSelectedListener = new OnPickerSelectedListener() {

		@Override
		public void onPickerSelected(int position) {
			mMonth = position + 1;
			int monthDays = getMonthDays(mYear, mMonth);
			Log.e("whuthm", "mMonthSelectedListener " + mMonth + "  monthDays = " + monthDays);
			if (monthDays != mDayPicker.getPickerCount()) {
				setupDayDatas(monthDays);
				mDayPicker.notifyDataChange();
				mDayPicker.setCurrentItem(mDay - 1);
			}
		}
	};

	private OnPickerSelectedListener mDaySelectedListener = new OnPickerSelectedListener() {

		@Override
		public void onPickerSelected(int position) {
			mDay = position + 1;
			Log.e("whuthm", "mDaySelectedListener " + mDay);
		}
	};

	DateAdapter createYearAdapter() {
		List<Integer> datas = new ArrayList<Integer>();
		for (int i = mMinYear; i <= mMaxYear; i++) {
			datas.add(i);
		}
		return new DateAdapter(getContext(), datas, DateType.YEAR);
	}

	DateAdapter createMonthAdapter() {
		List<Integer> datas = new ArrayList<Integer>();
		for (int i = 1; i <= 12; i++) {
			datas.add(i);
		}
		return new DateAdapter(getContext(), datas, DateType.MONTH);
	}

	DateAdapter createDayAdapter(int year, int month) {
		return createDayAdapter(getMonthDays(year, month));
	}

	DateAdapter createDayAdapter(int monthDays) {
		mDayDats.clear();
		for (int i = 1; i <= monthDays; i++) {
			mDayDats.add(i);
		}
		return new DateAdapter(getContext(), mDayDats, DateType.DAY);
	}

	private void setupDayDatas(int monthDays) {
		mDayDats.clear();
		for (int i = 1; i <= monthDays; i++) {
			mDayDats.add(i);
		}
	}

	static class DateAdapter extends BaseAdapter<Integer> {
		private DateType mType;

		public DateAdapter(Context context, List<Integer> list, DateType type) {
			super(context, list);
			mType = type;
		}

		@Override
		public View createView(Context context, ViewGroup parent) {
			return LayoutInflater.from(context).inflate(R.layout.item_date_picker, parent, false);
		}

		@Override
		public com.whuthm.picker.BaseAdapter.ViewHolder<Integer> createViewHolder() {
			return new DateHolder();
		}

		class DateHolder extends com.whuthm.picker.BaseAdapter.ViewHolder<Integer> {

			TextView textView;

			@Override
			public void init(Context context, View convertView) {
				textView = (TextView) convertView;
			}

			@Override
			public void update(Context context, Integer data) {
				textView.setText(getDateText(data, mType));
			}
		}

		public static String getDateText(int data, DateType type) {
			StringBuilder sb = new StringBuilder(Integer.toString(data));
			if (type != null) {
				sb.append(" ");
				switch (type) {
				case YEAR:
					sb.append("年");
					break;
				case MONTH:
					sb.append("月");
					break;
				case DAY:
					sb.append("日");
					break;
				default:
					break;
				}
			}
			return sb.toString();
		}

	}

	public static int getMonthDays(int year, int month) {
		Calendar c = Calendar.getInstance(Locale.getDefault());
		c.clear();
		c.set(Calendar.YEAR, year);
		c.set(Calendar.MONTH, month - 1);
		c.set(Calendar.DAY_OF_MONTH, 1);
		return c.getActualMaximum(Calendar.DAY_OF_MONTH);
	}

}


自定义的属性

<declare-styleable name="PickerView">
        <attr name="selectorIndex" format="integer" />
        <attr name="selectorCount" format="integer" />
        <attr name="selectorForeground" format="reference" />
        <attr name="fixedWidth" format="dimension|reference" />
        <attr name="fixedHeight" format="dimension|reference" />
    </declare-styleable>





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值