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:計算出來底部的最低位置