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. ( 转)基于.NET平台常用的框架整理

    自从学习.NET以来,优雅的编程风格,极度简单的可扩展性,足够强大开发工具,极小的学习曲线,让我对这个平台产生了浓厚的兴趣,在工作和学习中也积累了一些开源的组件,就目前想到的先整理于此,如果再想到,就 ...

  2. CBA 赛程的笔记 - 北京首钢

    2014-11-01 19:35 北京首钢 103:89 广东宏远 结束 技术统计  发挥不错,打的比较好! 2014-11-05 19:35 八一双鹿 89:100 北京首钢 结束 技术统计  第一 ...

  3. Java和C#中的接口对比(有你不知道的东西)

    1.与Java不同,C#中的接口不能包含字段(Field). 在java中,接口中可以包含字段,但是这些字段隐式地是static和final的.而C#不允许接口中有字段,编译器在编译时就会提示错误(如 ...

  4. GCD使用dispatch_group_notify、dispatch_group_enter、dispatch_group_leave处理多线程同步操作

    一.简介 dispatch_group_enter:通知group,下面的任务马上要放到group中执行了. dispatch_group_leave:通知group,任务完成了,该任务要从group ...

  5. sql server创建备份计划

    对于备份计划,在sql server中微软提供了相应的功能集,通过Maintenance Plans向导可以对数据库进行相关维护工作. 通过下图的向导,可以进行如定期备份和清除工作. 前提是安装介质包 ...

  6. C语言实现快速排序

    我觉得冒泡排序是比较简单的: 所以今天我们实现一个叫做快速排序的: Problem 你想要将(4,3,5,1,2)排序成(1,2,3,4,5) 你决定使用最简单的快速排序: Solution 首先,打 ...

  7. JAVA学习中Swing概述中的JFrame学习

    package com.swing; import java.awt.Color;import java.awt.Container;import java.awt.event.WindowAdapt ...

  8. Leetcode 26 Remove Duplicates from Sorted Array STL

    题目本身是去重 由于我很懒,所以用了STL库里的unique函数来去重,小伙伴们可以考虑自己实现去重的函数,其实并不复杂. class Solution { public: int removeDup ...

  9. Python核心编程(切片索引的更多内容)

    # coding=utf8 s = 'abcde' i = -1 for i in range(-1, -len(s), -1): print(s[:i]) # 结果 ''' abcd abc ab ...

  10. SAFS Distilled --- 9 April 2015 to 16 April 2015

    In the org.safs.model, the class Component stores: information of this component's name reference of ...