// 递归计算出对应的index private int getLoopMappingIndex(int index) { if (index < 0) { index = index + adapter.getItemsCount(); index = getLoopMappingIndex(index); } else if (index > adapter.getItemsCount() - 1) { index = index - adapter.getItemsCount(); index = getLoopMappingIndex(index); } return index; }
private void remeasure() { if (adapter == null) { return; } measureTextWidthHeight(); // 最大Text的高度乘间距倍数得到 可见文字实际的总高度,半圆的周长 halfCircumference = (int) (itemHeight * (itemsVisible - 1)); // 整个圆的周长除以PI得到直径,这个直径用作控件的总高度 measuredHeight = (int) ((halfCircumference * 2) / Math.PI); // 求出半径 radius = (int) (halfCircumference / Math.PI); // 控件宽度,这里支持weight measuredWidth = MeasureSpec.getSize(widthMeasureSpec); // 计算两条横线和控件中间点的Y位置 firstLineY = (measuredHeight - itemHeight) / 2.0F; secondLineY = (measuredHeight + itemHeight) / 2.0F; centerY = (measuredHeight + maxTextHeight) / 2.0F - CENTERCONTENTOFFSET; // 初始化显示的item的position,根据是否loop if (initPosition == -1) { if (isLoop) { initPosition = (adapter.getItemsCount() + 1) / 2; } else { initPosition = 0; } } preCurrentIndex = initPosition; }
/** 计算最大len的Text的宽高度 */ private void measureTextWidthHeight() { Rect rect = new Rect(); for (int i = 0; i < adapter.getItemsCount(); i++) { String s1 = getContentText(adapter.getItem(i)); paintCenterText.getTextBounds(s1, 0, s1.length(), rect); int textWidth = rect.width(); if (textWidth > maxTextWidth) { maxTextWidth = textWidth; } paintCenterText.getTextBounds("\u661F\u671F", 0, 2, rect); // 星期 int textHeight = rect.height(); if (textHeight > maxTextHeight) { maxTextHeight = textHeight; } } itemHeight = lineSpacingMultiplier * maxTextHeight; }
@Override public boolean onTouchEvent(MotionEvent event) { boolean eventConsumed = gestureDetector.onTouchEvent(event); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: startTime = System.currentTimeMillis(); cancelFuture(); previousY = event.getRawY(); break; case MotionEvent.ACTION_MOVE: float dy = previousY - event.getRawY(); previousY = event.getRawY(); totalScrollY = (int) (totalScrollY + dy); // 边界处理。 if (!isLoop) { float top = -initPosition * itemHeight; float bottom = (adapter.getItemsCount() - 1 - initPosition) * itemHeight; if (totalScrollY - itemHeight * 0.3 < top) { top = totalScrollY - dy; } else if (totalScrollY + itemHeight * 0.3 > bottom) { bottom = totalScrollY - dy; } if (totalScrollY < top) { totalScrollY = (int) top; } else if (totalScrollY > bottom) { totalScrollY = (int) bottom; } } break; case MotionEvent.ACTION_UP: default: if (!eventConsumed) { float y = event.getY(); double l = Math.acos((radius - y) / radius) * radius; int circlePosition = (int) ((l + itemHeight / 2) / itemHeight); float extraOffset = (totalScrollY % itemHeight + itemHeight) % itemHeight; mOffset = (int) ((circlePosition - itemsVisible / 2) * itemHeight - extraOffset); if ((System.currentTimeMillis() - startTime) > 120) { // 处理拖拽事件 smoothScroll(ACTION.DAGGLE); } else { // 处理条目点击事件 smoothScroll(ACTION.CLICK); } } break; } invalidate(); return true; }
/** * 获取Item个数 * * @return */ public int getItemsCount() { return adapter != null ? adapter.getItemsCount() : 0; }
@Override protected void onDraw(Canvas canvas) { if (adapter == null) { return; } // 可见的item数组 Object visibles[] = new Object[itemsVisible]; // 滚动的Y值高度除去每行Item的高度,得到滚动了多少个item,即change数 change = (int) (totalScrollY / itemHeight); try { // 滚动中实际的预选中的item(即经过了中间位置的item) = 滑动前的位置 + 滑动相对位置 preCurrentIndex = initPosition + change % adapter.getItemsCount(); } catch (ArithmeticException e) { System.out.println("出错了!adapter.getItemsCount() == 0,联动数据不匹配"); } if (!isLoop) { // 不循环的情况 if (preCurrentIndex < 0) { preCurrentIndex = 0; } if (preCurrentIndex > adapter.getItemsCount() - 1) { preCurrentIndex = adapter.getItemsCount() - 1; } } else { // 循环 if (preCurrentIndex < 0) { // 举个例子:如果总数是5,preCurrentIndex = -1,那么preCurrentIndex按循环来说,其实是0的上面,也就是4的位置 preCurrentIndex = adapter.getItemsCount() + preCurrentIndex; } if (preCurrentIndex > adapter.getItemsCount() - 1) { // 同理上面,自己脑补一下 preCurrentIndex = preCurrentIndex - adapter.getItemsCount(); } } // 跟滚动流畅度有关,总滑动距离与每个item高度取余,即并不是一格格的滚动,每个item不一定滚到对应Rect里的,这个item对应格子的偏移值 int itemHeightOffset = (int) (totalScrollY % itemHeight); // 设置数组中每个元素的值 int counter = 0; while (counter < itemsVisible) { int index = preCurrentIndex - (itemsVisible / 2 - counter); // 索引值,即当前在控件中间的item看作数据源的中间,计算出相对源数据源的index值 // 判断是否循环,如果是循环数据源也使用相对循环的position获取对应的item值,如果不是循环则超出数据源范围使用""空白字符串填充,在界面上形成空白无数据的item项 if (isLoop) { index = getLoopMappingIndex(index); visibles[counter] = adapter.getItem(index); } else if (index < 0) { visibles[counter] = ""; } else if (index > adapter.getItemsCount() - 1) { visibles[counter] = ""; } else { visibles[counter] = adapter.getItem(index); } counter++; } // 中间两条横线 canvas.drawLine(0.0F, firstLineY, measuredWidth, firstLineY, paintIndicator); canvas.drawLine(0.0F, secondLineY, measuredWidth, secondLineY, paintIndicator); // 单位的Label if (label != null) { int drawRightContentStart = measuredWidth - getTextWidth(paintCenterText, label); // 靠右并留出空隙 canvas.drawText(label, drawRightContentStart - CENTERCONTENTOFFSET, centerY, paintCenterText); } counter = 0; while (counter < itemsVisible) { canvas.save(); // L(弧长)=α(弧度)* r(半径) (弧度制) // 求弧度--> (L * π ) / (π * r) (弧长X派/半圆周长) float itemHeight = maxTextHeight * lineSpacingMultiplier; double radian = ((itemHeight * counter - itemHeightOffset) * Math.PI) / halfCircumference; // 弧度转换成角度(把半圆以Y轴为轴心向右转90度,使其处于第一象限及第四象限 float angle = (float) (90D - (radian / Math.PI) * 180D); // 九十度以上的不绘制 if (angle >= 90F || angle <= -90F) { canvas.restore(); } else { String contentText = getContentText(visibles[counter]); // 计算开始绘制的位置 measuredCenterContentStart(contentText); measuredOutContentStart(contentText); float translateY = (float) (radius - Math.cos(radian) * radius - (Math.sin(radian) * maxTextHeight) / 2D); // 根据Math.sin(radian)来更改canvas坐标系原点,然后缩放画布,使得文字高度进行缩放,形成弧形3d视觉差 canvas.translate(0.0F, translateY); canvas.scale(1.0F, (float) Math.sin(radian)); if (translateY <= firstLineY && maxTextHeight + translateY >= firstLineY) { // 条目经过第一条线 canvas.save(); canvas.clipRect(0, 0, measuredWidth, firstLineY - translateY); canvas.scale(1.0F, (float) Math.sin(radian) * SCALECONTENT); canvas.drawText(contentText, drawOutContentStart, maxTextHeight, paintOuterText); canvas.restore(); canvas.save(); canvas.clipRect(0, firstLineY - translateY, measuredWidth, (int) (itemHeight)); canvas.scale(1.0F, (float) Math.sin(radian) * 1F); canvas.drawText( contentText, drawCenterContentStart, maxTextHeight - CENTERCONTENTOFFSET, paintCenterText); canvas.restore(); } else if (translateY <= secondLineY && maxTextHeight + translateY >= secondLineY) { // 条目经过第二条线 canvas.save(); canvas.clipRect(0, 0, measuredWidth, secondLineY - translateY); canvas.scale(1.0F, (float) Math.sin(radian) * 1.0F); canvas.drawText( contentText, drawCenterContentStart, maxTextHeight - CENTERCONTENTOFFSET, paintCenterText); canvas.restore(); canvas.save(); canvas.clipRect(0, secondLineY - translateY, measuredWidth, (int) (itemHeight)); canvas.scale(1.0F, (float) Math.sin(radian) * SCALECONTENT); canvas.drawText(contentText, drawOutContentStart, maxTextHeight, paintOuterText); canvas.restore(); } else if (translateY >= firstLineY && maxTextHeight + translateY <= secondLineY) { // 中间条目 canvas.clipRect(0, 0, measuredWidth, (int) (itemHeight)); canvas.drawText( contentText, drawCenterContentStart, maxTextHeight - CENTERCONTENTOFFSET, paintCenterText); int preSelectedItem = adapter.indexOf(visibles[counter]); if (preSelectedItem != -1) { selectedItem = preSelectedItem; } } else { // 其他条目 canvas.save(); canvas.clipRect(0, 0, measuredWidth, (int) (itemHeight)); canvas.scale(1.0F, (float) Math.sin(radian) * SCALECONTENT); canvas.drawText(contentText, drawOutContentStart, maxTextHeight, paintOuterText); canvas.restore(); } canvas.restore(); } counter++; } }