android自定义view,实现竖直方向的文字功能,文字方向朝上,同时提供接口,判断当前touch的是哪个字符,并改变颜色。
由于时间比较仓促,因此没有对代码进行过多的优化,功能远远不如android的自带的TextView强大,只是继承于view,而不是textview。
主要用途:电话本的侧边快速导航等
效果图:(自定义字符串 “#ABCDEFGHIJKLMN),可以实现自定义任意字符串
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 可能会有bug218 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-Y235 //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,说明正在touchEvent267 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文件中)
12 3 4 5 6 7
布局文件引入此自定义view:
1
在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 @Override13 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 });