Android WheelView横向选择器

最近项目让做了一个注册流程是选择年龄的,规定是手动横幅型的选择器,顺便让他给了个模仿对象的apk

效果如下:

Android WheelView横向选择器_第1张图片

怎么办呢,最后反编译了那个apk,从里面找到了这个控件发现他也是引用的一个别人的依赖话不多说依赖先放这里:

dependencies {
  compile 'com.lantouzi.wheelview:library:1.1.2'
}

然后这个大佬的 github 我也放这里,能用到这个控件的朋友可以看一下:

说一下如果大家不想用依赖的话,也是可以的

接下来是我将依赖中的代码提取出来的代码:

  • WheelView.java
package com.online.myceshidemo.common;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextPaint;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.SoundEffectConstants;
import android.view.View;
import android.widget.OverScroller;

import androidx.core.view.GestureDetectorCompat;
import androidx.core.view.ViewCompat;

import com.online.myceshidemo.R;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by kyle on 15/11/9.
 */
public class WheelView extends View implements GestureDetector.OnGestureListener {
	public static final float DEFAULT_INTERVAL_FACTOR = 1.2f;
	public static final float DEFAULT_MARK_RATIO = 0.7f;

	private Paint mMarkPaint;
	private TextPaint mMarkTextPaint;
	private int mCenterIndex = -1;

	private int mHighlightColor, mMarkTextColor;
	private int mMarkColor, mFadeMarkColor;

	private int mHeight;
	private List mItems;
	private String mAdditionCenterMark;
	private OnWheelItemSelectedListener mOnWheelItemSelectedListener;
	private float mIntervalFactor = DEFAULT_INTERVAL_FACTOR;
	private float mMarkRatio = DEFAULT_MARK_RATIO;

	private int mMarkCount;
	private float mAdditionCenterMarkWidth;
	private Path mCenterIndicatorPath = new Path();
	private float mCursorSize;
	private int mViewScopeSize;

	// scroll control args ---- start
	private OverScroller mScroller;
	private float mMaxOverScrollDistance;
	private RectF mContentRectF;
	private boolean mFling = false;
	private float mCenterTextSize, mNormalTextSize;
	private float mTopSpace, mBottomSpace;
	private float mIntervalDis;
	private float mCenterMarkWidth, mMarkWidth;
	private GestureDetectorCompat mGestureDetectorCompat;
	// scroll control args ---- end

	private int mLastSelectedIndex = -1;
	private int mMinSelectableIndex = Integer.MIN_VALUE;
	private int mMaxSelectableIndex = Integer.MAX_VALUE;

	public WheelView(Context context) {
		super(context);
		init(null);
	}

	public WheelView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init(attrs);
	}

	public WheelView(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		init(attrs);
	}

	protected void init(AttributeSet attrs) {
		float density = getResources().getDisplayMetrics().density;
		mCenterMarkWidth = (int) (density * 1.5f + 0.5f);
		mMarkWidth = density;

		mHighlightColor = 0xFFF74C39;
		mMarkTextColor = 0xFF666666;
		mMarkColor = 0xFFEEEEEE;
		mCursorSize = density * 18;
		mCenterTextSize = density * 22;
		mNormalTextSize = density * 18;
		mBottomSpace = density * 6;

		TypedArray ta = attrs == null ? null : getContext().obtainStyledAttributes(attrs, R.styleable.lwvWheelView);
		if (ta != null) {
			mHighlightColor = ta.getColor(R.styleable.lwvWheelView_lwvHighlightColor, mHighlightColor);
			mMarkTextColor = ta.getColor(R.styleable.lwvWheelView_lwvMarkTextColor, mMarkTextColor);
			mMarkColor = ta.getColor(R.styleable.lwvWheelView_lwvMarkColor, mMarkColor);
			mIntervalFactor = ta.getFloat(R.styleable.lwvWheelView_lwvIntervalFactor, mIntervalFactor);
			mMarkRatio = ta.getFloat(R.styleable.lwvWheelView_lwvMarkRatio, mMarkRatio);
			mAdditionCenterMark = ta.getString(R.styleable.lwvWheelView_lwvAdditionalCenterMark);
			mCenterTextSize = ta.getDimension(R.styleable.lwvWheelView_lwvCenterMarkTextSize, mCenterTextSize);
			mNormalTextSize = ta.getDimension(R.styleable.lwvWheelView_lwvMarkTextSize, mNormalTextSize);
			mCursorSize = ta.getDimension(R.styleable.lwvWheelView_lwvCursorSize, mCursorSize);
		}
		mFadeMarkColor = mHighlightColor & 0xAAFFFFFF;
		mIntervalFactor = Math.max(1, mIntervalFactor);
		mMarkRatio = Math.min(1, mMarkRatio);
		mTopSpace = mCursorSize + density * 2;

		mMarkPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
		mMarkTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
		mMarkTextPaint.setTextAlign(Paint.Align.CENTER);
		mMarkTextPaint.setColor(mHighlightColor);

		mMarkPaint.setColor(mMarkColor);
		mMarkPaint.setStrokeWidth(mCenterMarkWidth);

		mMarkTextPaint.setTextSize(mCenterTextSize);
		calcIntervalDis();

		mScroller = new OverScroller(getContext());
		mContentRectF = new RectF();

		mGestureDetectorCompat = new GestureDetectorCompat(getContext(), this);

		selectIndex(0);
	}

	/**
	 * calculate interval distance between items
	 */
	private void calcIntervalDis() {
		if (mMarkTextPaint == null) {
			return;
		}
		String defaultText = "888888";
		Rect temp = new Rect();
		int max = 0;
		if (mItems != null && mItems.size() > 0) {
			for (String i : mItems) {
				mMarkTextPaint.getTextBounds(i, 0, i.length(), temp);
				if (temp.width() > max) {
					max = temp.width();
				}
			}
		} else {
			mMarkTextPaint.getTextBounds(defaultText, 0, defaultText.length(), temp);
			max = temp.width();
		}

		if (!TextUtils.isEmpty(mAdditionCenterMark)) {
			mMarkTextPaint.setTextSize(mNormalTextSize);
			mMarkTextPaint.getTextBounds(mAdditionCenterMark, 0, mAdditionCenterMark.length(), temp);
			mAdditionCenterMarkWidth = temp.width();
			max += temp.width();
		}

		mIntervalDis = max * mIntervalFactor;
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
	}

	private int measureWidth(int widthMeasureSpec) {
		int measureMode = MeasureSpec.getMode(widthMeasureSpec);
		int measureSize = MeasureSpec.getSize(widthMeasureSpec);
		int result = getSuggestedMinimumWidth();
		switch (measureMode) {
			case MeasureSpec.AT_MOST:
			case MeasureSpec.EXACTLY:
				result = measureSize;
				break;
			default:
				break;
		}
		return result;
	}

	private int measureHeight(int heightMeasure) {
		int measureMode = MeasureSpec.getMode(heightMeasure);
		int measureSize = MeasureSpec.getSize(heightMeasure);
		int result = (int) (mBottomSpace + mTopSpace * 2 + mCenterTextSize);
		switch (measureMode) {
			case MeasureSpec.EXACTLY:
				result = Math.max(result, measureSize);
				break;
			case MeasureSpec.AT_MOST:
				result = Math.min(result, measureSize);
				break;
			default:
				break;
		}
		return result;
	}

	public void fling(int velocityX, int velocityY) {
		mScroller.fling(getScrollX(), getScrollY(),
				velocityX, velocityY,
				(int) (-mMaxOverScrollDistance + mMinSelectableIndex * mIntervalDis), (int) (mContentRectF.width() - mMaxOverScrollDistance - (mMarkCount - 1 - mMaxSelectableIndex) * mIntervalDis),
				0, 0,
				(int) mMaxOverScrollDistance, 0);
		ViewCompat.postInvalidateOnAnimation(this);
	}

	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		super.onSizeChanged(w, h, oldw, oldh);
		if (w != oldw || h != oldh) {
			mHeight = h;
			mMaxOverScrollDistance = w / 2.f;
			mContentRectF.set(0, 0, (mMarkCount - 1) * mIntervalDis, h);
			mViewScopeSize = (int) Math.ceil(mMaxOverScrollDistance / mIntervalDis);
		}
	}

	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);

		mCenterIndicatorPath.reset();
		float sizeDiv2 = mCursorSize / 2f;
		float sizeDiv3 = mCursorSize / 3f;
		mCenterIndicatorPath.moveTo(mMaxOverScrollDistance - sizeDiv2 + getScrollX(), 0);
		mCenterIndicatorPath.rLineTo(0, sizeDiv3);
		mCenterIndicatorPath.rLineTo(sizeDiv2, sizeDiv2);
		mCenterIndicatorPath.rLineTo(sizeDiv2, -sizeDiv2);
		mCenterIndicatorPath.rLineTo(0, -sizeDiv3);
		mCenterIndicatorPath.close();

		mMarkPaint.setColor(mHighlightColor);
		canvas.drawPath(mCenterIndicatorPath, mMarkPaint);

		int start = mCenterIndex - mViewScopeSize;
		int end = mCenterIndex + mViewScopeSize + 1;

		start = Math.max(start, -mViewScopeSize * 2);
		end = Math.min(end, mMarkCount + mViewScopeSize * 2);

		// extends both ends
		if (mCenterIndex == mMaxSelectableIndex) {
			end += mViewScopeSize;
		} else if (mCenterIndex == mMinSelectableIndex) {
			start -= mViewScopeSize;
		}

		float x = start * mIntervalDis;

		float markHeight = mHeight - mBottomSpace - mCenterTextSize - mTopSpace;
		// small scale Y offset
		float smallMarkShrinkY = markHeight * (1 - mMarkRatio) / 2f;
		smallMarkShrinkY = Math.min((markHeight - mMarkWidth) / 2f, smallMarkShrinkY);

		for (int i = start; i < end; i++) {
			float tempDis = mIntervalDis / 5f;
			// offset: Small mark offset Big mark
			for (int offset = -2; offset < 3; offset++) {
				float ox = x + offset * tempDis;

				if (i >= 0 && i <= mMarkCount && mCenterIndex == i) {
					int tempOffset = Math.abs(offset);
					if (tempOffset == 0) {
						mMarkPaint.setColor(mHighlightColor);
					} else if (tempOffset == 1) {
						mMarkPaint.setColor(mFadeMarkColor);
					} else {
						mMarkPaint.setColor(mMarkColor);
					}
				} else {
					mMarkPaint.setColor(mMarkColor);
				}

				if (offset == 0) {
					// center mark
					mMarkPaint.setStrokeWidth(mCenterMarkWidth);
					canvas.drawLine(ox, mTopSpace, ox, mTopSpace + markHeight, mMarkPaint);
				} else {
					// other small mark
					mMarkPaint.setStrokeWidth(mMarkWidth);
					canvas.drawLine(ox, mTopSpace + smallMarkShrinkY, ox, mTopSpace + markHeight - smallMarkShrinkY, mMarkPaint);
				}
			}

			// mark text
			if (mMarkCount > 0 && i >= 0 && i < mMarkCount) {
				CharSequence temp = mItems.get(i);
				if (mCenterIndex == i) {
					mMarkTextPaint.setColor(mHighlightColor);
					mMarkTextPaint.setTextSize(mCenterTextSize);
					if (!TextUtils.isEmpty(mAdditionCenterMark)) {
						float off = mAdditionCenterMarkWidth / 2f;
						float tsize = mMarkTextPaint.measureText(temp, 0, temp.length());
						canvas.drawText(temp, 0, temp.length(), x - off, mHeight - mBottomSpace, mMarkTextPaint);
						mMarkTextPaint.setTextSize(mNormalTextSize);
						canvas.drawText(mAdditionCenterMark, x + tsize / 2f, mHeight - mBottomSpace, mMarkTextPaint);
					} else {
						canvas.drawText(temp, 0, temp.length(), x, mHeight - mBottomSpace, mMarkTextPaint);
					}
				} else {
					mMarkTextPaint.setColor(mMarkTextColor);
					mMarkTextPaint.setTextSize(mNormalTextSize);
					canvas.drawText(temp, 0, temp.length(), x, mHeight - mBottomSpace, mMarkTextPaint);
				}
			}

			x += mIntervalDis;
		}
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		if (mItems == null || mItems.size() == 0 || !isEnabled()) {
			return false;
		}
		boolean ret = mGestureDetectorCompat.onTouchEvent(event);
		if (!mFling && MotionEvent.ACTION_UP == event.getAction()) {
			autoSettle();
			ret = true;
		}
		return ret || super.onTouchEvent(event);
	}

	@Override
	public void computeScroll() {
		super.computeScroll();
		if (mScroller.computeScrollOffset()) {
			scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
			refreshCenter();
			invalidate();
		} else {
			if (mFling) {
				mFling = false;
				autoSettle();
			}
		}
	}

	public void setAdditionCenterMark(String additionCenterMark) {
		mAdditionCenterMark = additionCenterMark;
		calcIntervalDis();
		invalidate();
	}

	private void autoSettle() {
		int sx = getScrollX();
		float dx = mCenterIndex * mIntervalDis - sx - mMaxOverScrollDistance;
		mScroller.startScroll(sx, 0, (int) dx, 0);
		postInvalidate();
		if (mLastSelectedIndex != mCenterIndex) {
			mLastSelectedIndex = mCenterIndex;
			if (null != mOnWheelItemSelectedListener) {
				mOnWheelItemSelectedListener.onWheelItemSelected(this, mCenterIndex);
			}
		}
	}

	/**
	 * limit center index in bounds.
	 *
	 * @param center
	 * @return
	 */
	private int safeCenter(int center) {
		if (center < mMinSelectableIndex) {
			center = mMinSelectableIndex;
		} else if (center > mMaxSelectableIndex) {
			center = mMaxSelectableIndex;
		}
		return center;
	}

	private void refreshCenter(int offsetX) {
		int offset = (int) (offsetX + mMaxOverScrollDistance);
		int tempIndex = Math.round(offset / mIntervalDis);
		tempIndex = safeCenter(tempIndex);
		if (mCenterIndex == tempIndex) {
			return;
		}
		mCenterIndex = tempIndex;
		if (null != mOnWheelItemSelectedListener) {
			mOnWheelItemSelectedListener.onWheelItemChanged(this, mCenterIndex);
		}
	}

	private void refreshCenter() {
		refreshCenter(getScrollX());
	}

	public void selectIndex(int index) {
		mCenterIndex = index;
		post(new Runnable() {
			@Override
			public void run() {
				scrollTo((int) (mCenterIndex * mIntervalDis - mMaxOverScrollDistance), 0);
				invalidate();
				refreshCenter();
			}
		});
	}

	public void smoothSelectIndex(int index) {
		if (!mScroller.isFinished()) {
			mScroller.abortAnimation();
		}
		int deltaIndex = index - mCenterIndex;
		mScroller.startScroll(getScrollX(), 0, (int) (deltaIndex * mIntervalDis), 0);
		invalidate();
	}

	public int getMinSelectableIndex() {
		return mMinSelectableIndex;
	}

	public void setMinSelectableIndex(int minSelectableIndex) {
		if (minSelectableIndex > mMaxSelectableIndex) {
			minSelectableIndex = mMaxSelectableIndex;
		}
		mMinSelectableIndex = minSelectableIndex;
		int afterCenter = safeCenter(mCenterIndex);
		if (afterCenter != mCenterIndex) {
			selectIndex(afterCenter);
		}
	}

	public int getMaxSelectableIndex() {
		return mMaxSelectableIndex;
	}

	public void setMaxSelectableIndex(int maxSelectableIndex) {
		if (maxSelectableIndex < mMinSelectableIndex) {
			maxSelectableIndex = mMinSelectableIndex;
		}
		mMaxSelectableIndex = maxSelectableIndex;
		int afterCenter = safeCenter(mCenterIndex);
		if (afterCenter != mCenterIndex) {
			selectIndex(afterCenter);
		}
	}

	public List getItems() {
		return mItems;
	}

	public void setItems(List items) {
		if (mItems == null) {
			mItems = new ArrayList<>();
		} else {
			mItems.clear();
		}
		mItems.addAll(items);
		mMarkCount = null == mItems ? 0 : mItems.size();
		if (mMarkCount > 0) {
			mMinSelectableIndex = Math.max(mMinSelectableIndex, 0);
			mMaxSelectableIndex = Math.min(mMaxSelectableIndex, mMarkCount - 1);
		}
		mContentRectF.set(0, 0, (mMarkCount - 1) * mIntervalDis, getMeasuredHeight());
		mCenterIndex = Math.min(mCenterIndex, mMarkCount);
		calcIntervalDis();
		invalidate();
	}

	public int getSelectedPosition() {
		return mCenterIndex;
	}

	public void setOnWheelItemSelectedListener(OnWheelItemSelectedListener onWheelItemSelectedListener) {
		mOnWheelItemSelectedListener = onWheelItemSelectedListener;
	}

	@Override
	public boolean onDown(MotionEvent e) {
		if (!mScroller.isFinished()) {
			mScroller.forceFinished(false);
		}
		mFling = false;
		if (null != getParent()) {
			getParent().requestDisallowInterceptTouchEvent(true);
		}
		return true;
	}

	@Override
	public void onShowPress(MotionEvent e) {

	}

	@Override
	public boolean onSingleTapUp(MotionEvent e) {
		playSoundEffect(SoundEffectConstants.CLICK);
		refreshCenter((int) (getScrollX() + e.getX() - mMaxOverScrollDistance));
		autoSettle();
		return true;
	}

	@Override
	public void onLongPress(MotionEvent e) {

	}

	@Override
	public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
		float dis = distanceX;
		float scrollX = getScrollX();
		if (scrollX < mMinSelectableIndex * mIntervalDis - 2 * mMaxOverScrollDistance) {
			dis = 0;
		} else if (scrollX < mMinSelectableIndex * mIntervalDis - mMaxOverScrollDistance) {
			dis = distanceX / 4.f;
		} else if (scrollX > mContentRectF.width() - (mMarkCount - mMaxSelectableIndex - 1) * mIntervalDis) {
			dis = 0;
		} else if (scrollX > mContentRectF.width() - (mMarkCount - mMaxSelectableIndex - 1) * mIntervalDis - mMaxOverScrollDistance) {
			dis = distanceX / 4.f;
		}
		scrollBy((int) dis, 0);
		refreshCenter();
		return true;
	}

	@Override
	public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
		float scrollX = getScrollX();
		if (scrollX < -mMaxOverScrollDistance + mMinSelectableIndex * mIntervalDis || scrollX > mContentRectF.width() - mMaxOverScrollDistance - (mMarkCount - 1 - mMaxSelectableIndex) * mIntervalDis) {
			return false;
		} else {
			mFling = true;
			fling((int) -velocityX, 0);
			return true;
		}
	}

	@Override
	public Parcelable onSaveInstanceState() {
		Parcelable superState = super.onSaveInstanceState();
		SavedState ss = new SavedState(superState);
		ss.index = getSelectedPosition();
		ss.min = mMinSelectableIndex;
		ss.max = mMaxSelectableIndex;
		return ss;
	}

	@Override
	public void onRestoreInstanceState(Parcelable state) {
		SavedState ss = (SavedState) state;
		super.onRestoreInstanceState(ss.getSuperState());
		mMinSelectableIndex = ss.min;
		mMaxSelectableIndex = ss.max;
		selectIndex(ss.index);
		requestLayout();
	}

	public interface OnWheelItemSelectedListener {
		void onWheelItemChanged(WheelView wheelView, int position);

		void onWheelItemSelected(WheelView wheelView, int position);
	}

	static class SavedState extends BaseSavedState {
		public static final Creator CREATOR
				= new Creator() {
			public SavedState createFromParcel(Parcel in) {
				return new SavedState(in);
			}

			public SavedState[] newArray(int size) {
				return new SavedState[size];
			}
		};
		int index;
		int min;
		int max;

		SavedState(Parcelable superState) {
			super(superState);
		}

		private SavedState(Parcel in) {
			super(in);
			index = in.readInt();
			min = in.readInt();
			max = in.readInt();
		}

		@Override
		public void writeToParcel(Parcel out, int flags) {
			super.writeToParcel(out, flags);
			out.writeInt(index);
			out.writeInt(min);
			out.writeInt(max);
		}

		@Override
		public String toString() {
			return "WheelView.SavedState{"
					+ Integer.toHexString(System.identityHashCode(this))
					+ " index=" + index + " min=" + min + " max=" + max + "}";
		}
	}
}

这个是主要类的代码,这样还是不够的,我们看一下布局中的引用:

里面还有许多的属性: res  -->  values  --> 创建attrs.xml文件


    
        
        
        
        
        
        
        
        
        
    

在Activity里面咱们调用方法:

private void confitAgeWheelView() {
        ArrayList localArrayList = new ArrayList();
        int i = 18;
        while (i <= 60) {
            StringBuilder localStringBuilder = new StringBuilder();
            localStringBuilder.append(String.valueOf(i));
            localStringBuilder.append("岁");
            localArrayList.add(localStringBuilder.toString());
            i += 1;
        }
        mWheelView.setItems(localArrayList);
        mWheelView.selectIndex(6);

        mWheelView.setOnWheelItemSelectedListener(new WheelView.OnWheelItemSelectedListener() {
            @Override
            public void onWheelItemChanged(WheelView wheelView, int position) {

            }

            @Override
            public void onWheelItemSelected(WheelView wheelView, int position) {
                age_num = position + 18;//选中数字因为position是从0开始的,所以要加上你初始化起始的数字大小
            }
        });
    }

差不多到这里就完成了,有问题请留言,谢谢

你可能感兴趣的:(WheelView横向选择器)