wheelview是一款模拟ios的缩放滑动的控件,
wheelview 继承于View父布局,整个布局是画出来的,
一、构造函数初始化
1.1构造函数进行了以下的操作,对CENTER_CONTENT_OFFSET(偏移量)进行适配,
DisplayMetrics dm = getResources().getDisplayMetrics();
float density = dm.density; // 屏幕密度比(0.75/1.0/1.5/2.0/3.0)
if (density < 1) {//根据密度不同进行适配
CENTER_CONTENT_OFFSET = 2.4F;
} else if (1 <= density && density < 2) {
CENTER_CONTENT_OFFSET = 3.6F;
} else if (1 <= density && density < 2) {
CENTER_CONTENT_OFFSET = 4.5F;
} else if (2 <= density && density < 3) {
CENTER_CONTENT_OFFSET = 6.0F;
} else if (density >= 3) {
CENTER_CONTENT_OFFSET = density * 2.5F;
}
1.2 lineSpacingMultiplier,间距倍数,绘制的两条线的距离
/**
* 判断间距是否在1.0-4.0之间
*/
private void judgeLineSpace() {
if (lineSpacingMultiplier < 1.0f) {
lineSpacingMultiplier = 1.0f;
} else if (lineSpacingMultiplier > 4.0f) {
lineSpacingMultiplier = 4.0f;
}
}
1.3 initLoopView是初始化 消息、手势,以及画笔的内容。
private void initLoopView(Context context) {
this.context = context;
handler = new MessageHandler(this);
gestureDetector = new GestureDetector(context, new LoopViewGestureListener(this));
gestureDetector.setIsLongpressEnabled(false);
isLoop = true;
totalScrollY = 0;
initPosition = -1;
initPaints();
}
二、测量方法
测量的方法有以下几个reMeasure,
1.1 reMeasure,第一步实现了对单个item的宽度和高度的测量,这个方法是在setAdapter和onMeasure时执行了,当数据更新时是要重新测试高度的
1.2
三、绘制方法
由于这个是继承于View的,所以所有的条目以及横线都是绘制生成的,
3.1.1 计算滚动了多少个Item,是从当前所在位置计算的
change = (int) (totalScrollY / itemHeight);
3.1.2 绘制中间两条线
//绘制中间两条横线
if (dividerType == DividerType.WRAP) {//横线长度仅包裹内容
float startX;
float endX;
if (TextUtils.isEmpty(label)) {//隐藏Label的情况
startX = (measuredWidth - maxTextWidth) / 2 - 12;
} else {
startX = (measuredWidth - maxTextWidth) / 4 - 12;
}
if (startX <= 0) {//如果超过了WheelView的边缘
startX = 10;
}
endX = measuredWidth - startX;
canvas.drawLine(startX, firstLineY, endX, firstLineY, paintIndicator);
canvas.drawLine(startX, secondLineY, endX, secondLineY, paintIndicator);
} else {
//Todo 画线
canvas.drawLine(0.0F, firstLineY, measuredWidth, firstLineY, paintIndicator);
canvas.drawLine(0.0F, secondLineY+100, measuredWidth, secondLineY+100, paintIndicator);
}
四、滚动的方法
4.1 onTouch方法
使用了gestureDetector手势,
4.1.1 MotionEvent.ACTION_DOWN,
初始化开始时间,执行cancelFuture()方法,停止滚动,获取停止坐标previousY = event.getRawY();
4.1.2 MotionEvent.ACTION_MOVE:
通过previousY - event.getRawY(),计算出来移动的Y的距离,totalScrollY记录移动的总的距离,这里是有计算缩放的方法的,应该是利用圆的半径来计算的。
if (!eventConsumed) {//未消费掉事件
/**
*@describe <关于弧长的计算>
*
* 弧长公式: L = α*R
* 反余弦公式:arccos(cosα) = α
* 由于之前是有顺时针偏移90度,
* 所以实际弧度范围α2的值 :α2 = π/2-α (α=[0,π] α2 = [-π/2,π/2])
* 根据正弦余弦转换公式 cosα = sin(π/2-α)
* 代入,得: cosα = sin(π/2-α) = sinα2 = (R - y) / R
* 所以弧长 L = arccos(cosα)*R = arccos((R - y) / R)*R
*/
float y = event.getY();
double L = Math.acos((radius - y) / radius) * radius;
//item0 有一半是在不可见区域,所以需要加上 itemHeight / 2
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);
}
}
五、各个值的分析
totalScrollY:表示滚动的距离,初始化为0,向上滚动增加 dy,向上dy为正,向下为负
itemHeightOffset:totalScrollY % itemHeight,是滚动距离的相对于itemHeight的偏移量,是多余的部门,算弧长的时候要减去这个值。
top:-initPosition * itemHeight,计算出来顶部的距离
bottom:计算出来底部的最低位置