MeasureSpec学习—对Integer.MAX_VALUE >> 2的认识

本文详细介绍了MeasureSpec在自定义View和ViewGroup中的应用,包括MeasureSpec的三种模式:精确模式、最大模式和未指定模式,以及MeasureSpec工具类的使用方法。
在自定义ViewViewGroup的时候,我们经常会遇到int型的MeasureSpec来表示一个组件的大小,这个变量里面不仅有组件的尺寸大小,还有大小的模式。

这个大小的模式,有点难以理解。在系统中组件的大小模式有三种:

1.精确模式(MeasureSpec.EXACTLY

在这种模式下,尺寸的值是多少,那么这个组件的长或宽就是多少。

2.最大模式(MeasureSpec.AT_MOST

这个也就是父组件,能够给出的最大的空间,当前组件的长或宽最大只能为这么大,当然也可以比这个小。

3.未指定模式(MeasureSpec.UNSPECIFIED

这个就是说,当前组件,可以随便用空间,不受限制。

    可能有很多人想不通,一个int型整数怎么可以表示两个东西(大小模式和大小的值),一个int类型我们知道有32位。而模式有三种,要表示三种状  态,至少得2位二进制位。于是系统采用了最高的2位表示模式。如图:


最高两位是00的时候表示"未指定模式"。即MeasureSpec.UNSPECIFIED

最高两位是01的时候表示"'精确模式"。即MeasureSpec.EXACTLY

最高两位是10的时候表示"最大模式"。即MeasureSpec.AT_MOST

很多人一遇到位操作头就大了,为了操作简便,于是系统给我提供了一个MeasureSpec工具类。

这个工具类有四个方法和三个常量(上面所示)供我们使用:

 

//这个是由我们给出的尺寸大小和模式生成一个包含这两个信息的int变量,这里这个模式这个参数,传三个常量中的一个。

public static int makeMeasureSpec(int size, int mode)

 

//这个是得到这个变量中表示的模式信息,将得到的值与三个常量进行比较。

public static int getMode(int measureSpec)

 

//这个是得到这个变量中表示的尺寸大小的值。

public static int getSize(int measureSpec)

 

//把这个变量里面的模式和大小组成字符串返回来,方便打日志

 public static String toString(int measureSpec)

package com.super_rabbit.wheel_picker import android.content.Context import android.graphics.Canvas import android.graphics.Paint import android.graphics.Typeface import android.os.Build import android.util.AttributeSet import android.util.Log import android.view.* import android.view.animation.DecelerateInterpolator import android.widget.OverScroller import androidx.core.content.ContextCompat import java.util.* /** * Created by wanglu on 3/10/18. */ /** * Interface to listen for changes of the current value. */ interface OnValueChangeListener { /** * Called upon a change of the current value. * * @param picker The NumberPicker associated with this listener. * @param oldVal The previous value. * @param newVal The new value. */ fun onValueChange(picker: WheelPicker, oldVal: String, newVal: String) } interface OnScrollListener { /** * Callback invoked while the number picker scroll state has changed. * * @param view The view whose scroll state is being reported. * @param scrollState The current scroll state. One of * [.SCROLL_STATE_IDLE], * [.SCROLL_STATE_TOUCH_SCROLL] or * [.SCROLL_STATE_IDLE]. */ fun onScrollStateChange(view: WheelPicker, scrollState: Int) companion object { /** * The view is not scrolling. */ const val SCROLL_STATE_IDLE = 0 /** * The user is scrolling using touch, and his finger is still on the screen. */ const val SCROLL_STATE_TOUCH_SCROLL = 1 /** * The user had previously been scrolling using touch and performed a fling. */ const val SCROLL_STATE_FLING = 2 } } class WheelPicker @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : View(context, attrs, defStyleAttr) { private val TOP_AND_BOTTOM_FADING_EDGE_STRENGTH = 0.9f private val SNAP_SCROLL_DURATION = 300 private val SELECTOR_MAX_FLING_VELOCITY_ADJUSTMENT = 4 private val DEFAULT_ITEM_COUNT = 3 private val DEFAULT_TEXT_SIZE = 80 private var mSelectorItemCount: Int private var mSelectorVisibleItemCount: Int private var mMinIndex: Int private var mMaxIndex: Int private var mMaxValidIndex: Int? = null private var mMinValidIndex: Int? = null private var mWheelMiddleItemIndex: Int private var mWheelVisibleItemMiddleIndex: Int private var mSelectorItemIndices: ArrayList<Int> private var mSelectorItemValidStatus: ArrayList<Boolean> private var mCurSelectedItemIndex = 0 private var mWrapSelectorWheelPreferred: Boolean private var mTextPaint: Paint = Paint() private var mSelectedTextColor: Int private var mUnSelectedTextColor: Int private var mTextSize: Int private var mTextAlign: String private var mOverScroller: OverScroller? = null private var mVelocityTracker: VelocityTracker? = null private val mTouchSlop: Int private val mMaximumVelocity: Int private val mMinimumVelocity: Int private var mLastY: Float = 0f private var mIsDragging: Boolean = false private var mCurrentFirstItemOffset: Int = 0 private var mInitialFirstItemOffset = Int.MIN_VALUE private var mTextGapHeight: Int = 0 private var mItemHeight: Int = 0 private var mTextHeight: Int = 0 private var mPreviousScrollerY: Int = 0 private var mOnValueChangeListener: OnValueChangeListener? = null private var mOnScrollListener: OnScrollListener? = null private var mAdapter: WheelAdapter? = null private var mFadingEdgeEnabled = true private var mSelectedTextScale = 0.3f private var mTypefaceIndex: Int = 0 /** * The current scroll state of the number picker. */ private var mScrollState = OnScrollListener.SCROLL_STATE_IDLE init { val attributesArray = context.obtainStyledAttributes(attrs, R.styleable.WheelPicker, defStyleAttr, 0) mSelectorItemCount = attributesArray.getInt(R.styleable.WheelPicker_wheelItemCount, DEFAULT_ITEM_COUNT) + 2 mWheelMiddleItemIndex = (mSelectorItemCount - 1) / 2 mSelectorVisibleItemCount = mSelectorItemCount - 2 mWheelVisibleItemMiddleIndex = (mSelectorVisibleItemCount - 1) / 2 mSelectorItemIndices = ArrayList(mSelectorItemCount) mSelectorItemValidStatus = ArrayList(mSelectorItemCount) mMinIndex = attributesArray.getInt(R.styleable.WheelPicker_min, Integer.MIN_VALUE) mMaxIndex = attributesArray.getInt(R.styleable.WheelPicker_max, Integer.MAX_VALUE) if (attributesArray.hasValue(R.styleable.WheelPicker_maxValidIndex)) mMaxValidIndex = attributesArray.getInt(R.styleable.WheelPicker_maxValidIndex, 0) if (attributesArray.hasValue(R.styleable.WheelPicker_minValidIndex)) mMinValidIndex = attributesArray.getInt(R.styleable.WheelPicker_minValidIndex, 0) mWrapSelectorWheelPreferred = attributesArray.getBoolean(R.styleable.WheelPicker_wrapSelectorWheel, false) mSelectedTextScale = attributesArray.getFloat(R.styleable.WheelPicker_selectedTextScale, 0.3f) mOverScroller = OverScroller(context, DecelerateInterpolator(2.5f)) val configuration = ViewConfiguration.get(context) mTouchSlop = configuration.scaledTouchSlop mMaximumVelocity = configuration.scaledMaximumFlingVelocity / SELECTOR_MAX_FLING_VELOCITY_ADJUSTMENT mMinimumVelocity = configuration.scaledMinimumFlingVelocity mSelectedTextColor = attributesArray.getColor( R.styleable.WheelPicker_selectedTextColor , ContextCompat.getColor(context, R.color.color_4_blue) ) mUnSelectedTextColor = attributesArray.getColor( R.styleable.WheelPicker_textColor , ContextCompat.getColor(context, R.color.color_3_dark_blue) ) mTextSize = attributesArray.getDimensionPixelSize(R.styleable.WheelPicker_textSize, DEFAULT_TEXT_SIZE) val textAlignInt = attributesArray.getInt(R.styleable.WheelPicker_align, 1) mTextAlign = when (textAlignInt) { 0 -> "LEFT" 1 -> "CENTER" 2 -> "RIGHT" else -> "CENTER" } mFadingEdgeEnabled = attributesArray.getBoolean(R.styleable.WheelPicker_fadingEdgeEnabled, true) mTypefaceIndex = attributesArray.getInt(R.styleable.WheelPicker_typeface, 0); mTextPaint.run { isAntiAlias = true isAntiAlias = true textSize = mTextSize.toFloat() textAlign = Paint.Align.valueOf(mTextAlign) style = Paint.Style.FILL_AND_STROKE typeface = when(mTypefaceIndex) { // for the constant values please check the WheelPicker_typeface in the attrs.xml 0 -> Typeface.DEFAULT 1 -> Typeface.SANS_SERIF 2 -> Typeface.SERIF else -> Typeface.DEFAULT } } attributesArray.recycle() initializeSelectorWheelIndices() } override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { super.onLayout(changed, left, top, right, bottom) if (changed) { // need to do all this when we know our size initializeSelectorWheel() initializeFadingEdges() } } override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { // Try greedily to fit the max width and height. var lp: ViewGroup.LayoutParams? = layoutParams if (lp == null) lp = ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) var width = calculateSize(suggestedMinimumWidth, lp.width, widthMeasureSpec) var height = calculateSize(suggestedMinimumHeight, lp.height, heightMeasureSpec) width += paddingLeft + paddingRight height += paddingTop + paddingBottom setMeasuredDimension(width, height) } override fun getSuggestedMinimumWidth(): Int { var suggested = super.getSuggestedMinimumHeight() if (mSelectorVisibleItemCount > 0) { suggested = Math.max(suggested, computeMaximumWidth()) } return suggested } override fun getSuggestedMinimumHeight(): Int { var suggested = super.getSuggestedMinimumWidth() if (mSelectorVisibleItemCount > 0) { val fontMetricsInt = mTextPaint.fontMetricsInt val height = fontMetricsInt.descent - fontMetricsInt.ascent suggested = Math.max(suggested, height * mSelectorVisibleItemCount) } return suggested } private fun computeMaximumWidth(): Int { mTextPaint.textSize = mTextSize * 1.3f if (mAdapter != null) { return if (!mAdapter!!.getTextWithMaximumLength().isEmpty()) { val suggestedWith = mTextPaint.measureText(mAdapter!!.getTextWithMaximumLength()).toInt() mTextPaint.textSize = mTextSize * 1.0f suggestedWith } else { val suggestedWith = mTextPaint.measureText("0000").toInt() mTextPaint.textSize = mTextSize * 1.0f suggestedWith } } val widthForMinIndex = mTextPaint.measureText(mMinIndex.toString()).toInt() val widthForMaxIndex = mTextPaint.measureText(mMaxIndex.toString()).toInt() mTextPaint.textSize = mTextSize * 1.0f return if (widthForMinIndex > widthForMaxIndex) widthForMinIndex else widthForMaxIndex } private fun calculateSize(suggestedSize: Int, paramSize: Int, measureSpec: Int): Int { var result = 0 val size = MeasureSpec.getSize(measureSpec) val mode = MeasureSpec.getMode(measureSpec) when (MeasureSpec.getMode(mode)) { MeasureSpec.AT_MOST -> if (paramSize == ViewGroup.LayoutParams.WRAP_CONTENT) result = Math.min(suggestedSize, size) else if (paramSize == ViewGroup.LayoutParams.MATCH_PARENT) result = size else { result = Math.min(paramSize, size) } MeasureSpec.EXACTLY -> result = size MeasureSpec.UNSPECIFIED -> result = if (paramSize == ViewGroup.LayoutParams.WRAP_CONTENT || paramSize == ViewGroup.LayoutParams .MATCH_PARENT ) suggestedSize else { paramSize } } return result } private fun initializeSelectorWheel() { mItemHeight = getItemHeight() mTextHeight = computeTextHeight() mTextGapHeight = getGapHeight() val visibleMiddleItemPos = mItemHeight * mWheelVisibleItemMiddleIndex + (mItemHeight + mTextHeight) / 2 mInitialFirstItemOffset = visibleMiddleItemPos - mItemHeight * mWheelMiddleItemIndex mCurrentFirstItemOffset = mInitialFirstItemOffset } private fun initializeFadingEdges() { isVerticalFadingEdgeEnabled = mFadingEdgeEnabled if (mFadingEdgeEnabled) setFadingEdgeLength((bottom - top - mTextSize) / 2) } private fun initializeSelectorWheelIndices() { mSelectorItemIndices.clear() mSelectorItemValidStatus.clear() mCurSelectedItemIndex = if (mMinValidIndex == null || mMinValidIndex!! < mMinIndex) { if (mMinIndex <= 0) { 0 } else { mMinIndex } } else { if (mMinValidIndex!! <= 0) { 0 } else { mMinValidIndex!! } } for (i in 0 until mSelectorItemCount) { var selectorIndex = mCurSelectedItemIndex + (i - mWheelMiddleItemIndex) if (mWrapSelectorWheelPreferred) { selectorIndex = getWrappedSelectorIndex(selectorIndex) } mSelectorItemIndices.add(selectorIndex) mSelectorItemValidStatus.add(isValidPosition(selectorIndex)) } } override fun getBottomFadingEdgeStrength(): Float { return TOP_AND_BOTTOM_FADING_EDGE_STRENGTH } override fun getTopFadingEdgeStrength(): Float { return TOP_AND_BOTTOM_FADING_EDGE_STRENGTH } override fun onDraw(canvas: Canvas) { super.onDraw(canvas) drawVertical(canvas) } override fun onTouchEvent(event: MotionEvent): Boolean { onTouchEventVertical(event) return true } private fun onTouchEventVertical(event: MotionEvent) { if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain() } mVelocityTracker?.addMovement(event) val action: Int = event.actionMasked when (action) { MotionEvent.ACTION_DOWN -> { if (!mOverScroller!!.isFinished) mOverScroller!!.forceFinished(true) mLastY = event.y } MotionEvent.ACTION_MOVE -> { var deltaY = event.y - mLastY if (!mIsDragging && Math.abs(deltaY) > mTouchSlop) { parent?.requestDisallowInterceptTouchEvent(true) if (deltaY > 0) { deltaY -= mTouchSlop } else { deltaY += mTouchSlop } onScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) mIsDragging = true } if (mIsDragging) { scrollBy(0, deltaY.toInt()) invalidate() mLastY = event.y } } MotionEvent.ACTION_UP -> { if (mIsDragging) { mIsDragging = false parent?.requestDisallowInterceptTouchEvent(false) mVelocityTracker?.computeCurrentVelocity(1000, mMaximumVelocity.toFloat()) val velocity = mVelocityTracker?.yVelocity?.toInt() if (Math.abs(velocity!!) > mMinimumVelocity) { mPreviousScrollerY = 0 mOverScroller?.fling( scrollX, scrollY, 0, velocity, 0, 0, Integer.MIN_VALUE, Integer.MAX_VALUE, 0, (getItemHeight() * 0.7).toInt() ) invalidateOnAnimation() onScrollStateChange(OnScrollListener.SCROLL_STATE_FLING) } recyclerVelocityTracker() } else { //click event val y = event.y.toInt() handlerClickVertical(y) } } MotionEvent.ACTION_CANCEL -> { if (mIsDragging) { mIsDragging = false } recyclerVelocityTracker() } } } private fun handlerClickVertical(y: Int) { val selectorIndexOffset = y / mItemHeight - mWheelVisibleItemMiddleIndex changeValueBySteps(selectorIndexOffset) } override fun scrollBy(x: Int, y: Int) { if (y == 0) return val gap = mTextGapHeight if (!mWrapSelectorWheelPreferred && y > 0 && (mSelectorItemIndices[mWheelMiddleItemIndex] <= mMinIndex || (mMinValidIndex != null && mSelectorItemIndices[mWheelMiddleItemIndex] <= mMinValidIndex!!)) ) { if (mCurrentFirstItemOffset + y - mInitialFirstItemOffset < gap / 2) mCurrentFirstItemOffset += y else { mCurrentFirstItemOffset = mInitialFirstItemOffset + (gap / 2) if (!mOverScroller!!.isFinished && !mIsDragging) { mOverScroller!!.abortAnimation() } } return } if (!mWrapSelectorWheelPreferred && y < 0 && (mSelectorItemIndices[mWheelMiddleItemIndex] >= mMaxIndex || (mMaxValidIndex != null && mSelectorItemIndices[mWheelMiddleItemIndex] >= mMaxValidIndex!!)) ) { if (mCurrentFirstItemOffset + y - mInitialFirstItemOffset > -(gap / 2)) mCurrentFirstItemOffset += y else { mCurrentFirstItemOffset = mInitialFirstItemOffset - (gap / 2) if (!mOverScroller!!.isFinished && !mIsDragging) { mOverScroller!!.abortAnimation() } } return } mCurrentFirstItemOffset += y while (mCurrentFirstItemOffset - mInitialFirstItemOffset < -gap) { mCurrentFirstItemOffset += mItemHeight increaseSelectorsIndex() if (!mWrapSelectorWheelPreferred && (mSelectorItemIndices[mWheelMiddleItemIndex] >= mMaxIndex || (mMaxValidIndex != null && mSelectorItemIndices[mWheelMiddleItemIndex] >= mMaxValidIndex!!)) ) { mCurrentFirstItemOffset = mInitialFirstItemOffset } } while (mCurrentFirstItemOffset - mInitialFirstItemOffset > gap) { mCurrentFirstItemOffset -= mItemHeight decreaseSelectorsIndex() if (!mWrapSelectorWheelPreferred && (mSelectorItemIndices[mWheelMiddleItemIndex] <= mMinIndex || (mMinValidIndex != null && mSelectorItemIndices[mWheelMiddleItemIndex] <= mMinValidIndex!!)) ) { mCurrentFirstItemOffset = mInitialFirstItemOffset } } onSelectionChanged(mSelectorItemIndices[mWheelMiddleItemIndex], true) } override fun computeScroll() { super.computeScroll() if (mOverScroller!!.computeScrollOffset()) { val x = mOverScroller!!.currX val y = mOverScroller!!.currY if (mPreviousScrollerY == 0) { mPreviousScrollerY = mOverScroller!!.startY } scrollBy(x, y - mPreviousScrollerY) mPreviousScrollerY = y invalidate() } else { if (!mIsDragging) //align item adjustItemVertical() } } private fun adjustItemVertical() { mPreviousScrollerY = 0 var deltaY = mInitialFirstItemOffset - mCurrentFirstItemOffset if (Math.abs(deltaY) > mItemHeight / 2) { deltaY += if (deltaY > 0) -mItemHeight else mItemHeight } if (deltaY != 0) { mOverScroller!!.startScroll(scrollX, scrollY, 0, deltaY, 800) invalidateOnAnimation() } onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE) } private fun recyclerVelocityTracker() { mVelocityTracker?.recycle() mVelocityTracker = null } override fun onScrollChanged(l: Int, t: Int, oldl: Int, oldt: Int) { super.onScrollChanged(l, t, oldl, oldt) } private fun onScrollStateChange(scrollState: Int) { if (mScrollState == scrollState) { return } mScrollState = scrollState mOnScrollListener?.onScrollStateChange(this, scrollState) } private fun getItemHeight(): Int { return height / (mSelectorItemCount - 2) } private fun getGapHeight(): Int { return getItemHeight() - computeTextHeight() } private fun computeTextHeight(): Int { val metricsInt = mTextPaint.fontMetricsInt return Math.abs(metricsInt.bottom + metricsInt.top) } private fun invalidateOnAnimation() { if (Build.VERSION.SDK_INT >= 16) postInvalidateOnAnimation() else invalidate() } private fun drawVertical(canvas: Canvas) { if (mSelectorItemIndices.size == 0) return val itemHeight = getItemHeight() val x = when (mTextPaint.textAlign) { Paint.Align.LEFT -> paddingLeft.toFloat() Paint.Align.CENTER -> ((right - left) / 2).toFloat() Paint.Align.RIGHT -> (right - left).toFloat() - paddingRight.toFloat() else -> ((right - left) / 2).toFloat() } var y = mCurrentFirstItemOffset.toFloat() var i = 0 val topIndexDiffToMid = mWheelVisibleItemMiddleIndex val bottomIndexDiffToMid = mSelectorVisibleItemCount - mWheelVisibleItemMiddleIndex - 1 val maxIndexDiffToMid = Math.max(topIndexDiffToMid, bottomIndexDiffToMid) while (i < mSelectorItemIndices.size) { var scale = 1f val offsetToMiddle = Math.abs(y - (mInitialFirstItemOffset + mWheelMiddleItemIndex * itemHeight).toFloat()) if (maxIndexDiffToMid != 0) scale = mSelectedTextScale * (itemHeight * maxIndexDiffToMid - offsetToMiddle) / (itemHeight * maxIndexDiffToMid) + 1 if (mSelectorItemValidStatus[i]) { if (offsetToMiddle < mItemHeight / 2) { mTextPaint.color = mSelectedTextColor } else { mTextPaint.color = mUnSelectedTextColor } } else { mTextPaint.color = ContextCompat.getColor(context, R.color.material_grey_300) } canvas.save() canvas.scale(scale, scale, x, y) canvas.drawText(getValue(mSelectorItemIndices[i]), x, y, mTextPaint) canvas.restore() y += itemHeight i++ } } private fun getPosition(value: String): Int = when { mAdapter != null -> { validatePosition(mAdapter!!.getPosition(value)) } else -> try { val position = value.toInt() validatePosition(position) } catch (e: NumberFormatException) { 0 } } private fun increaseSelectorsIndex() { for (i in 0 until (mSelectorItemIndices.size - 1)) { mSelectorItemIndices[i] = mSelectorItemIndices[i + 1] mSelectorItemValidStatus[i] = mSelectorItemValidStatus[i + 1] } var nextScrollSelectorIndex = mSelectorItemIndices[mSelectorItemIndices.size - 2] + 1 if (mWrapSelectorWheelPreferred && nextScrollSelectorIndex > mMaxIndex) { nextScrollSelectorIndex = mMinIndex } mSelectorItemIndices[mSelectorItemIndices.size - 1] = nextScrollSelectorIndex mSelectorItemValidStatus[mSelectorItemIndices.size - 1] = isValidPosition(nextScrollSelectorIndex) } private fun decreaseSelectorsIndex() { for (i in mSelectorItemIndices.size - 1 downTo 1) { mSelectorItemIndices[i] = mSelectorItemIndices[i - 1] mSelectorItemValidStatus[i] = mSelectorItemValidStatus[i - 1] } var nextScrollSelectorIndex = mSelectorItemIndices[1] - 1 if (mWrapSelectorWheelPreferred && nextScrollSelectorIndex < mMinIndex) { nextScrollSelectorIndex = mMaxIndex } mSelectorItemIndices[0] = nextScrollSelectorIndex mSelectorItemValidStatus[0] = isValidPosition(nextScrollSelectorIndex) } private fun changeValueBySteps(steps: Int) { mPreviousScrollerY = 0 mOverScroller!!.startScroll(0, 0, 0, -mItemHeight * steps, SNAP_SCROLL_DURATION) invalidate() } private fun onSelectionChanged(current: Int, notifyChange: Boolean) { val previous = mCurSelectedItemIndex mCurSelectedItemIndex = current if (notifyChange && previous != current) { notifyChange(previous, current) } } private fun getWrappedSelectorIndex(selectorIndex: Int): Int { if (selectorIndex > mMaxIndex) { return mMinIndex + (selectorIndex - mMaxIndex) % (mMaxIndex - mMinIndex + 1) - 1 } else if (selectorIndex < mMinIndex) { return mMaxIndex - (mMinIndex - selectorIndex) % (mMaxIndex - mMinIndex + 1) + 1 } return selectorIndex } private fun notifyChange(previous: Int, current: Int) { mOnValueChangeListener?.onValueChange(this, getValue(previous), getValue(current)) } private fun validatePosition(position: Int): Int { return if (!mWrapSelectorWheelPreferred) { when { mMaxValidIndex == null && position > mMaxIndex -> mMaxIndex mMaxValidIndex != null && position > mMaxValidIndex!! -> mMaxValidIndex!! mMinValidIndex == null && position < mMinIndex -> mMinIndex mMinValidIndex != null && position < mMinValidIndex!! -> mMinValidIndex!! else -> position } } else { getWrappedSelectorIndex(position) } } fun scrollTo(position: Int) { if (mCurSelectedItemIndex == position) return mCurSelectedItemIndex = position mSelectorItemIndices.clear() for (i in 0 until mSelectorItemCount) { var selectorIndex = mCurSelectedItemIndex + (i - mWheelMiddleItemIndex) if (mWrapSelectorWheelPreferred) { selectorIndex = getWrappedSelectorIndex(selectorIndex) } mSelectorItemIndices.add(selectorIndex) } invalidate() } fun setOnValueChangedListener(onValueChangeListener: OnValueChangeListener) { mOnValueChangeListener = onValueChangeListener } fun setOnScrollListener(onScrollListener: OnScrollListener) { mOnScrollListener = onScrollListener } fun smoothScrollTo(position: Int) { val realPosition = validatePosition(position) changeValueBySteps(realPosition - mCurSelectedItemIndex) } fun smoothScrollToValue(value: String) { smoothScrollTo(getPosition(value)) } fun scrollToValue(value: String) { scrollTo(getPosition(value)) } fun setUnselectedTextColor(resourceId: Int) { mUnSelectedTextColor = resourceId } /** * Set user define adapter * * @adapter user define adapter * @indexRangeBasedOnAdapterSize specific if the picker's min~max range is based on adapter's size */ fun setAdapter(adapter: WheelAdapter?, indexRangeBasedOnAdapterSize: Boolean = true) { mAdapter = adapter if (mAdapter == null) { initializeSelectorWheelIndices() invalidate() return } if (adapter!!.getSize() != -1 && indexRangeBasedOnAdapterSize) { mMaxIndex = adapter.getSize() - 1 mMinIndex = 0 } mMaxValidIndex = adapter.getMaxValidIndex() mMinValidIndex = adapter.getMinValidIndex() initializeSelectorWheelIndices() invalidate() mAdapter?.picker = this } /** * Set a custom typeface object for the text * * @param typeface the custom typeface object */ fun setTypeface(typeface: Typeface) { mTextPaint.typeface = typeface } /** * Sets whether the selector wheel shown during flinging/scrolling should * wrap around the {@link NumberPicker#getMinValue()} and * {@link NumberPicker#getMaxValue()} values. * <p> * By default if the range (max - min) is more than the number of items shown * on the selector wheel the selector wheel wrapping is enabled. * </p> * <p> * <strong>Note:</strong> If the number of items, i.e. the range ( * {@link #getMaxValue()} - {@link #getMinValue()}) is less than * the number of items shown on the selector wheel, the selector wheel will * not wrap. Hence, in such a case calling this method is a NOP. * </p> * * @param wrapSelectorWheel Whether to wrap. */ fun setWrapSelectorWheel(wrap: Boolean) { mWrapSelectorWheelPreferred = wrap invalidate() } /** * Gets whether the selector wheel wraps when reaching the min/max value. * * @return True if the selector wheel wraps. * * @see .getMinValue * @see .getMaxValue */ fun getWrapSelectorWheel(): Boolean { return mWrapSelectorWheelPreferred } /** * Set how many visible item show in the picker */ fun setWheelItemCount(count: Int) { mSelectorItemCount = count + 2 mWheelMiddleItemIndex = (mSelectorItemCount - 1) / 2 mSelectorVisibleItemCount = mSelectorItemCount - 2 mWheelVisibleItemMiddleIndex = (mSelectorVisibleItemCount - 1) / 2 mSelectorItemIndices = ArrayList(mSelectorItemCount) mSelectorItemValidStatus = ArrayList(mSelectorItemCount) reset() invalidate() } /** * Set color for current selected item */ fun setSelectedTextColor(colorId: Int) { mSelectedTextColor = ContextCompat.getColor(context, colorId) invalidate() } fun getValue(position: Int): String = when { mAdapter != null -> mAdapter!!.getValue(position) else -> if (!mWrapSelectorWheelPreferred) { when { position > mMaxIndex -> "" position < mMinIndex -> "" else -> position.toString() } } else { getWrappedSelectorIndex(position).toString() } } fun setValue(value: String) { scrollToValue(value) } fun setMaxValue(max: Int) { mMaxIndex = max } fun getMaxValue(): String { return if (mAdapter != null) { mAdapter!!.getValue(mMaxIndex) } else { mMaxIndex.toString() } } fun setMinValue(min: Int) { mMinIndex = min } fun setMinValidValue(minValid: Int?) { mMinValidIndex = minValid } fun setMaxValidValue(maxValid: Int?) { mMaxValidIndex = maxValid } fun getMinValue(): String { return if (mAdapter != null) { mAdapter!!.getValue(mMinIndex) } else { mMinIndex.toString() } } fun reset() { initializeSelectorWheelIndices() initializeSelectorWheel() invalidate() } fun getCurrentItem(): String { return getValue(mCurSelectedItemIndex) } fun isValidPosition(position: Int): Boolean { return when { mMinValidIndex != null && position < mMinValidIndex!! -> false mMaxValidIndex != null && position > mMaxValidIndex!! -> false else -> true } } } internal fun Int.clamp(min: Int, max: Int): Int { if (this < min) return min return if (this > max) max else this }
最新发布
10-29
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值