Android学习小Demo(11)一个显示行线的自定义EditText

本文介绍了一种自定义EditText的方法,使其能在每一行下方绘制线条,即使在无文字内容的情况下也能保持一致的视觉效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天在处理一个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" />

结束。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值