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的是哪个字符,并改变颜色的更多相关文章

  1. android开发:Android 中自定义View的应用

    大家好我们今天的教程是在Android 教程中自定义View 的学习,对于初学着来说,他们习惯了Android 传统的页面布局方式,如下代码: <?xml version="1.0&q ...

  2. android中自定义view构造函数ContentItemView(Context context, AttributeSet paramAttributeSet)的用处

    自己定义一个view <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:a ...

  3. Android中自定义View和自定义动画

    Android FrameWork 层给我们提供了很多界面组件,但是在实际的商业开发中这些组件往往并不能完全满足我们的需求,这时候我们就需要自定义我们自己的视图和动画. 我们要重写系统的View就必须 ...

  4. Android中自定义View的MeasureSpec使用

    有时,Android系统控件无法满足我们的需求,因此有必要自定义View.具体方法参见官方开发文档:http://developer.android.com/guide/topics/ui/custo ...

  5. Android 中的View与ViewGroup

    Android重点知识--View和ViewGroup与自定义控件 作者:丁明祥 邮箱:2780087178@qq.com 一.基础 ViewGroup 参考资料: Android 手把手教您自定义V ...

  6. 详解实现Android中实现View滑动的几种方式

    注: 本文提到的所有三种滑动方式的完整demo:ScrollDemo 1. 关于View我们需要知道的 (1)什么是View? Android中的View类是所有UI控件的基类(Base class) ...

  7. android中实现view可以滑动的六种方法续篇(二)

    承接上一篇,上一篇中讲解了实现滑动的第五种方法,如果你还没读过,可点击下面链接: http://www.cnblogs.com/fuly550871915/p/4985482.html 这篇文章现在来 ...

  8. Android的自定义View及View的绘制流程

    目标:实现Android中的自定义View,为理清楚Android中的View绘制流程“铺路”. 想法很简单:从一个简单例子着手开始编写自定义View,对ViewGroup.View类中与绘制View ...

  9. Android之自定义View学习(一)

    Android之自定义View学习(一) Canvas常用方法: 图片来源 /** * Created by SiberiaDante on 2017/6/3. */ public class Bas ...

随机推荐

  1. 利用sklearn进行tfidf计算

    转自:http://blog.csdn.net/liuxuejiang158blog/article/details/31360765?utm_source=tuicool 在文本处理中,TF-IDF ...

  2. [Xamarin] 簡單使用AlertDialog (转帖)

    這東西跟Toast 很像,有方便提示的作用 像是Windows 上面的MessageBox 或是 Javascript 的 Alert 會先阻斷使用者並且下一個決定 很簡單我就不贅述,基本上透過 Al ...

  3. IQueryable和IQueryProvider初尝

    前言 相信大家对Entity Framework一定不陌生,我相信其中Linq To Sql是其最大的亮点之一,但是我们一直使用到现在却不曾明白内部是如何实现的,今天我们就简单的介绍IQueryabl ...

  4. Linq学习之操作符

    一.环境搭建 下面将逐步搭建我们学习的环境,这个环境不仅仅是这次需要使用,以后的教程一样需要使用这个环境.所以请大家务必按照 搭建这里的环境否则会影响你后面的学习. 我们用到的几张表 通知消息表: 用 ...

  5. Dynamic CRM 2013学习笔记(四十四)CRM技术支持

    有时我们经常遇到一些CRM的问题,一时又无法解决,这时我们可能要找下外援,下面列出一些基本的技术支持.   1. CRM 论坛 https://community.dynamics.com/crm/f ...

  6. WebApi 服务监控

    本文主要介绍在请求WebApi时,监控Action执行的时间,及Action传递的参数值,以及Http请求头信息.采用log4net记录监控日志,通过日志记录的时间方便我们定位哪一个Action执行的 ...

  7. 在.sln文件中设置Visual Studio默认启动项目的简单方法

    昨天在一台电脑上用git新签出一个项目进行build,却出现一堆编译错误,而在原先的开发机上build无任何错误.对比分析后发现,开发机上VS的启动项目(startup project)与这台电脑上的 ...

  8. [MSSQL]SQL疑难杂症实战记录-巧妙利用PARTITION分组排名递增特性解决合并连续相同数据行

    问题提出 先造一些测试数据以说明题目: DECLARE @TestData TABLE(ID INT,Col1 VARCHAR(20),Col2 VARCHAR(20)) INSERT INTO @T ...

  9. C#,Java,C -循环冗余检验:CRC-16-CCITT查表法

    C#代码 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ...

  10. 如果下次做模板,我就使用Nvelocity

    普通Replace模板做法 很多人在做邮件模板.短信模板的时候,都是使用特殊标识的字符串进行占位,然后在后台代码中进行Replace字符串,如果遇到表格形式的内容,则需要在后台进行遍历数据集合,进行字 ...