今天在处理一个EditText的时候,想着把EditText做成像一本作业本上的纸一样,每一行都可以由线条隔开,具体效果如下:

1)最开始的思路

一开始的想法是很简单的,找出每一行的高度,然后一行一行地画线不就好了吗,代码如下:

viewHeight = getMeasuredHeight();
viewWidth = getMeasuredWidth();
lineHeight = getLineHeight();
int maxLines = viewHeight / lineHeight + 1;
int i = 0;
int currentLineHeight = 0;
while(i < maxLines){
currentLineHeight += lineHeight;
canvas.drawLine(0, currentLineHeight, viewWidth, currentLineHeight, mPaint);
i++;
}

但是出来的效果一看,高度很明显不够,如下:

看起来好像是高度的问题,那就每一行的高度都加一点呗,再试试:

lineHeight = getLineHeight() + 5;

再来看一下效果,

前面几行好像效果不错呀,但是到后面又挤在一起了,我估计这里面的原因应该是getLineHeight返回来的值不是标准的,而是随着内容的变化而不同,看一下它的函数说明,如下:

/**
* @return the height of one standard line in pixels. Note that markup
* within the text can cause individual lines to be taller or shorter
* than this height, and the layout may contain additional first-
* or last-line padding.
*/
public int getLineHeight() {
return FastMath.round(mTextPaint.getFontMetricsInt(null) * mSpacingMult + mSpacingAdd);
}

果然是这样,那么通过这个getLineHeight来表示每一行的高度就不可行了。

在android提供的sample里面,有一个是NotePad的小demo,在里面它实现了为EditText里面的每一行添加一条下划线,其代码如下:

public static class LinedEditText extends EditText {

...
@Override
protected void onDraw(Canvas canvas) {
int count = getLineCount();
Rect r = mRect;
Paint paint = mPaint; for (int i = 0; i < count; i++) {
int baseline = getLineBounds(i, r); canvas.drawLine(r.left, baseline + 1, r.right, baseline + 1, paint);
} super.onDraw(canvas);
}
}

好,我们就用它这个来试试,看效果

不错哦!但是发现一个问题,后面没有字符的它就不划线了,这是因为它划线的基础在于getLineCount这个函数,定义如下:

/**
* Return the number of lines of text, or 0 if the internal Layout has not
* been built.
*/
public int getLineCount() {
return mLayout != null ? mLayout.getLineCount() : 0;
}

就是说,它返回来的值是跟内容有关的的,而getLineBounds的取值则是基于getLineCount的,定义如下(看第一句注释):

/**
* Return the baseline for the specified line (0...getLineCount() - 1)
* If bounds is not null, return the top, left, right, bottom extents
* of the specified line in it. If the internal Layout has not been built,
* return 0 and set bounds to (0, 0, 0, 0)
* @param line which line to examine (0..getLineCount() - 1)
* @param bounds Optional. If not null, it returns the extent of the line
* @return the Y-coordinate of the baseline
*/
public int getLineBounds(int line, Rect bounds)

所以,只有有内容的行才会画下划线,可是我们的目标是没有内容也想划一条线,如何是好呢?

我的想法一样很简单,在屏幕上显示的线条其实是有 限的,那么就分成两部分,前面一部分是有内容的,就拿上面NotePad这种办法画,如果线条的数目已填满这个区域了(其实是达到预先定义的最大数目 了),则不必自己再去画,反之,如果还差几样没有画,那么就根据前面已经画出来的平均高度,把剩下的几条线也给画上,因为当输入内容的时 候,EditText其实是一直在刷新的,所以自然而然,新添加的内容的线条也是应用上面NotePad那种方法来画的,所以画出来的线也是符合要求的。 这个自定义EditText的完整代码如下:

package com.lms.todo.views;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.widget.EditText; /**
* @author Linmiansheng
*/
public class CustomEditText extends EditText { // private static final String TAG = "com.lms.todo.views.CustomEditText";
private Rect mRect;
private Paint mPaint; private final int padding = 10; private int lineHeight;
private int viewHeight,viewWidth; public CustomEditText(Context context) {
this(context, null);
} public CustomEditText(Context context, AttributeSet attrs) {
this(context, attrs, 0);
} public CustomEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
} private void init(Context context, AttributeSet attrs) {
mRect = new Rect();
mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.BLACK);
mPaint.setAntiAlias(true); setFocusable(true);
setFocusableInTouchMode(true);
} @Override
protected void onDraw(Canvas canvas) {
int count = getLineCount();
Rect r = mRect;
Paint paint = mPaint;
int lineHeight = 0;
int i = 0;
while (i < count) {
lineHeight = getLineBounds(i, r);
canvas.drawLine(r.left, lineHeight + padding, r.right, lineHeight + padding,
paint);
i++;
}
int maxLines = 15;
int avgHeight = lineHeight / count;
int currentLineHeight = lineHeight; while(i < maxLines){
currentLineHeight = currentLineHeight + avgHeight + padding;
canvas.drawLine(r.left, currentLineHeight, r.right, currentLineHeight, paint);
i++;
}
super.onDraw(canvas);
}
}

不过有一点要注意的就是,这个padding的设置,也就是每一行的高度,其实跟我们在应用的时候设置的字体大小也是有一定的关系的,所以,真正应用的话,可能要去调一下。

在xml布局中的使用如下:

<com.lms.todo.views.CustomEditText
android:id="@+id/etContent"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_weight="8"
android:background="#00000000"
android:gravity="top"
android:inputType="textMultiLine"
android:padding="5dip"
android:textSize="20sp" />

结束。

Android学习小Demo一个显示行线的自定义EditText的更多相关文章

  1. Android学习小Demo(19)利用Loader来实时接收短信

    之前写过一篇文章<Android学习小Demo(13)Android中关于ContentObserver的使用>,在里面利用ContentOberver去监測短信URI内容的变化.我们先来 ...

  2. Android学习笔记-构建一个可复用的自定义BaseAdapter

    转载自http://www.runoob.com/w3cnote/android-tutorial-customer-baseadapter.html   作者:coder-pig 本节引言: 如题, ...

  3. Android学习小Demo(20)关于Fragment的应用

    Android在3.0之后引入了Fragment的概念,我推測其想法可能仅仅是想更好地兼容大屏幕或者平板的开发,由于大屏幕能够展示很多其它的内容,而内容一多,逻辑有可能就乱,而利用Fragment,则 ...

  4. Android学习小Demo(21)ListView的联动选择

    在日常的App开发中,尤其是在开发生活服务的应用上,非常多时候,我们会须要联动地展现省市区的数据等,需求大概例如以下: 1)展现全部省份 2)当点击某省份的时候,在二级菜单上展现此省份以下所属的城市列 ...

  5. RPC框架学习+小Demo实例

    一.什么是RPC协议? 全称:远程过程调度协议 效果:使消费者向调用本地方法一样调用远程服务方法,对使用者透明 目前常用:Dubbo.Thirft.Sofa.... 功能: 建立远程通信(socket ...

  6. Android学习系列(3)--App自动更新之自定义进度视图和内部存储

    友好的视觉感知和稳定的不出错表现,来自于我们追求美感和考虑的全面性,博客园从技术的角度,一直我都很欣赏.这篇文章是android开发人员的必备知识,是我特别为大家整理和总结的,不求完美,但是有用. 这 ...

  7. 高强度学习训练第二天总结:Opencv+Android+CameraView小demo

    前言:网上已经有很多人将Opencv集成进Android项目中了.因此我只给大家看Gradle文件和项目目录. 1.gradle 三个gradle script // Top-level build ...

  8. QT学习小demo之LightMD(MarkDown编辑器)

    很早之前就有了写一个类似Windows记事本的想法,加上最近也刚好在学编译原理,所以就想把两者结合起来,于是就打算结合MarkDown,开发一款MarkDown编辑器. 不过由于我之前一直使用的是Ja ...

  9. android学习小例子——验证码倒计时按钮

    1.activity_main.xml: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/andro ...

随机推荐

  1. 在easyui dialog的子页面内如何关闭弹窗

    因项目需要在dialog中添加滚动条,所以就在div中加了iframe: <div id="applyRefundDialog" style="display:no ...

  2. width:100%缩小窗口时背景图片出现空白bug

    页面容器(#wrap)与页面头部(#header )为100%宽度.而内容的容器(#page)为固定宽度960px.浏览窗口缩小而小于内容层宽度时会产生宽度理解上的差异.如下图所示窗口宽度大于内容层宽 ...

  3. C# 窗体间传值方法大汇总(转)

    第一种方法:创建一个类,里面声明用于存储接收的字段.传的时候存储于字段中,要用的时候,直接类名.字段名 进行调用.(这种方法传递是双向的) 第二种方法:1.在Form1里定义 public strin ...

  4. linker command failed with exit code 1

    这种问题,通常出现在添加第三方库文件或者多人开发时. 这种问题一般是找不到文件而导致的链接错误. 我们可以从如下几个方面着手排查. 1.以如下错误为例,如果是多人开发,你同步完成后发现出现如下的错误. ...

  5. ExtJS 4 Grids 详解

    Grid Panel是ExtJS最常用的组件之一,它的功能非常丰富,提供了非常便捷的方法执行排序,分组,编辑数据. Basic Grid Panel 基本表格面板 让我们创建一个简单的表格,这有创建和 ...

  6. opencv 2.46与visual studio 2012 配置方法

    一开学就搞实训,还是没学过的图像处理.痛苦啊!图像处理时一般使用Matlab中的图像工具箱,或者是C/C++和OpenCV结合使用.以前看过一些关于opencv的文章,没想到现在要用上了. 把搭建开发 ...

  7. 转:SSDB:快速取代redis的nosql

    原文来自于:http://hao.jobbole.com/ssdb%EF%BC%9A%E5%BF%AB%E9%80%9F%E5%8F%96%E4%BB%A3redis%E7%9A%84nosql/ S ...

  8. Js使用word书签填充内容

    Js使用word书签填充内容 1.在模板文件中需要填充的地方插入书签 填充内容为:(|光标所在处) 填写书签名,点击添加完成: 2.使用js打开模板,获取书签位置,填充数据: function pri ...

  9. int 和String之间的互转

    int -> String int i=12345;String s="";第一种方法:s=i+"";第二种方法:s=String.valueOf(i); ...

  10. 几个国外FMX网站 good

    FireMonkey X –  Amazing overview of FireMonkey FMX Feeds – All your FireMonkey news in one place FMX ...