android中自定义view---实现竖直方向的文字功能,文字方向朝上,同时提供接口,判断当前touch的是哪个字符,并改变颜色
android自定义view,实现竖直方向的文字功能,文字方向朝上,同时提供接口,判断当前touch的是哪个字符,并改变颜色。
由于时间比较仓促,因此没有对代码进行过多的优化,功能远远不如android的自带的TextView强大,只是继承于view,而不是textview。
主要用途:电话本的侧边快速导航等
效果图:(自定义字符串 “#ABCDEFGHIJKLMN),可以实现自定义任意字符串

view的实现:
package cn.carbs.verticalstraighttextview.view; import cn.carbs.verticalstraighttextview.R;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint.Align;
import android.graphics.Rect;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
/**
* 参考资料:
* http://chris.banes.me/2014/03/27/measuring-text/
* http://blog.163.com/gobby_1110/blog/static/2928171520136304172378/
* @author Rick.Wang
*
*/
public class VerticalStraightTextView extends View { private final static int DEFAULT_TEXT_SIZE = 15;
private final static int DEFAULT_TEXT_COLOR = 0xFF000000;
private final static int DEFAULT_TEXT_COLOR_PICKED = 0xFF990000;
private final static int DEFAULT_CHAR_SPACE = 0; private TextPaint mTextPaint;
private TextPaint mTextPaintPicked;
private String mText = ""; private int mTextLength = 0;
private int mCharGap = 0;
private int mCharWidth = 0;
private int mCharHeight = 0; private int currPickedCharIndex = -1; float[] coordinates = null; public float[] getCoordinates(){
return coordinates;
} public VerticalStraightTextView(Context context) {
super(context);
init();
} public VerticalStraightTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init(); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.verticalstraighttextview); int n = a.getIndexCount();
for (int i = 0; i < n; i++) {
int attr = a.getIndex(i);
switch (attr) {
case R.styleable.verticalstraighttextview_text:
mText = a.getString(attr);
if(mText == null){
mText = "";
break;
}
mTextLength = mText.length();
break;
case R.styleable.verticalstraighttextview_textSize:
int textSize = a.getDimensionPixelOffset(R.styleable.verticalstraighttextview_textSize, DEFAULT_TEXT_SIZE);
if (textSize > 0) {
mTextPaint.setTextSize(textSize);
mTextPaintPicked.setTextSize(textSize);
}
break; case R.styleable.verticalstraighttextview_charGap:
mCharGap = a.getDimensionPixelSize(R.styleable.verticalstraighttextview_charGap, (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_PX, DEFAULT_CHAR_SPACE, getResources().getDisplayMetrics()));
break;
case R.styleable.verticalstraighttextview_textColor:
mTextPaint.setColor(a.getColor(R.styleable.verticalstraighttextview_textColor, DEFAULT_TEXT_COLOR));
break;
case R.styleable.verticalstraighttextview_textColorPicked:
mTextPaintPicked.setColor(a.getColor(R.styleable.verticalstraighttextview_textColorPicked, DEFAULT_TEXT_COLOR_PICKED));
break;
}
}
a.recycle(); requestLayout();
invalidate();
} private final void init() {
mTextPaint = new TextPaint();
mTextPaint.setAntiAlias(true);
mTextPaint.setTextSize(DEFAULT_TEXT_SIZE);
mTextPaint.setTextAlign(Align.CENTER);
mTextPaintPicked = new TextPaint(mTextPaint);
mTextPaint.setColor(DEFAULT_TEXT_COLOR);
mTextPaintPicked.setColor(DEFAULT_TEXT_COLOR_PICKED);
} public void setText(String text) {
if(text == null){
text = "";
}
if(!mText.equals(text)){
mText = text;
mTextLength = text.length();
requestLayout();
invalidate();
}
} public void setTextSize(int size) {
if(mTextPaint.getTextSize() != size){
mTextPaint.setTextSize(size);
mTextPaintPicked.setTextSize(size);
requestLayout();
invalidate();
}
} public void setTextColor(int color) {
if(color != mTextPaint.getColor()){
mTextPaint.setColor(color);
invalidate();
}
} public void setTextColorPicked(int color) {
if(color != mTextPaintPicked.getColor()){
mTextPaintPicked.setColor(color);
invalidate();
}
} public int getCharHeight(){
return mCharGap + mCharHeight;
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Log.d("1218", "onMeasure");
//获取字体宽度
float maxCharWidth = 0f;
for(int i = 0; i < mTextLength; i++){
maxCharWidth = Math.max(mTextPaint.measureText(mText.substring(i, i+1)), maxCharWidth);
}
mCharWidth = (int)Math.ceil(maxCharWidth); //获取字体高度
Rect textBounds = new Rect();
mTextPaint.getTextBounds(mText, 0, mTextLength, textBounds);
mCharHeight = textBounds.height(); setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
} private int measureWidth(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else {
result = this.getPaddingLeft() + this.getPaddingRight() + mCharWidth;
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
}
return result;
} private int measureHeight(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else {
result = getPaddingTop() + getPaddingBottom();
if(mTextLength > 0){
result += mTextLength * (mCharGap + mCharHeight) - mCharGap;
}
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
}
return result;
} @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Log.d("1218", "onDraw");
if(mTextLength == 0){
return;
} int height = getMeasuredHeight();
int measuredWidth = getMeasuredWidth(); int paddingTop = getPaddingTop();
int paddingBottom = getPaddingBottom();
int paddingLeft = getPaddingLeft();
int paddingRight = getPaddingRight(); //默认居中
int x = paddingLeft + (measuredWidth - paddingLeft - paddingRight)/2;
int y = 0; int cellHeight = (height - paddingTop - paddingBottom)/ mTextLength;
//TODO 可能会有bug
if(coordinates == null || coordinates.length != mTextLength){
coordinates = new float[mTextLength + 1];
}
coordinates[0] = 0;
for(int i = 0; i < mTextLength; i++){
y = paddingTop + i * cellHeight + cellHeight/2;
coordinates[i + 1] = y + cellHeight/2;
if(currPickedCharIndex != i){
canvas.drawText(mText, i, i + 1, x, y, mTextPaint);
}else{
canvas.drawText(mText, i, i + 1, x, y, mTextPaintPicked);
}
}
coordinates[mTextLength] = height;
} //y is the coordinate-Y
//this function can return the "touched char"
public int getPickedCharIndex(float[] coordinates, float y){
int start = 0;
int end = coordinates.length - 1;
while (start != end - 1) {
int middle = (start + end) / 2;
if (y < coordinates[middle]) {
end = middle;
} else if (y > coordinates[middle]) {
start = middle;
}
}
return start;
} /***************************************
*
* +---------------+ <-- Y == coordinates[0]
* | # |
* |---------------| coordinates[1]
* | A |
* |---------------| coordinates[2]
* | B |
* |---------------| coordinates[3]
* | C |
* +---------------| coordinates[4]
***************************************/ public int getPickedCharIndex(float y){
//优化查询
//如果当前的>-1,说明正在touchEvent
if(currPickedCharIndex > -1){
if(coordinates[currPickedCharIndex] < y && y < coordinates[currPickedCharIndex+1]){
return currPickedCharIndex;
}
} int start = 0;
int end = coordinates.length - 1;
while (start != end - 1) {
int middle = (start + end) / 2;
if (y < coordinates[middle]) {
end = middle;
} else if (y > coordinates[middle]) {
start = middle;
}
}
return start;
} public void setCurrPickedCharIndex(int index){
if(currPickedCharIndex != index){
currPickedCharIndex = index;
invalidate();
}
} }
style文件的定义:(将此代码写入values文件夹下的styles.xml文件中)
<declare-styleable name="verticalstraighttextview">
<attr name= "text" format ="string" />
<attr name= "textColor" format ="reference|color" />
<attr name= "textColorPicked" format="reference|color" />
<attr name= "textSize" format="reference|dimension" />
<attr name= "charGap" format ="reference|dimension" />
</declare-styleable >
布局文件引入此自定义view:
<cn.carbs.verticalstraighttextview.view.VerticalStraightTextView
android:id="@+id/kk"
android:layout_width="wrap_content"
android:padding="5dp"
android:layout_height="fill_parent"
android:background="#33333333"
app:textSize="20sp"
app:text= "#ABCEDFGHIJKLMN" />
在activity中的使用:
verticalView = (VerticalStraightTextView)this.findViewById(R.id.kk);
verticalView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getApplicationContext(), "onclick", Toast.LENGTH_SHORT).show();
}
});
verticalView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent event) {
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
verticalView.setCurrPickedCharIndex(verticalView.getPickedCharIndex(event.getY()));
break;
case MotionEvent.ACTION_MOVE:
verticalView.setCurrPickedCharIndex(verticalView.getPickedCharIndex(event.getY()));
break;
case MotionEvent.ACTION_UP:
verticalView.setCurrPickedCharIndex(-1);
break;
case MotionEvent.ACTION_CANCEL:
verticalView.setCurrPickedCharIndex(-1);
break;
}
return true;
}
});
android中自定义view---实现竖直方向的文字功能,文字方向朝上,同时提供接口,判断当前touch的是哪个字符,并改变颜色的更多相关文章
- android开发:Android 中自定义View的应用
大家好我们今天的教程是在Android 教程中自定义View 的学习,对于初学着来说,他们习惯了Android 传统的页面布局方式,如下代码: <?xml version="1.0&q ...
- android中自定义view构造函数ContentItemView(Context context, AttributeSet paramAttributeSet)的用处
自己定义一个view <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:a ...
- Android中自定义View和自定义动画
Android FrameWork 层给我们提供了很多界面组件,但是在实际的商业开发中这些组件往往并不能完全满足我们的需求,这时候我们就需要自定义我们自己的视图和动画. 我们要重写系统的View就必须 ...
- Android中自定义View的MeasureSpec使用
有时,Android系统控件无法满足我们的需求,因此有必要自定义View.具体方法参见官方开发文档:http://developer.android.com/guide/topics/ui/custo ...
- Android 中的View与ViewGroup
Android重点知识--View和ViewGroup与自定义控件 作者:丁明祥 邮箱:2780087178@qq.com 一.基础 ViewGroup 参考资料: Android 手把手教您自定义V ...
- 详解实现Android中实现View滑动的几种方式
注: 本文提到的所有三种滑动方式的完整demo:ScrollDemo 1. 关于View我们需要知道的 (1)什么是View? Android中的View类是所有UI控件的基类(Base class) ...
- android中实现view可以滑动的六种方法续篇(二)
承接上一篇,上一篇中讲解了实现滑动的第五种方法,如果你还没读过,可点击下面链接: http://www.cnblogs.com/fuly550871915/p/4985482.html 这篇文章现在来 ...
- Android的自定义View及View的绘制流程
目标:实现Android中的自定义View,为理清楚Android中的View绘制流程“铺路”. 想法很简单:从一个简单例子着手开始编写自定义View,对ViewGroup.View类中与绘制View ...
- Android之自定义View学习(一)
Android之自定义View学习(一) Canvas常用方法: 图片来源 /** * Created by SiberiaDante on 2017/6/3. */ public class Bas ...
随机推荐
- 那些年我们学过的PHP黑魔法
那些年我们学过的PHP黑魔法 提交 我的评论 加载中 已评论 那些年我们学过的PHP黑魔法 2015-04-10 Sco4x0 红客联盟 红客联盟 红客联盟 微信号 cnhonker_huc 功能介绍 ...
- 《Linux内核设计与实现》读书笔记(十九)- 可移植性
linux内核的移植性非常好, 目前的内核也支持非常多的体系结构(有20多个). 但是刚开始时, linux也只支持 intel i386 架构, 从 v1.2版开始支持 Digital Alpha, ...
- 优秀的UI插件
妹子UI: http://amazeui.org/getting-started 百度图表: http://echarts.baidu.com/ 手机UI库:https://github.com/ac ...
- Spec模板
Spec模板 一.概述 1.项目背景 图书馆在正常运营中面对大量书籍.读者信息以及两者间相互联系产生的借书信息.还书信息.现有的人工记录方法既效率低又错误过多,大大影响了图书馆 ...
- Android开发学习总结(六)—— APK反编译
学习和开发Android应用有一段时间了,今天写一篇博客总结一下Android的apk文件反编译.我们知道,Android应用开发完成之后,我们最终都会将应用打包成一个apk文件,然后让用户通过手机或 ...
- Linux下jvm、tomcat、mysql、log4j优化配置笔记
小菜一直对操作系统心存畏惧,以前也很少接触,这次创业购买了Linux云主机,由于木有人帮忙,只能自己动手优化服务器了.... 小菜的云主机配置大致为:centeos6(32位),4核心cpu,4G内存 ...
- Linux系列笔记 - vim相关记录
一.常用到的vim命令 这里只简单记录常用到的命令,后面会有自己记录的命令,但有些可能不常用. 常规模式: gg 跳到文件头 shift+g 跳到文件尾 行数+gg 跳到指定行 如:123gg 跳到1 ...
- Javascript中递归造成的堆栈溢出及解决方案
关于堆栈的溢出问题,在Javascript日常开发中很常见,Google了下,相关问题还是比较多的.本文旨在描述如何解决此类问题. 首先看一个实例(当然你可以使用更容易的方式实现,这里我们仅探讨递归) ...
- C#课外实践——校园二手平台(技术篇1)
前面分享了这次的课外实践的心得,这次,就分享一下从这次的课外实践的过程中学到的知识技能吧.虽然有句话说的好,不要做没有准备的战争,但是,我想说的是,生活中有很多的事情是不允许我们有准备的.遇到事情必须 ...
- Hibernate 的saveOrUpdate方法(转)
hibernate提供了saveOrUpdate的方法来进行数据库的操作.hibernate会根据对象的状态决定是insert还是update,其根本是通过xml文件中unsaved-value来确定 ...