以前、EditTextに下線を表示する実装例を紹介しましたが、いくつか問題が残っていることがわかったため、ここでさらに探究を続けます。原文リンク:http://www.cnblogs.com/ayqy/p/3599414.html
0. もう一つのバグ
もし線が描画されない場合は、`canvas.drawLine`メソッドの最後の引数である`paint`の線幅(stroke width)に関するバグの可能性があります。Android 2.3.5では線幅0.5で正常に描画されますが、Android 4.2.2では線幅0.5だと描画されません。具体的には以下の通りです:
// 線幅に注意。Android 2.3.5では0.5で正常に描画されますが、Android 4.2.2では0.5だと描画されません lineWidth = 1.0f; // デフォルトの幅を1.0にする
それでも描画されない場合は、開始点の座標を確認してください。描画時に開始点が可視範囲内にない場合、線全体が表示されません。TextViewの可視領域は、コントロールが占有する矩形領域です。具体的には以下の通りです:
// 描画時に開始点が可視範囲内にない場合、線全体が表示されません。TextViewの可視領域は、コントロールが占有する矩形領域です canvas.drawLine(padL // startX , baseTop + gap * i // startY , this.getWidth() - padR // endX , baseTop + gap * i // endY , mPaint);
1. 問題点
以前のカスタムEditTextは、画面の高さを超えない範囲のテキスト内容しか表示できませんでした。内容を増やすと以下のような問題が発生します:
2. 原因分析
下部(画面の高さを超える部分)に線が描画されていません。つまり、横線の数が足りていないため、ループ制御の部分に問題があるはずです。
3. 解決方法
1. 各行のテキストの下に必ず線を表示するには?
まず、`EditText.getLineCount()`を使用してテキストの行数を取得し、行ごとに線を描画します。
2. 横線の位置をどのように決定するか?
Y = EditText.getPaddingTop() + EditText.getLineHeight() * index; // Y座標 = テキストボックス内の上部パディング + 行の高さ * 行インデックス(第何行)
上記の方法は上から下へ描画する方法ですが、もちろん下から上へ描画することも可能です。ここでは詳細は割愛します。
4. コーディング
package com.ayqy.app_test;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.widget.EditText;
public class myEditText extends EditText{
private int lineColor; // 横線の色
private float lineWidth; // 横線の幅
public myEditText(Context context) {
super(context);
// デフォルトの色と横線の幅を設定
lineColor = Color.BLUE; // デフォルトは青色の線
lineWidth = 0.5f; // デフォルトの幅は0.5
}
public myEditText(Context context,int color,float width) {
super(context);
// 色と横線の幅を設定
this.lineColor = color;
this.lineWidth = width;
}
@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
// ペイントを作成
Paint mPaint = new Paint();
mPaint.setStrokeWidth(lineWidth);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(lineColor);
// パラメータを取得
int padL = this.getPaddingLeft(); // ボックス内の左パディングを取得
int padR = this.getPaddingRight(); // ボックス内の右パディングを取得
int padT = this.getPaddingTop(); // ボックス内の上パディングを取得
int lines = this.getLineCount(); // 行数を取得
float size = this.getTextSize(); // フォントサイズを取得
float baseTop = padT + size / 6; // 上から下へ1本目の線の位置
/* ここで説明が必要なのは size/6 という値です。これはテストで偶然得られた値で、行間のおよそ半分に相当します。
* なぜ EditText.getLineSpacingExtra() を使って行間を取得しないのか?
* テストの結果、EditText の getLineSpacingExtra メソッドを呼び出すと NoSuchMethod エラーが発生したためです。具体的な理由は不明です。
* テストでは、行間の値が TextSize の 1/3 に近似することがわかったため、行間が必要な場合は getLineSpacingExtra メソッドの代わりにこの値を使用できます。
* */
float gap = this.getLineHeight(); // 行の高さを取得
// 上から下へ線を引く
for(int i = 1;i <= lines;i++)
{
canvas.drawLine(padL // startX
, baseTop + gap * i // startY
, this.getWidth() - padR // endX
, baseTop + gap * i // endY
, mPaint);
}
}
public int getLineColor() {
return lineColor;
}
public void setLineColor(int color) {
this.lineColor = color;
}
public float getLineWidth() {
return lineWidth;
}
public void setLineWidth(float width) {
this.lineWidth = width;
}
}
P.S. 上記のコードコメントで「EditTextのgetLineSpacingExtraメソッドを呼び出すとNoSuchMethodエラーが発生する」と述べましたが、Googleで検索してもこの問題に遭遇した人はいないようです。答えをご存知の方がいれば、コメント欄で教えてください。ありがとうございます。
5. 実行結果のスクリーンショット
改良後のコードは、「テキストが線に重なる」および「カーソルが線に重なる」問題を完璧に解決しました。核心は「行間 = TextSize / 3」です。行間の存在が表示効果に影響を与えるため、半行分を加算すればうまくいきます。
6. まとめ
バグを見つけたらすぐに修正する、プログラミングの楽しさはまさにここにあります。
コメントはまだありません