一. 前言 常用的应用场景:城市搜索的侧边栏,通讯录的侧边栏
Github地址:XLLetterView
二. 自定义View 1. 自定义用到的属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 private Paint paint;private int text_color = Color.WHITE;private int select_text_color = Color.MAGENTA;private int text_size = 60 ;private int space_hor = 10 ;private int space_ver = 10 ;private int max_letter_height = 0 ;private LetterChangeListener listener;private String lastLetter;private String currentLetter;
2. 在values文件夹下自定义属性 1 2 3 4 5 6 7 8 9 10 <?xml version="1.0" encoding="utf-8"?> <resources > <declare-styleable name ="XLLetterView" > <attr name ="text_color" format ="color" /> <attr name ="select_text_color" format ="color" /> <attr name ="text_size" format ="integer" /> <attr name ="space_hor" format ="integer" /> <attr name ="space_ver" format ="integer" /> </declare-styleable > </resources >
3. 创建构造方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public XLLetterView (Context context, @Nullable AttributeSet attrs) { super (context, attrs); if (attrs != null ){ TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.XLLetterView); text_color = typedArray.getColor(R.styleable.XLLetterView_text_color,text_color); select_text_color = typedArray.getColor(R.styleable.XLLetterView_select_text_color,select_text_color); text_size = typedArray.getInteger(R.styleable.XLLetterView_text_size,text_size); space_hor = typedArray.getInteger(R.styleable.XLLetterView_space_hor,space_hor); space_ver = typedArray.getInteger(R.styleable.XLLetterView_space_ver,space_ver); text_size = PxUtil.spToPx(text_size,context); space_hor = PxUtil.spToPx(space_hor,context); space_ver = PxUtil.spToPx(space_ver,context); typedArray.recycle(); } init(); } private void init () { paint = new Paint(); paint.setColor(text_color); paint.setTextSize(text_size); }
4. onMeasure和onDraw方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 @Override protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) { super .onMeasure(widthMeasureSpec, heightMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int wrap_width = 0 ; int wrap_height = 0 ; for (int i = 0 ; i < Constant.letter.length; i++) { Rect rect = new Rect(); paint.getTextBounds(Constant.letter[i],0 ,1 ,rect); Log.d(Constant.TAG,Constant.letter[i]+":" +(rect.bottom - rect.top)); wrap_width = Math.max(wrap_width,rect.right - rect.left); max_letter_height = Math.max(max_letter_height,rect.bottom - rect.top); } wrap_width += space_hor * 2 ; wrap_height += max_letter_height * Constant.letter.length + space_ver * (Constant.letter.length + 1 ); setMeasuredDimension( (widthMode == MeasureSpec.EXACTLY) ? widthSize : wrap_width, (heightMode == MeasureSpec.EXACTLY) ? heightSize : wrap_height ); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 @Override protected void onDraw (Canvas canvas) { super .onDraw(canvas); int width = getWidth(); int height = getHeight(); Paint.FontMetrics fontMetrics = paint.getFontMetrics(); float base_space = fontMetrics.bottom; Log.d(Constant.TAG,"top:" +fontMetrics.top); Log.d(Constant.TAG,"ascent:" +fontMetrics.ascent); Log.d(Constant.TAG,"descent:" +fontMetrics.descent); Log.d(Constant.TAG,"bottom:" +fontMetrics.bottom); Log.d(Constant.TAG,"leading:" +fontMetrics.leading); for (int i = 0 ; i < Constant.letter.length; i++) { Rect rect = new Rect(); paint.getTextBounds(Constant.letter[i],0 ,1 ,rect); int letter_width = rect.right - rect.left; int letter_height = rect.bottom - rect.top; float x; float y; x = (width - letter_width) >> 1 ; float distance = ((fontMetrics.bottom - fontMetrics.top) / 2 ) - fontMetrics.bottom; float location = ((max_letter_height - letter_height) >> 1 ) + (letter_height >> 1 ) + distance; y = space_ver + location + (space_ver + max_letter_height) * i; if (TextUtils.equals(currentLetter,Constant.letter[i])){ paint.setColor(select_text_color); canvas.drawText( Constant.letter[i], x, y, paint ); paint.setColor(text_color); }else { canvas.drawText( Constant.letter[i], x, y, paint ); } } }
5. 触摸事件,回调触摸到的字母 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 @Override public boolean onTouchEvent (MotionEvent event) { float x = event.getX(); float y = event.getY(); float position = (y / (space_ver + max_letter_height)); Log.d(Constant.TAG,"position:" +position); if (position >= 0 && position < Constant.letter.length) { currentLetter = Constant.letter[(int ) Math.ceil(position)-1 ]; }else { currentLetter = "" ; } switch (event.getAction()) { case MotionEvent.ACTION_DOWN: if (TextUtils.equals(currentLetter,lastLetter)){ return true ; } break ; case MotionEvent.ACTION_UP: lastLetter = currentLetter; return true ; } if (listener != null ) { listener.currentLetter(currentLetter); } invalidate(); return true ; } public interface LetterChangeListener { void currentLetter (String letter) ; } public void setLetterChangeListener (LetterChangeListener listener) { this .listener = listener; }
三. 具体的使用 首先需要添加依赖,添加依赖之后,在布局文件中这样调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android ="http://schemas.android.com/apk/res/android" xmlns:app ="http://schemas.android.com/apk/res-auto" xmlns:tools ="http://schemas.android.com/tools" android:layout_width ="match_parent" android:layout_height ="match_parent" tools:context =".MainActivity" > <swu.xl.xlletterview.XLLetterView android:id="@+id/letter" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@color/colorPrimaryDark" android:layout_centerInParent="true" app:text_size="12" /> </RelativeLayout >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class MainActivity extends AppCompatActivity { @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); XLLetterView letterView = findViewById(R.id.letter); letterView.setLetterChangeListener(new XLLetterView.LetterChangeListener() { @Override public void currentLetter (String letter) { Toast.makeText(MainActivity.this , "当前的字母:" +letter, Toast.LENGTH_SHORT).show(); } }); letterView.setCurrentLetter("S" ); } }