簡介
製作一個垂直列表非常常見也很簡單,如下所示:
但是我們如何建立一個可以圓弧型的列表呢?就像下面的效果:
如何??
當然,我們必須定製一個視圖來建立一個那樣的列表。我決定使用SurfaceView 來建立這個列表視圖。經過分析之後,該問題包含如下內容:
-
如何在圓弧內繪製圖片?
-
如何讓列表按照圓弧的軌跡滑動?
如何在圓弧內繪製圖片?
假設有一個帶中心點的圓弧(centerX, centerY)和半徑r。一個點P有相對於中心點的角度。那麼下面的公式就可以描述出該問題:
基於此,我們可以很方便的在圓弧上繪製一個清單項目.
如何在圓弧上滑動列表視圖?
當使用者滑動列表時,會出現該問題。我們如何更新每個清單項目的角度?確切的說,我們必須詳細記錄滑動的角度。之後添加到當前列表視圖中每一項的角度中。為瞭解決這個要求。我決定使用GestureDetector 來控制這個事件。同時,在這個類中,我主要考慮的是如何詳細記錄滑動的角度(函數名如下).
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)
基於e2, distanceX, 和 distanceY,滑動的角度將被詳細記錄到下面的圖片中:
基於此圖片,我們給出了下面的公式
=> scroll angle = β - α
代碼
我講建立一個展示的列表作為範例。列表中的圖片來自於資源檔夾。你可以參考我上傳的原始碼.
| 代碼如下 |
複製代碼 |
|
int[] playerDrawableResourceIds = new int[] { R.drawable.ronaldo,
R.drawable.zindance, R.drawable.congvinh, R.drawable.huynhduc,
R.drawable.gerrard, R.drawable.nagatomo, R.drawable.messi,
R.drawable.minhphuong, R.drawable.neymar, R.drawable.ronaldo_beo,
R.drawable.ronaldinho, R.drawable.xavi }; |
如何在圓弧中繪製圖片?
首先,建立一個清單項目的類CircleDrawItem.
| 代碼如下 |
複製代碼 |
public class CircleDrawItem {
public Bitmap mIconBitmap;
public double mAngle;
} |
mIconBitmap: 清單項目的位元影像.
mAngle: 每個清單項目的弧度。這個弧度在旋轉清單項目的時候會被更新.
因為該圓弧將被在螢幕左側展示。所以圓心將會把X座標設定為負數。同時,Y座標將被設定為螢幕中心的Y座標值.
| 代碼如下 |
複製代碼 |
mCenterX = (int) (-Global.dp * 200);
mCenterY = (int) (Global.dh / 2.0f);
mRadius = (int) (450 * Global.dp);
mStartAngleInRadian = Math.PI / 4; |
用Global 的值進行計算:
| 代碼如下 |
複製代碼 |
Global.dw = getResources().getDisplayMetrics().widthPixels;
Global.dh = getResources().getDisplayMetrics().heightPixels;
Global.dp = Global.density / 1.5f; |
這個圓弧保有CircleDrawItem的數組列表。第一項的弧度為PI/4。每個清單項目的間隔是PI/10.第一項的弧度和兩項間的弧度可以根據你的需求進行修改。
| 代碼如下 |
複製代碼 |
for (int i = 0; i < playerDrawableResourceIds.length; i++) {
CircleDrawItem circleDrawItem = new CircleDrawItem();
circleDrawItem.mIconBitmap = decodeSampledBitmapFromResource(
getResources(), playerDrawableResourceIds[i], 50, 50);
circleDrawItem.mAngle = mStartAngleInRadian - i * Math.PI / 10;
datas.add(circleDrawItem);
} |
我建立了一個線程,以便將每個資料項目繪製到圓弧中。
| 代碼如下 |
複製代碼 |
protected void draw() {
Canvas canvas = getHolder().lockCanvas();
if (canvas == null) {
return;
}
canvas.save();
canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
paint.setAntiAlias(true);
for (int i = 0; i < datas.size(); i++) {
canvas.save();
CircleDrawItem item = datas.get(i);
double x = mCenterX + Math.cos(item.mAngle) * mRadius;
double y = mCenterY - Math.sin(item.mAngle) * mRadius;
canvas.drawBitmap(item.mIconBitmap,
(int) x - item.mIconBitmap.getWidth() / 2, (int) y
- item.mIconBitmap.getHeight() / 2, paint);
canvas.restore();
}
canvas.restore();
getHolder().unlockCanvasAndPost(canvas);
}
|
如何讓列表在圓弧中滑動?
引用OnGestureListener 並重載onScroll()函數。
| 代碼如下 |
複製代碼 |
|
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
float tpx = e2.getX();
float tpy = e2.getY();
float disx = (int) distanceX;
float disy = (int) distanceY;
double scrollAngle = calculateScrollAngle(tpx, tpy, tpx + disx, tpy
+ disy);
for (int i = 0; i < datas.size(); i++) {
datas.get(i).mAngle += scrollAngle;
}
return true;
} |
計算滑動角度的方法如下:
| 代碼如下 |
複製代碼 |
|
private double calculateScrollAngle(float px1, float py1, float px2,
float py2) {
double radian1 = Math.atan2(py1, px1);
double radian2 = Math.atan2(py2, px2);
double diff = radian2 - radian1;
return diff;
} |