android中自定义view---实现竖直方向的文字功能,文字方向朝上,同时提供接口,判断当前touch的是哪个字符,并改变颜色

android自定义view,实现竖直方向的文字功能,文字方向朝上,同时提供接口,判断当前touch的是哪个字符,并改变颜色。

由于时间比较仓促,因此没有对代码进行过多的优化,功能远远不如android的自带的TextView强大,只是继承于view,而不是textview。

主要用途:电话本的侧边快速导航等

效果图:(自定义字符串 “#ABCDEFGHIJKLMN),可以实现自定义任意字符串

android中自定义view---实现竖直方向的文字功能,文字方向朝上,同时提供接口,判断当前touch的是哪个字符,并改变颜色

view的实现:

  1 package cn.carbs.verticalstraighttextview.view;
  2 
  3 import cn.carbs.verticalstraighttextview.R;
  4 import android.content.Context;
  5 import android.content.res.TypedArray;
  6 import android.graphics.Canvas;
  7 import android.graphics.Paint.Align;
  8 import android.graphics.Rect;
  9 import android.text.TextPaint;
 10 import android.util.AttributeSet;
 11 import android.util.Log;
 12 import android.util.TypedValue;
 13 import android.view.View;
 14 /**
 15  * 参考资料:
 16  *         http://chris.banes.me/2014/03/27/measuring-text/
 17  *         http://blog.163.com/gobby_1110/blog/static/2928171520136304172378/
 18  * @author Rick.Wang
 19  *
 20  */
 21 public class VerticalStraightTextView extends View {
 22     
 23     private final static int DEFAULT_TEXT_SIZE = 15;  
 24     private final static int DEFAULT_TEXT_COLOR = 0xFF000000;
 25     private final static int DEFAULT_TEXT_COLOR_PICKED = 0xFF990000;
 26     private final static int DEFAULT_CHAR_SPACE = 0;
 27     
 28     private TextPaint mTextPaint;  
 29     private TextPaint mTextPaintPicked;
 30     private String mText = "";
 31     
 32     private int mTextLength = 0;
 33     private int mCharGap = 0;
 34     private int mCharWidth = 0;
 35     private int mCharHeight = 0;
 36     
 37     private int currPickedCharIndex = -1;
 38     
 39     float[] coordinates = null;
 40     
 41     public float[] getCoordinates(){
 42         return coordinates;
 43     }
 44     
 45     public VerticalStraightTextView(Context context) {  
 46         super(context);  
 47         init();  
 48     }  
 49   
 50     public VerticalStraightTextView(Context context, AttributeSet attrs) {  
 51         super(context, attrs);  
 52         init();  
 53   
 54         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.verticalstraighttextview);  
 55         
 56         int n = a.getIndexCount();  
 57         for (int i = 0; i < n; i++) {
 58             int attr = a.getIndex(i);  
 59             switch (attr) {  
 60             case R.styleable.verticalstraighttextview_text:
 61                 mText = a.getString(attr);
 62                 if(mText == null){
 63                     mText = "";
 64                     break;
 65                 }
 66                 mTextLength = mText.length();
 67                 break;  
 68             case R.styleable.verticalstraighttextview_textSize:  
 69                 int textSize = a.getDimensionPixelOffset(R.styleable.verticalstraighttextview_textSize, DEFAULT_TEXT_SIZE);  
 70                 if (textSize > 0) {
 71                     mTextPaint.setTextSize(textSize);
 72                     mTextPaintPicked.setTextSize(textSize);
 73                 }
 74                 break;  
 75 
 76             case R.styleable.verticalstraighttextview_charGap:
 77                 mCharGap = a.getDimensionPixelSize(R.styleable.verticalstraighttextview_charGap, (int) TypedValue.applyDimension(  
 78                         TypedValue.COMPLEX_UNIT_PX, DEFAULT_CHAR_SPACE, getResources().getDisplayMetrics())); 
 79                 break;  
 80             case R.styleable.verticalstraighttextview_textColor:  
 81                 mTextPaint.setColor(a.getColor(R.styleable.verticalstraighttextview_textColor, DEFAULT_TEXT_COLOR));
 82                 break;  
 83             case R.styleable.verticalstraighttextview_textColorPicked:  
 84                 mTextPaintPicked.setColor(a.getColor(R.styleable.verticalstraighttextview_textColorPicked, DEFAULT_TEXT_COLOR_PICKED));
 85                 break;  
 86             }  
 87         }  
 88         a.recycle();  
 89         
 90         requestLayout();  
 91         invalidate();  
 92     }  
 93   
 94     private final void init() {  
 95         mTextPaint = new TextPaint();  
 96         mTextPaint.setAntiAlias(true);  
 97         mTextPaint.setTextSize(DEFAULT_TEXT_SIZE);  
 98         mTextPaint.setTextAlign(Align.CENTER);
 99         mTextPaintPicked = new TextPaint(mTextPaint);
100         mTextPaint.setColor(DEFAULT_TEXT_COLOR);
101         mTextPaintPicked.setColor(DEFAULT_TEXT_COLOR_PICKED);
102     }  
103   
104     public void setText(String text) {
105         if(text == null){
106             text = "";
107         }
108         if(!mText.equals(text)){
109             mText = text;  
110             mTextLength = text.length();
111             requestLayout();  
112             invalidate();  
113         }
114     }  
115   
116     public void setTextSize(int size) {
117         if(mTextPaint.getTextSize() != size){
118             mTextPaint.setTextSize(size);  
119             mTextPaintPicked.setTextSize(size);
120             requestLayout();  
121             invalidate();  
122         }
123     }  
124   
125     public void setTextColor(int color) {
126         if(color != mTextPaint.getColor()){
127             mTextPaint.setColor(color);
128             invalidate();  
129         }
130     }
131     
132     public void setTextColorPicked(int color) {
133         if(color != mTextPaintPicked.getColor()){
134             mTextPaintPicked.setColor(color);
135             invalidate();  
136         }
137     }
138     
139     public int getCharHeight(){
140         return mCharGap + mCharHeight;
141     }
142   
143     @Override  
144     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
145         Log.d("1218", "onMeasure");
146         //获取字体宽度
147         float maxCharWidth = 0f;
148         for(int i = 0; i < mTextLength; i++){
149              maxCharWidth = Math.max(mTextPaint.measureText(mText.substring(i, i+1)), maxCharWidth);
150         }
151         mCharWidth = (int)Math.ceil(maxCharWidth);
152         
153         //获取字体高度
154         Rect textBounds = new Rect();
155         mTextPaint.getTextBounds(mText, 0, mTextLength, textBounds);
156         mCharHeight = textBounds.height();
157         
158         setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
159     }
160     
161     private int measureWidth(int measureSpec) {  
162         int result = 0;  
163         int specMode = MeasureSpec.getMode(measureSpec);  
164         int specSize = MeasureSpec.getSize(measureSpec);  
165   
166         if (specMode == MeasureSpec.EXACTLY) {  
167             result = specSize;
168         } else {  
169             result = this.getPaddingLeft() + this.getPaddingRight() + mCharWidth;
170             if (specMode == MeasureSpec.AT_MOST) {
171                 result = Math.min(result, specSize);  
172             }
173         }
174         return result;  
175     }  
176   
177     private int measureHeight(int measureSpec) {  
178         int result = 0;  
179         int specMode = MeasureSpec.getMode(measureSpec);  
180         int specSize = MeasureSpec.getSize(measureSpec);  
181 
182         if (specMode == MeasureSpec.EXACTLY) {  
183             result = specSize;  
184         } else {  
185             result = getPaddingTop() + getPaddingBottom();
186             if(mTextLength > 0){
187                 result += mTextLength * (mCharGap + mCharHeight) - mCharGap;
188             }
189             if (specMode == MeasureSpec.AT_MOST) {  
190                 result = Math.min(result, specSize);  
191             }  
192         }  
193         return result;
194     }  
195 
196     @Override  
197     protected void onDraw(Canvas canvas) {  
198         super.onDraw(canvas);  
199         Log.d("1218", "onDraw");
200         if(mTextLength == 0){
201             return;
202         }
203         
204         int height = getMeasuredHeight();
205         int measuredWidth = getMeasuredWidth();
206         
207         int paddingTop = getPaddingTop();
208         int paddingBottom = getPaddingBottom();
209         int paddingLeft = getPaddingLeft();
210         int paddingRight = getPaddingRight();
211         
212         //默认居中
213         int x = paddingLeft + (measuredWidth - paddingLeft - paddingRight)/2;
214         int y = 0;
215         
216         int cellHeight = (height - paddingTop - paddingBottom)/ mTextLength;
217         //TODO 可能会有bug
218         if(coordinates == null || coordinates.length != mTextLength){
219             coordinates = new float[mTextLength + 1];
220         }
221         coordinates[0] = 0;
222         for(int i = 0; i < mTextLength; i++){
223             y = paddingTop + i * cellHeight + cellHeight/2;
224             coordinates[i + 1] = y + cellHeight/2;
225             if(currPickedCharIndex != i){
226                 canvas.drawText(mText, i, i + 1, x, y, mTextPaint);
227             }else{
228                 canvas.drawText(mText, i, i + 1, x, y, mTextPaintPicked);
229             }
230         }
231         coordinates[mTextLength] = height;
232     }
233     
234     //y is the coordinate-Y
235     //this function can return the "touched char"
236     public int getPickedCharIndex(float[] coordinates, float y){
237         int start = 0;
238         int end = coordinates.length - 1;
239         while (start != end - 1) {
240             int middle = (start + end) / 2;
241             if (y < coordinates[middle]) {
242                 end = middle;
243             } else if (y > coordinates[middle]) {
244                 start = middle;
245             }
246         }
247         return start;
248     }
249     
250     
251     /***************************************
252      * 
253      *         +---------------+  <-- Y == coordinates[0]
254      *         |        #        |
255      *         |---------------|            coordinates[1]
256      *         |        A        |
257      *         |---------------|            coordinates[2]
258      *         |        B        |
259      *         |---------------|            coordinates[3]
260      *         |        C        |
261      *         +---------------|            coordinates[4]
262      ***************************************/
263     
264     public int getPickedCharIndex(float y){
265         //优化查询
266         //如果当前的>-1,说明正在touchEvent
267         if(currPickedCharIndex > -1){
268             if(coordinates[currPickedCharIndex] < y && y < coordinates[currPickedCharIndex+1]){
269                 return currPickedCharIndex;
270             }
271         }
272         
273         int start = 0;
274         int end = coordinates.length - 1;
275         while (start != end - 1) {
276             int middle = (start + end) / 2;
277             if (y < coordinates[middle]) {
278                 end = middle;
279             } else if (y > coordinates[middle]) {
280                 start = middle;
281             }
282         }
283         return start;
284     }
285     
286     
287     public void setCurrPickedCharIndex(int index){
288         if(currPickedCharIndex != index){
289             currPickedCharIndex = index;
290             invalidate();
291         }
292     }
293 
294 }  

style文件的定义:(将此代码写入values文件夹下的styles.xml文件中)

1 <declare-styleable name="verticalstraighttextview">
2         <attr name= "text" format ="string" />
3         <attr name= "textColor" format ="reference|color" />
4         <attr name= "textColorPicked" format="reference|color" />
5         <attr name= "textSize" format="reference|dimension" />
6         <attr name= "charGap" format ="reference|dimension" />
7     </declare-styleable >

布局文件引入此自定义view:

1 <cn.carbs.verticalstraighttextview.view.VerticalStraightTextView
2         android:id="@+id/kk"
3         android:layout_width="wrap_content"
4         android:padding="5dp"
5         android:layout_height="fill_parent"
6         android:background="#33333333"
7         app:textSize="20sp"
8         app:text= "#ABCEDFGHIJKLMN" />

在activity中的使用:

 1 verticalView = (VerticalStraightTextView)this.findViewById(R.id.kk);
 2         
 3         verticalView.setOnClickListener(new View.OnClickListener() {
 4             @Override
 5             public void onClick(View v) {
 6                 Toast.makeText(getApplicationContext(), "onclick", Toast.LENGTH_SHORT).show();
 7             }
 8         });
 9        
10         verticalView.setOnTouchListener(new View.OnTouchListener() {
11             
12             @Override
13             public boolean onTouch(View view, MotionEvent event) {
14                 switch(event.getAction()){
15                 case MotionEvent.ACTION_DOWN:
16                     verticalView.setCurrPickedCharIndex(verticalView.getPickedCharIndex(event.getY()));
17                     break;
18                 case MotionEvent.ACTION_MOVE:
19                     verticalView.setCurrPickedCharIndex(verticalView.getPickedCharIndex(event.getY()));
20                     break;
21                 case MotionEvent.ACTION_UP:
22                     verticalView.setCurrPickedCharIndex(-1);
23                     break;
24                 case MotionEvent.ACTION_CANCEL:
25                     verticalView.setCurrPickedCharIndex(-1);
26                     break;
27                 }
28                 return true;
29             }
30         });