NumberPicker源码分析+自定义View简单实现NumberPicker

更新时间:2023-07-13 13:50:29 阅读: 评论:0

NumberPicker 源码分析+⾃定义View 简单实现NumberPicker
NumberPicker 介绍
A widget that enables the ur to lect a number from a predefined
range. There are two flavors of this widget and which one is prented
to the ur depends on the current theme.
简单来说就是可滑动选择数字的控件,具体的效果如下:
使⽤
源码分析
因为NumberPicker的外表会因为Theme的不同⽽不同,因此我们可以不⽤特别关⼼初始化外观的那部分代码。
NumberPicker继承⾃LinearLayout,⽽它有需要重写onDraw⽅法,因此下⾯的这句代码是必须的:按着view的绘制流程来看源码,NumberPicker的并没有重写onMeasure,因此直接看onLayout⽅法:
mNumberPicker = findViewById(R .id.system _number_picker);
mNumberPicker .tMaxValue (5);
mNumberPicker .tMinValue (1);
mNumberPicker .tWrapSelectorWheel (true);//是否循环显⽰
// By default  Linearlayout that we extend is  not  drawn. This is
// its draw() method is  not  called but dispatchDraw() is  called
// directly (e ViewGroup.drawChild()). However, this  class  us
// the fading edge effect implemented by  View and  we need our
// draw() method to be called. Therefore, we declare we will draw.
tWillNotDraw(!mHasSelectorWheel);
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (!mHasSelectorWheel) {
return;
}
final int msrdWdth = getMeasuredWidth();
final int msrdHght = getMeasuredHeight();
//对中间的editext进⾏layout操作
final int inptTxtMsrdWdth = MeasuredWidth();
final int inptTxtMsrdHght = MeasuredHeight();
final int inptTxtLeft = (msrdWdth - inptTxtMsrdWdth) / 2;
final int inptTxtTop = (msrdHght - inptTxtMsrdHght) / 2;
final int inptTxtRight = inptTxtLeft + inptTxtMsrdWdth;
final int inptTxtBottom = inptTxtTop + inptTxtMsrdHght;
mInputText.layout(inptTxtLeft, inptTxtTop, inptTxtRight, inptTxtBottom);
if (changed) {
// need to do all this when we know our size
initializeSelectorWheel();//初始化显⽰的三个数字的位置信息,以备onDraw使⽤
initializeFadingEdges();//布局上⽅和下⽅的背景虚化的部分
//两根分割线的top和bottom位置
mTopSelectionDividerTop = (getHeight() - mSelectionDividersDistance) / 2
- mSelectionDividerHeight;
mBottomSelectionDividerBottom = mTopSelectionDividerTop + 2 * mSelectionDividerHeight
+ mSelectionDividersDistance;
}
}
onLayout⽅法⾥有⼀个mHasSelectorWheel,他起什么作⽤呢?
Flag whether this widget has a lector wheel.
仅仅是⼀个标记位,具体的赋值是在NumberPicker的构造函数⾥:
mHasSelectorWheel = (layoutResId != DEFAULT_LAYOUT_RESOURCE_ID);
我们可以在这句代码前设个断点,然后debug,我的测试机是Android4.4的,通过debug发现这个值为true,因此他不会直接调⽤Layout(changed, left, top, right, bottom);,⽽是⾃⼰处理layout操作。
onLayout⽅法⾥调⽤了initializeSelectorWheel():
private void initializeSelectorWheel() {
blake lively
initializeSelectorWheelIndices();
int[] lectorIndices = mSelectorIndices;
//根据⽂字的数量以及gap来计算所需布局的⾼
int totalTextHeight = lectorIndices.length * mTextSize;
float totalTextGapHeight = (mBottom - mTop) - totalTextHeight;
float textGapCount = lectorIndices.length;
mSelectorTextGapHeight = (int) (totalTextGapHeight / textGapCount + 0.5f);
mSelectorElementHeight = mTextSize + mSelectorTextGapHeight;
voa官网// Ensure that the middle item is positioned the same as the text in
// mInputText
//初始化了mInitialScrollOfft和mCurrentScrollOfft两个属性,
// 在scrollby和ondraw⾥绘制⽂字时使⽤,以达到轮⼦的效果。
int editTextTextPosition = Baline() + Top();
mInitialScrollOfft = editTextTextPosition
- (mSelectorElementHeight * SELECTOR_MIDDLE_ITEM_INDEX);
mCurrentScrollOfft = mInitialScrollOfft;
updateInputTextView();
}
mSelectorIndices是⼀个数组,private final int[] mSelectorIndices = new int[SELECTOR_WHEEL_ITEM_COUNT];,再来看
SELECTOR_WHEEL_ITEM_COUNT的值:private static final int SELECTOR_WHEEL_ITEM_COUNT = 3;,这就很明显
了,mSelectorIndices⾥⾯保存的就是在屏幕上显⽰的那三个数字,⽽initializeSelectorWheel() 的作⽤就是获取到屏幕显⽰的三个数字的位置信息,以备onDraw时使⽤。那接下来就去看onDraw⽅法。
@Override
protected void onDraw(Canvas canvas) {
其他代码...
final boolean showSelectorWheel = mHideWheelUntilFocud ? hasFocus() : true;
float x = (mRight - mLeft) / 2;
9223//记录当前滑动的距离,mCurrentScrollOfft会在onTouchEvent和scrollby⾥⾯会重新被赋值。
float y = mCurrentScrollOfft;
省略绘制上下两个imagebutton按下状态的代码...
//重要的代码:⽤来绘制显⽰的⽂字。他的y值就是当前滑动的偏移量,即:mCurrentScrollOfft
// draw the lector wheel
int[] lectorIndices = mSelectorIndices;
for (int i = 0; i < lectorIndices.length; i++) {//循环绘制显⽰的⽂字
int lectorIndex = lectorIndices[i];
String scrollSelectorValue = (lectorIndex);
// Do not draw the middle item if input is visible since the input
/
/ is shown only if the wheel is static and it covers the middle
// item. Otherwi, if the ur starts editing the text via the
// IME he may e a dimmed version of the old value intermixed
entrepreneur// with the new one.万圣节英语
if ((showSelectorWheel && i != SELECTOR_MIDDLE_ITEM_INDEX) ||
(i == SELECTOR_MIDDLE_ITEM_INDEX && Visibility() != VISIBLE)) {
canvas.drawText(scrollSelectorValue, x, y, mSelectorWheelPaint);
}
y += mSelectorElementHeight;
}
省略绘制分割线的代码...
}
onDraw⾥的mCurrentScrollOfft,代表滑动的偏移量,当⼿指滑动NumberPicker的时候,他会被赋值。因此,onDraw⽅法的作⽤就是根据当前滑动偏移量来绘制屏幕上显⽰的三个数字,以及分割线等。
⾄此,绘制流程⾛完了,接下来我们关注下他是如何滑动的。看onTouchEvent ⽅法会发现,他是直接调⽤了scrollBy⽅法来滑动布局,⽽NumberPicker重写了scrollBy⽅法:
beaver@Override
public void scrollBy(int x, int y) {
int[] lectorIndices = mSelectorIndices;
//mWrapSelectorWheel作⽤:如果为true,可循环显⽰所有的数字,如果为fal,当向下或向上滑动到最后⼀个数字的时候,不可再向下或向上滑动。可以if (!mWrapSelectorWheel && y > 0
&& lectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] <= mMinValue) {
mCurrentScrollOfft = mInitialScrollOfft;//mCurrentScrollOfft的赋值
return;
}
if (!mWrapSelectorWheel && y < 0
&& lectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] >= mMaxValue) {
mCurrentScrollOfft = mInitialScrollOfft;//mCurrentScrollOfft的赋值
return;
}
mCurrentScrollOfft += y;//mCurrentScrollOfft的赋值
while (mCurrentScrollOfft - mInitialScrollOfft > mSelectorTextGapHeight) {
mCurrentScrollOfft -= mSelectorElementHeight;
decrementSelectorIndices(lectorIndices);//向下滑动
tValueInternal(lectorIndices[SELECTOR_MIDDLE_ITEM_INDEX], true);//设置NumberPicker的当前值。
if (!mWrapSelectorWheel && lectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] <= mMinValue) {
mCurrentScrollOfft = mInitialScrollOfft;//mCurrentScrollOfft的赋值
}
}
while (mCurrentScrollOfft - mInitialScrollOfft < -mSelectorTextGapHeight) {
mCurrentScrollOfft += mSelectorElementHeight;
incrementSelectorIndices(lectorIndices);//向上滑动
tValueInternal(lectorIndices[SELECTOR_MIDDLE_ITEM_INDEX], true);
if (!mWrapSelectorWheel && lectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] >= mMaxValue) {
mCurrentScrollOfft = mInitialScrollOfft;//mCurrentScrollOfft的赋值
}
}
}
把向下滑动的代码也贴出来:
private void decrementSelectorIndices(int[] lectorIndices) {
for (int i = lectorIndices.length - 1; i > 0; i--) {
lectorIndices[i] = lectorIndices[i - 1];
}
int nextScrollSelectorIndex = lectorIndices[1] - 1;
if (mWrapSelectorWheel && nextScrollSelectorIndex < mMinValue) {
nextScrollSelectorIndex = mMaxValue;
}英语演讲开场白
lectorIndices[0] = nextScrollSelectorIndex;
ensureCachedScrollSelectorValue(nextScrollSelectorIndex);
}
⼊参其实就是mSelectorIndices,即界⾯显⽰的那三个数字,把⽅法⾥的循环⾛⼀遍就会发现他的作⽤。向上滑动的代码基本差不多,这
就不再贴出。对于NumberPicker的滑动操作,还有两个Scroller:
/**
* The {@link Scroller} responsible for flinging the lector.
*/
private final Scroller mFlingScroller;additions
/**
* The {@link Scroller} responsible for adjusting the lector.
*/
private final Scroller mAdjustScroller;
⼀个负责随⼿势滑动NumberPicker,⼀个负责滑动后的调整,既然有Scroller,那肯定需要重写computeScroll()⽅法,computeScroll内
部还是调⽤了上⾯说的scrollBy⽅法,这不再赘述。
总结下,⽆论要显⽰⼏个数,显⽰在屏幕上的数字只有三个,都是通过drawText⽅法绘制上去的,⽽这三个数字的位置随⼿势滑动的距离变化⽽变化。知道他的⼤体流程之后,我们可以⾃⼰实现⼀个简单的NumberPicker。
⾃定义View简单实现NumberPicker
先来上实现的效果:
/**
* by shenmingliang1
奥巴马的演讲稿* 2018.03.28 17:44.
*/
public class MyNumberPicker extends LinearLayout {
private static final int DEFAULT_HEIGHT_OF_ITEM = 50;
private static final int TEXT_GAP = 20;
private int mCurrentScrollOfft;
private int mInitOfft;
private int mWidth;
private int[] mNumbers = new int[]{1, 2, 3};
private int mTouchSlop;
随时随地背单词
private Paint mTextPaint = new Paint();
private Paint mDividerPaint = new Paint();
public MyNumberPicker(Context context) {
this(context, null);
}

本文发布于:2023-07-13 13:50:29,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/78/1094397.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:滑动   代码   绘制
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图