GossipView:圓圈布局的自定義view
當我們想展示一個數組形式的數據的時候,要么是使用列表的形式,要么是使用網格的形式,今天我們介紹一種奇葩的形式,圓圈形式:
注意,周邊的扇形是可以點擊的。如果使用現有控件,要實現起來是有難度的,所以我們就采用了自定義View的方式。
下面是原理以及使用方法,整個項目可以到這里下載 http://jcodecraeer.com/a/opensource/2014/1115/1988.html
繪制
主要是外部扇形以及內部圓圈背景的繪制,最里面其實還有個很細的圓圈,那個其實是用在當想顯示加載效果的時候,但這不是重點略去不講。
內部圓圈背景的繪制很簡單直接使用Drawable 的draw方法,前提是先設置好bounds(下面會講到).
mInnerBackGroud.draw(canvas);
而外部扇形的繪制是分別調用drawArc方法(也許應該取別的名字,和canvas的方法沖突了)完成的:
for(int i = 0;i < mPieceNumber ; i++){ drawArc(i , canvas); }
有多少個扇形調用多少次。
drawArc定義如下:
/** 按索引值繪制扇區,第一個扇區的中心位于3點鐘方向*/ public void drawArc(int index , Canvas canvas){ int startdegree = mPieceDegree * (index) - (mPieceDegree - mDividerDegree) / 2; if(index == mSelectIndex){ mOuterArcPaint.setColor(0xFFcacccc); }else{ mOuterArcPaint.setColor(outArcColor[index]); } float radious = ((float)mWidth - (float)outArctrokeWidth) / 2 - padding ; float midDegree = startdegree + ( mPieceDegree - mDividerDegree) /2 ; double x = radious * Math.cos(midDegree * Math.PI/180); double y = radious * Math.sin(midDegree * Math.PI/180); x = x + getOriginal().x; y = y + getOriginal().y; canvas.drawArc(mOuterArcRectangle, startdegree, mPieceDegree - mDividerDegree, false, mOuterArcPaint); Rect rect = new Rect(); mOuterTextPaint.getTextBounds(items.get(index).getTitle(), 0, items.get(index).getTitle().length(), rect); int txWidth = rect.width(); int txHeight = rect.height(); canvas.drawText(items.get(index).getTitle(), (int)x - txWidth/2, (int)y + txHeight/2, mOuterTextPaint); }
空間計算:
根據onMeasure方法中的寬和高計算出不同區域的基本參數,比如外扇形的厚度outArctrokeWidth,外扇形文字的大小mOuterTextPaint,外扇形的半徑mOuterArcRadius,外扇形繪制的矩形區域mOuterArcRectangle;以及內部圓圈mInnerBackGroud的bounds。
按下效果:
外部扇形的按下效果是根據不同狀態下設置畫筆的顏色來實現的:
如果某一個扇形的索引剛好等于選中的mSelectIndex
,則設置按下的顏色:
if(index == mSelectIndex){ mOuterArcPaint.setColor(0xFFcacccc); }else{ mOuterArcPaint.setColor(outArcColor[index]); }
而內部的圓圈部分則直接采用selector圖片的方式:
if(mSelectIndex == -1){ Log.i(TAG,"mSelectIndex = "+mSelectIndex); mInnerBackGroud.setState(PRESSED_FOCUSED_STATE_SET); }else{ mInnerBackGroud.setState(EMPTY_STATE_SET); }
mSelectIndex
== -1 表示選中的是最中間的圓圈,若為真設置Drawable mInnerBackGroud的狀態為PRESSED_FOCUSED_STATE_SET,反之為EMPTY_STATE_SET,因為mInnerBackGroud其實是由selector得來的Drawable ,所以只要設置了不同的狀態就會繪出不同的圖片效果。
另外為了處理扇形和內部圓圈的按下效果,我們必須判斷當前到底是點中了那部分
@Override public boolean onTouchEvent(MotionEvent event) { if(event.getAction() == MotionEvent.ACTION_DOWN) { mSelectIndex = getTouchArea(new Point(event.getX() , event.getY())); this.invalidate(); Log.i(TAG ,"mSelectIndex =" +mSelectIndex); //mSelectIndex = -1; }else if(event.getAction() == MotionEvent.ACTION_UP && event.getAction() != MotionEvent.ACTION_CANCEL){ int upIndex = getTouchArea(new Point(event.getX() , event.getY())); if(mListener != null){ mListener.onPieceClick(upIndex); } mSelectIndex = -2; this.invalidate(); }else if(event.getAction() == MotionEvent.ACTION_CANCEL){ mSelectIndex = -2; this.invalidate(); } return true; }
在ACTION_DOWN事件中,調用getTouchArea來判斷當前選中的是什么
,getTouchArea可能返回的有三種值:
-1 選中的是最中間的圓圈
-2 選中的是扇形之間的間隔部分
整數:選中的是某個扇形。
當
ACTION_UP
事件發生之后我們通知UI重繪,并且調用onPieceClick
通知注冊的Lisetner我選擇了什么。
監聽選中了什么
GossipView.OnPieceClickListener
public interface OnPieceClickListener { void onPieceClick(int whitchPiece); }
這個不用解釋了吧,最常見的觀察者模式。
下面是使用方法:
xml中:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.jcodecraeer.gossipview.MainActivity" > <com.jcodecraeer.gossipview.GossipView android:id="@+id/gossipview" android:layout_width="fill_parent" android:layout_height="fill_parent" /> </RelativeLayout>
Activity中:
... GossipView gossipView = (GossipView)findViewById(R.id.gossipview); String [] strs = {"安卓","微軟","蘋果","谷歌","百度","騰訊"} ;final List<GossipItem> items =new ArrayList<GossipItem>(); for(int i = 0; i < strs.length; i++) { GossipItem item = new GossipItem(strs[i],3); items.add(item); } gossipView.setItems(items); gossipView.setNumber(3); gossipView.setOnPieceClickListener( new GossipView.OnPieceClickListener(){ @Override public void onPieceClick(int index) { if(index != -1 && index != -2) { Toast.makeText(MainActivity.this, "你選擇了" + items.get(index).getTitle(), 300).show(); } } });
....</pre>
GossipItem
的定義:package com.jcodecraeer.gossipview; public class GossipItem { private String title; private int index; public GossipItem (String title,int index){ this.title =title; this.index = index; }public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; }
}</pre>
轉載請注明出處:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1115/1986.html