簡介
製作一個垂直列表非常常見也很簡單,如下所示:
但是我們如何建立一個可以圓弧型的列表呢?就像下面的效果:
如何??
當然,我們必須定製一個視圖來建立一個那樣的列表。我決定使用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; } |