为了理解WheelView滚轮实现效果,我按照WheelView源码简单的依样画葫芦。
某些关键的计算:
1,滑动偏移量
2.显示的范围ItemsRange,这个非常关键,移除Item,跟新增Item需要这个范围
private ItemsRange getItemsRange() {
if (getItemHeight() == 0) {
return null;
}
int first = currentItem;
int count = 1;
while (count * getItemHeight() < getHeight()) {
first--;
count += 2; // top + bottom items
}
return new ItemsRange(first, count);
}
3.LinearLayout布中显示
private void drawItems(Canvas canvas) {
canvas.save();
int top = (currentItem - firstItem) * getItemHeight() + (getItemHeight() - getHeight()) / 2;//第一个Item的位置
canvas.translate(PADDING, -top + scrollingOffset);//画布原点。
itemsLayout.draw(canvas);
canvas.restore();
}
理解WheelView开源源码的实现思路:
1.自定义View---WheelView,通过Canvas画出一个LinearLayout的内容,能正确把Linearlayout显示出来,完成第一步。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
itemsLayout.draw(canvas);
canvas.restore();
}
@Override
public void layout(int l, int t, int r, int b) {
super.layout(l, t, r, b);
itemsLayout.layout(0, 0, itemsWidth, height);
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
Log.d("uuu10","onMeasure");
buildViewForMeasuring();//增删LinearLaout中的子类
int width = calculateLayoutWidth(widthSize, widthMode);
int height;
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
} else {
height = getDesiredHeight(itemsLayout);
if (heightMode == MeasureSpec.AT_MOST) {
height = Math.min(height, heightSize);
}
}
setMeasuredDimension(width, height);
Log.d("uuu", "onMeasure" + itemsLayout.getChildCount());
}
2.通过Touch事件(使用源码的WheelScroller辅助类)让LinearLaout移动,且每超过一个子类的高度就重设scrollingOffset,并计算移动的Item数量
@Override
public void onScroll(int distance) {
scrollingOffset += distance;
int itemHeight = getItemHeight();
int count = scrollingOffset / itemHeight;
int fixPos = scrollingOffset % itemHeight;
scrollingOffset = fixPos;
}
3.通过ItemsRange的范围更新Linear Layout的内容,增删Linear Layout子类。如果Linear Layout有增删子类的话,需要重新 layout 与measure
private void updateView() {
if (rebuildItems()) {
calculateLayoutWidth(getWidth(), MeasureSpec.EXACTLY);
layout(getWidth(), getHeight());
}
}
<pre name="code" class="html"> private boolean rebuildItems() {
boolean updated = false;
ItemsRange range = getItemsRange();
if (itemsLayout != null) {
<span style="white-space:pre"> </span> //移除不在Range中的view
int first = recycleItems(itemsLayout, firstItem, range);
updated = firstItem != first;
firstItem = first;
} else {
createItemsLayout();
updated = true;
}
if (!updated) {
updated = firstItem != range.getFirst() || itemsLayout.getChildCount() != range.getCount();
}
//firstItem在可视范围中
if (firstItem > range.getFirst() && firstItem <= range.getLast()) {
for (int i = firstItem - 1; i >= range.getFirst(); i--) {
if (!addViewItem(i, true)) {
break;
}
firstItem = i;
}
} else {
firstItem = range.getFirst();
}
//不在可视范围
int first = firstItem;
for (int i = itemsLayout.getChildCount(); i < range.getCount(); i++) {
if (!addViewItem(firstItem + i, false) && itemsLayout.getChildCount() == 0) {
first++;
}
}
firstItem = first;
Log.d("uuu", "visibleItems=" + visibleItems + " firstItem=" + firstItem + " itemsLayout.getChildCount()= " + itemsLayout.getChildCount());
return updated;
}
4.滑动结束后,调整Item位置
@Override
public void onJustify() {
if (Math.abs(scrollingOffset) > WheelScroller.MIN_DELTA_FOR_SCROLLING) {
mScroller.scroll(scrollingOffset, 0);
}
}
Wheel类的整个流程:
1.初始化:onMeasue --> layout --> buildViewForMeasureing --> onDraw--->updateView
buildViewForMeasureing --- 初始化linearLayout 内容
onDraw --- 画linearLayout 的Canvas
2.滑动的时候:onScroll-->invalidate-->onDraw--->updateview --->recycleItems(如果内容变化--->linearLayout layout).--->onJustify
onScroll--- 设置偏移量,与当前位置currentItem.
invalidate----滑动的后刷新
onDraw----画linearLayout 的最新位置
updateview----
recycleItems----获取ItemsRange,移除已出ItemsRange的View,再增加进入ItemsRange的View
源码:http://download.csdn.net/detail/lzo4a/9606895