Android自訂View之用觀察者模式寫自訂監聽事件以及常用豎直型字母索引欄的寫法
概述:
目前,豎直索引欄還是很流行的,、美團、手機通訊錄等各種常用軟體都要用到它。
Demo
寫一個自訂View,利用觀察者模式,自訂其中的點擊事件。
public class MySlider extends View { private int width; private int height; private float x; private float y; private float letterSize; private int index = -1; private char[] letters = {'A','B','C','D','E','F','G', 'H','I','J','K','L','M','N','O','P','Q','R','S','T','U', 'V','W','X','Y','Z','#'}; private Paint mPaintText; private Paint mPaintTextSel; public MySlider(Context context) { super(context); } //定義一個介面,作為觀察者 public interface OnItemListener{ public void onItemSelected(int index,String content); } //觀察者進行通訊的對象 private OnItemListener listener; public void setOnItemListener(OnItemListener listener){ this.listener = listener; } public MySlider(Context context, AttributeSet attrs) { super(context, attrs); mPaintText = new Paint(); mPaintText.setColor(Color.BLACK); //讓字母置中排列 mPaintText.setTextAlign(Paint.Align.CENTER); mPaintTextSel = new Paint(); mPaintTextSel.setColor(Color.RED); mPaintTextSel.setTextAlign(Paint.Align.CENTER); } public int getIndex() { return index; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec); height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec); setMeasuredDimension(width, height); //setPaintText務必在此寫,因為運行了onMeasure()後才得到height值 mPaintText.setTextSize(height / 27-20); mPaintTextSel.setTextSize(height / 27-20); } /** *自訂點擊事件 */ @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_MOVE: //此處故意不加break,只要滑動螢幕上字母就會促發點擊事件 case MotionEvent.ACTION_DOWN: //得到觸碰點的x,y座標 x = event.getX(); y = event.getY(); //根據座標得到點擊的字母 if(x>width-mPaintText.measureText(A)*2){ index = (int)(y/(height/27)); //index可能會大於等於27,會造成letters數組下標越界的錯誤,所以此處先作判斷,防止其大於26 if(index>=27){ index = 26; } Log.d(letter, letters[index] + ); //調用listener對象的onItemSelected()函數,傳入index if(listener!=null) { listener.onItemSelected(index,letters[index]+); } //讓主線程重繪 invalidate(); return true; } break; default: break; } return super.onTouchEvent(event); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); letterSize = mPaintText.measureText(A); for(int i=0;i<27;i++){ //根據index值判斷字母是否被點擊到,被點擊的字母是紅色,否則是黑色 if(index==i) { //將字母依次向下繪出 canvas.drawText(letters[i] + , width - letterSize, height / 27 * (i + 1), mPaintTextSel); }else{ canvas.drawText(letters[i] + , width - letterSize, height / 27 * (i + 1), mPaintText); } } }}
主活動的寫法:
public class TimerActivity extends Activity { private TextView mTextViewLetter; private MySlider mySlider; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_timer); mTextViewLetter = (TextView) findViewById(R.id.textView_letter); mySlider = (MySlider) findViewById(R.id.my_slider); mySlider.setOnItemListener(new MySlider.OnItemListener() { @Override public void onItemSelected(int index, String content) { //如果點擊到字母,就列印該字母 mTextViewLetter.setText(content); } }); }}
activity_timer布局:
結果示範: