android自定义view,实现竖直方向的文字功能,文字方向朝上,同时提供接口,判断当前touch的是哪个字符,并改变颜色。
由于时间比较仓促,因此没有对代码进行过多的优化,功能远远不如android的自带的TextView强大,只是继承于view,而不是textview。
主要用途:电话本的侧边快速导航等
效果图:(自定义字符串 “#ABCDEFGHIJKLMN),可以实现自定义任意字符串
view的实现:
1 import cn.carbs.verticalstraighttextview.R; 2 import android.content.Context; 3 import android.content.res.TypedArray; 4 import android.graphics.Canvas; 5 import android.graphics.Paint.Align; 6 import android.graphics.Rect; 7 import android.text.TextPaint; 8 import android.util.AttributeSet; 9 import android.util.Log; 10 import android.util.TypedValue; 11 import android.view.View; 12 /** 13 * 参考资料: 14 * http://chris.banes.me/2014/03/27/measuring -text/ 15 * http://blog.163.com/gobby_1110/blog/static/2928171520136304172378/ 16 * @author Rick.Wang 17 * 18 */ 19 public class VerticalStraightTextView extends View { 20 21 private final static int DEFAULT_TEXT_SIZE = 15; 22 private final static int DEFAULT_TEXT_COLOR = 0xFF000000; 23 private final static int DEFAULT_TEXT_COLOR_PICKED = 0xFF990000; 24 private final static int DEFAULT_CHAR_SPACE = 0; 25 26 private TextPaint mTextPaint; 27 private TextPaint mTextPaintPicked; 28 private String mText = ""; 29 30 private int mTextLength = 0; 31 private int mCharGap = 0; 32 private int mCharWidth = 0; 33 private int mCharHeight = 0; 34 35 float[] coordinates = null ; 36 37 public float[] getCoordinates(){ 38 return coordinates; 39 } 40 41 public VerticalStraightTextView(Context context) { 42 super(context); 43 init(); 44 } 45 46 public VerticalStraightTextView(Context context, AttributeSet attrs) { 47 super(context, attrs); 48 init(); 49 50 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.verticalstraighttextview ); 51 52 int n = a.getIndexCount(); 53 for (int i = 0; i < n; i++) { 54 int attr = a.getIndex(i); 55 switch (attr) { 56 case R.styleable.verticalstraighttextview_text : 57 mText = a.getString(attr); 58 if(mText == null){ 59 mText = ""; 60 break; 61 } 62 mTextLength = mText .length(); 63 break; 64 case R.styleable.verticalstraighttextview_textSize : 65 int textSize = a.getDimensionPixelOffset(R.styleable.verticalstraighttextview_textSize , DEFAULT_TEXT_SIZE); 66 if (textSize > 0) { 67 mTextPaint.setTextSize(textSize); 68 mTextPaintPicked.setTextSize(textSize); 69 } 70 break; 71 72 case R.styleable.verticalstraighttextview_charGap : 73 mCharGap = a.getDimensionPixelSize(R.styleable.verticalstraighttextview_charGap , (int) TypedValue.applyDimension( 74 TypedValue. COMPLEX_UNIT_PX, DEFAULT_CHAR_SPACE, getResources().getDisplayMetrics())); 75 break; 76 case R.styleable.verticalstraighttextview_textColor : 77 mTextPaint .setColor(a.getColor(R.styleable.verticalstraighttextview_textColor, DEFAULT_TEXT_COLOR)); 78 break; 79 case R.styleable.verticalstraighttextview_textColorPicked : 80 mTextPaintPicked .setColor(a.getColor(R.styleable.verticalstraighttextview_textColorPicked, DEFAULT_TEXT_COLOR_PICKED )); 81 break; 82 } 83 } 84 a.recycle(); 85 86 requestLayout(); 87 invalidate(); 88 } 89 90 private final void init() { 91 mTextPaint = new TextPaint(); 92 mTextPaint.setAntiAlias(true); 93 mTextPaint.setTextSize(DEFAULT_TEXT_SIZE); 94 mTextPaint.setTextAlign(Align.CENTER); 95 mTextPaintPicked = new TextPaint(mTextPaint); 96 mTextPaint.setColor(DEFAULT_TEXT_COLOR); 97 mTextPaintPicked.setColor(DEFAULT_TEXT_COLOR_PICKED); 98 } 99 100 public void setText(String text) { 101 if(text == null){ 102 text = ""; 103 } 104 if(!mText.equals(text)){ 105 mText = text; 106 mTextLength = text.length(); 107 requestLayout(); 108 invalidate(); 109 } 110 } 111 112 public void setTextSize(int size) { 113 if(mTextPaint.getTextSize() != size){ 114 mTextPaint.setTextSize(size); 115 mTextPaintPicked.setTextSize(size); 116 requestLayout(); 117 invalidate(); 118 } 119 } 120 121 public void setTextColor(int color) { 122 if(color != mTextPaint.getColor()){ 123 mTextPaint.setColor(color); 124 invalidate(); 125 } 126 } 127 128 public void setTextColorPicked(int color) { 129 if(color != mTextPaintPicked.getColor()){ 130 mTextPaintPicked.setColor(color); 131 invalidate(); 132 } 133 } 134 135 public int getCharHeight(){ 136 return mCharGap + mCharHeight; 137 } 138 139 @Override 140 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 141 Log.d("1218" , "onMeasure" ); 142 //获取字体宽度 143 float maxCharWidth = 0f; 144 for(int i = 0; i < mTextLength; i++){ 145 maxCharWidth = Math.max(mTextPaint.measureText( mText.substring(i, i+1)), maxCharWidth); 146 } 147 mCharWidth = ( int)Math.ceil(maxCharWidth); 148 149 //获取字体高度 150 Rect textBounds = new Rect(); 151 mTextPaint.getTextBounds( mText, 0, mTextLength, textBounds); 152 mCharHeight = textBounds.height(); 153 154 setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec)); 155 } 156 157 private int measureWidth(int measureSpec) { 158 int result = 0; 159 int specMode = MeasureSpec.getMode(measureSpec); 160 int specSize = MeasureSpec.getSize(measureSpec); 161 162 if (specMode == MeasureSpec.EXACTLY) { 163 result = specSize; 164 } else { 165 result = this.getPaddingLeft() + this.getPaddingRight() + mCharWidth ; 166 if (specMode == MeasureSpec.AT_MOST) { 167 result = Math. min(result, specSize); 168 } 169 } 170 return result; 171 } 172 173 private int measureHeight(int measureSpec) { 174 int result = 0; 175 int specMode = MeasureSpec.getMode(measureSpec); 176 int specSize = MeasureSpec.getSize(measureSpec); 177 178 if (specMode == MeasureSpec.EXACTLY) { 179 result = specSize; 180 } else { 181 result = getPaddingTop() + getPaddingBottom(); 182 if(mTextLength > 0){ 183 result += mTextLength * (mCharGap + mCharHeight) - mCharGap ; 184 } 185 if (specMode == MeasureSpec.AT_MOST) { 186 result = Math. min(result, specSize); 187 } 188 } 189 return result; 190 } 191 192 int pickedCharIndex = -1; 193 194 @Override 195 protected void onDraw(Canvas canvas) { 196 super.onDraw(canvas); 197 Log. d("1218", "onDraw"); 198 if(mTextLength == 0){ 199 return; 200 } 201 202 int height = getMeasuredHeight(); 203 int measuredWidth = getMeasuredWidth(); 204 205 int paddingTop = getPaddingTop(); 206 int paddingBottom = getPaddingBottom(); 207 int paddingLeft = getPaddingLeft(); 208 int paddingRight = getPaddingRight(); 209 210 //默认居中 211 int x = paddingLeft + (measuredWidth - paddingLeft - paddingRight)/2; 212 int y = 0; 213 214 int cellHeight = (height - paddingTop - paddingBottom)/ mTextLength ; 215 //TODO 可能会有bug 216 if(coordinates == null || coordinates. length != mTextLength){ 217 coordinates = new float[mTextLength + 1]; 218 } 219 coordinates[0] = 0; 220 for(int i = 0; i < mTextLength; i++){ 221 y = paddingTop + i * cellHeight + cellHeight/2; 222 coordinates[i + 1] = y + cellHeight/2; 223 if(pickedCharIndex != i){ 224 canvas.drawText( mText, i, i + 1, x, y, mTextPaint); 225 } else{ 226 canvas.drawText( mText, i, i + 1, x, y, mTextPaintPicked); 227 } 228 } 229 coordinates[mTextLength ] = height; 230 } 231 232 //y is the coordinate-Y 233 //this function can return the "touched char" 234 public int getPickedCharIndex(float[] coordinates, float y){ 235 int start = 0; 236 int end = coordinates. length - 1; 237 while (start != end - 1) { 238 int middle = (start + end) / 2; 239 if (y < coordinates[middle]) { 240 end = middle; 241 } else if (y > coordinates[middle]) { 242 start = middle; 243 } 244 } 245 return start; 246 } 247 248 public int getPickedCharIndex(float y){ 249 int start = 0; 250 int end = coordinates.length - 1; 251 while (start != end - 1) { 252 int middle = (start + end) / 2; 253 if (y < coordinates [middle]) { 254 end = middle; 255 } else if (y > coordinates[middle]) { 256 start = middle; 257 } 258 } 259 return start; 260 } 261 262 public void setPickedCharIndex(int index){ 263 if(pickedCharIndex != index){ 264 pickedCharIndex = index; 265 invalidate(); 266 } 267 } 268 }
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.setPickedCharIndex(verticalView.getPickedCharIndex(event.getY())); 17 break; 18 case MotionEvent.ACTION_MOVE: 19 verticalView.setPickedCharIndex(verticalView.getPickedCharIndex(event.getY())); 20 break; 21 case MotionEvent.ACTION_UP: 22 verticalView.setPickedCharIndex(-1); 23 break; 24 case MotionEvent.ACTION_CANCEL: 25 verticalView.setPickedCharIndex(-1); 26 break; 27 } 28 return true; 29 } 30 });
时间: 2024-10-05 06:00:00