サンプルプログラム工場

AAKAKA Appで使われているプログラムのサンプルコードをそのまま公開!

サンプルプログラム工場 > android > [Android]TextViewが重いと感じたあなたのためのLabelViewサンプル#tryLabelView00
Google Play AAKAKAxSOFTへ

[Android]TextViewが重いと感じたあなたのためのLabelViewサンプル#tryLabelView00

実行ファイル(APK)やサンプル(zip)をダウンロードする
tryLabelView00の実行イメージ

TextViewを何個も並べて表示すると突然重くなったりする。
特にListViewなんかに何個も並べたり、TextViewの中で複数行にになったとき突然パフォーマンスが落ちたりする。
そんな時にシンプルなテキストを表示するだけのものがほしくなる。軽いやつ。
Android のAPIサンプルにLabelViewというのがのっているけど、それは複数行に対応していないのでそれに対応したものを作る。

LabelViewを使うためにはres/values/attrs.xmlが必要。
その中の設定データをIDEが使ってプロパティで設定できるようになる。
app:xxxxでアプリケーションで作ったコントロールは値をセットする。
プロパティでセットされた値はR.styleable.LabelView_textという形になって定義される。
それらをコンストラクタなどで取り出す。

検索した事
 Android TextView 重い
 Android LabelView
 Android 軽いTextView サンプル

開発環境
 Eclipse IDE バージョン: 4.2.1
 ターゲットプラットフォーム: 2.3.3
 API レベル: 10

package com.example.trylabelview00;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;


/**
 * メインのアクティビティ
 *
 */
public class TryLabelView00 extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_try_label_view00);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_try_label_view00, menu);
        return true;
    }
}

アクティビティのXMLレイアウト

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res/com.example.trylabelview00"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <com.example.trylabelview00.LabelView
        android:id="@+id/labelView1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="100dip"
        app:text="普通のTextViewを使うのと同じように使うことができます。" />

    <com.example.trylabelview00.LabelView
        android:id="@+id/labelView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:text="Graphical Layoutに表示されないときはEclipseを再起動!" />

    <com.example.trylabelview00.LabelView
        android:id="@+id/labelView3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:textColor="#ff507f50"
        app:textSize="24dip"
        app:text="色やサイズもかえられます。" />
    
    <com.example.trylabelview00.LabelView
        android:id="@+id/labelView3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:textColor="#ffff5050"
        app:textSize="16dip"
        app:text="TextViewを並べるよりLabelViewとして自分で作ったほうが軽くなる部分が多いと思います。" />
    
</LinearLayout>

ラベルビュー本体

package com.aakaka.common;

import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

import com.aakaka.ooapp009.R;


/**
 * EditViewはなんか重いから単純なテキストを表示するだけのモノを作るよ
 * @author a5
 *
 */
public class LabelView extends View {
	
    private Paint mTextPaint;	// 文字を書く色
    private String mText = "";	// 文字列
    private int mTextSize;	// テキストサイズ
    private int mTextColor = 0xff000000;	// テキストカラー
    private int mAscent;	// ベースラインより上の高さ
    private int mLinePaddingTop;	// 1行当たりの↑パディング
    private int mLinePaddingBottom;	// 1行当たりの↓パディング
    
    
    private int mLineHeight;	// 1行の高さ
    
    private List<String> mListLineText = new ArrayList<String>();	// 行を分けたテキスト

    /**
     * コンストラクタ
     * @param context
     */
    public LabelView(Context context) {
        super(context);
        
        // ラベルビューを初期化するよ
        this.initLabelView();
    }

    /**
     * コンストラクタ
     * これはレイアウトに配置したときに呼び出される
     * @param context
     * @param attrs
     */
    public LabelView(Context context, AttributeSet attrs) {
        super(context, attrs);
        
        // ラベルビューの初期化
        this.initLabelView();
        
        // XMLレイアウトから値を取り出す
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.LabelView);

        // 設定されているテキストを取り出す
        CharSequence s = typedArray.getString(R.styleable.LabelView_text);
        if (s != null) {
        	this.setText(s.toString());
        }

        this.setColor(typedArray.getColor(R.styleable.LabelView_textColor, 0xFF000000));

        // テキストサイズを取り出す
        int textSize = typedArray.getDimensionPixelOffset(R.styleable.LabelView_textSize, 0);
        
        // ラインパディングを取り出す
        this.mLinePaddingTop = typedArray.getDimensionPixelOffset(R.styleable.LabelView_linePaddingTop, 0);
        this.mLinePaddingBottom = typedArray.getDimensionPixelOffset(R.styleable.LabelView_linePaddingBottom, 0);

        // 解放
        typedArray.recycle();
        
        // テキストサイズが設定されているならそのサイズを指定する
        if (textSize > 0) {
        	this.setTextSize(textSize);
        }

        
    }

    /**
     * ラベルビューの初期化
     */
    private final void initLabelView() {
    	// 色を変えたりするためのペイントを用意
        this.mTextPaint = new Paint();
        this.mTextPaint.setAntiAlias(false);
        
        // スクリーンに合わせたテキストサイズに変更する
        this.mTextPaint.setTextSize(16 * getResources().getDisplayMetrics().density);
        this.mTextPaint.setColor(0xff000000);
        //setPadding(3, 3, 3, 3);
    }


    /**
     * 表示するテキスト
     * @param text 表示したいテキスト
     */
    public void setText(String text) {

    	// テキストに変更がある場合だけ修正する
    	if (this.mText.equals(text) == false) {
    		this.mText = text;
    		
    		// リストをクリアして再計算させる
    		this.mListLineText.clear();
    		
    		// レイアウト変更を通知
    		this.requestLayout();
    		
    		// 再描画
    		this.invalidate();
    	}
    }

    /**
     * ラベルのテキストサイズを変える
     * @param size テキストサイズ
     */
    public void setTextSize(int size) {
    	
    	// サイズに変更があった時だけ変更する
    	if (this.mTextSize != size) {
    		this.mTextSize = size;
            this.mTextPaint.setTextSize(size);
            this.requestLayout();
            this.invalidate();
    	}
    }

    /**
     * テキストカラーを変更する
     * @param color 色
     */
    public void setColor(int color) {

    	// 変更があったときだけ変える
    	if (this.mTextColor != color) {
    		this.mTextColor = color;
    		this.mTextPaint.setColor(color);
            this.invalidate();
    	}
    }

    /**
     * @see android.view.View#measure(int, int)
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    	// ビューの幅を求める
    	int width = this.measureWidth(widthMeasureSpec);
    	
        // 複数行になる場合は、テキストの切り離し位置を求める
        this.updateLineBreakedText(this.mTextPaint, this.mText, width - (getPaddingLeft() + getPaddingRight()));

    	// ビューの高さを求める
    	int height = this.measureHeight(heightMeasureSpec);
    	
    	// ビューのサイズをセットする
        this.setMeasuredDimension(width, height);
    }

    /**
     * ビューの幅を求める
     * @param measureSpec
     * @return
     */
    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 = (int) mTextPaint.measureText(mText) + getPaddingLeft() + getPaddingRight();
            if (specMode == MeasureSpec.AT_MOST) {
                // Respect AT_MOST value if that was what is called for by measureSpec
                result = Math.min(result, specSize);
            }
        }

        return result;
    }

    /**
     * ビューの高さを求める
     * @param measureSpec
     * @param width
     * @return
     */
    private int measureHeight(int measureSpec) {
    	
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        
        // 最初の段階での1行の高さ
        this.mLineHeight = specSize;
        
        // ベースラインより上の高さ
        this.mAscent = (int) mTextPaint.ascent();
        
        // サイズが指定されている?
        if (specMode == MeasureSpec.EXACTLY) {
            // We were told how big to be
            result = specSize;
            
        } else {
        	// 1行の高さ
        	float lineHeight = (-this.mAscent + mTextPaint.descent()) + this.getLinePaddingTop() + this.getLinePaddingBottom();
        	this.mLineHeight = (int)lineHeight;
        	
        	// 文字列の行数
        	int lineCount = this.mListLineText.size();
        	
        	result = (this.mLineHeight * lineCount) + this.getPaddingTop() + this.getPaddingBottom();
            // Measure the text (beware: ascent is a negative number)
            //result = (int)((-mAscent + mTextPaint.descent()) * (this.mListLineText.size())) + getPaddingTop() + getPaddingBottom();
            
            if (specMode == MeasureSpec.AT_MOST) {
                // Respect AT_MOST value if that was what is called for by measureSpec
                result = Math.min(result, specSize);
            }
        }
        return result;
    }
    
    /**
     * 改行位置で分割されたテキストを更新する
     * @param paint
     * @param text
     * @param width
     * @return
     */
    private void updateLineBreakedText(Paint paint, String text, int width) {
    	
    	// ここの処理を書くと複数行にまたぐときに文字の長さが正常にならないので削除
    	//  2013/02/06
    	// セットされている場合は、新しく計算しない
    	//if (0 < this.mListLineText.size()) {
    	//	return ;
    	//}
    	
    	// リストを追加する前にクリア
    	List<String> listLineText = this.mListLineText;
    	listLineText.clear();

    	
    	// テキストが無い場合はとりあえず1行何か入れとく
    	if (text == null) {
    		listLineText.add("");
    		return ;
    	}
    	
    	// 複数ラインを想定
    	String[] multiLineText = text.split("\n");
    	
    	// 改行で区切られたラインが終わるまで回す
    	for (int i = 0, len = multiLineText.length; i < len; i++) {
    		
        	int lineBreakPos = 1;	// 改行場所
        	int currStart = 0;	// 次の行のスタート位置

    		// 今のテキストを取り出す
    		String currLine = multiLineText[i];
    		
			// 改行ができるだけ回す
    		while (lineBreakPos != 0) {
    			
    		// 次の調べる文字列
    			String currText = currLine.substring(currStart);
		    		
    			// 次の改行位置を調べる
    			lineBreakPos = paint.breakText(currText, true, width, null);
    			
    			if (lineBreakPos != 0) {
    				
    				// テキストビルダーに保存する
    				listLineText.add(currText.substring(0, lineBreakPos));
    				
    				currStart += lineBreakPos;
    			}
    		}
    	}
    }

    /**
     * テキストを表示する
     */
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        
        // テキストが無いときは何もせんよ
        if (this.mText == null) {
        	return ;
        }
        
        // 行数
        int lineCount = this.mListLineText.size();


        // 文字を書き始める最初の位置
		int baseTop = (-this.mAscent + this.getPaddingTop() + this.getLinePaddingTop());

		// 文字色をセットする
		//this.setColor(this.getContext().getResources().getColor(android.R.color.black));
		// 1行に収まる
        if (lineCount == 1) {
        	// テキストをそのまま書く
        	canvas.drawText(this.mText, getPaddingLeft(), baseTop, mTextPaint);
        	
        // 2行以上ある
        } else {
        	

        	// 作られたラインを順番に欠いていく
        	for (int i = 0; i < lineCount; i++) {
        		
        		// 文字を表示する高さ
        		int y = baseTop + (this.mLineHeight * i);
        		canvas.drawText(this.mListLineText.get(i), this.getPaddingLeft(), y, this.mTextPaint);
        	}
        }
    }
    
    /**
     * 行数を返す
     * @return 行数
     */
    public int getLineCount() {
    	return this.mListLineText.size();
    }
    
    /**
     * 1行の高さを返す
     * @return 1行の高さ
     */
    public int getLineHeight() {
    	return this.mLineHeight;
    }
    
    /**
     * テキスト表示用のペイントを取り出す
     * @return ペイント
     */
    public Paint getPaint() {
    	return this.mTextPaint;
    }
    
    /**
     * 上のパディングを取り出す
     * @return 上パディングのサイズ
     */
    public int getLinePaddingTop() {
    	return this.mLinePaddingTop;
    }
    
    /**
     * 下パディングを取り出す
     * @return 下パディングのサイズ
     */
    public int getLinePaddingBottom() {
    	return this.mLinePaddingBottom;
    }
}

ラベルビューとXMLをつなげるために必要なもの

<?xml version="1.0" encoding="utf-8"?>
<resources>
	<declare-styleable name="LabelView">
		<attr name="text" format="string" />
		<attr name="textColor" format="color" />
		<attr name="textSize" format="dimension" />
		<attr name="linePaddingTop" format="dimension" />
		<attr name="linePaddingBottom" format="dimension" />
	</declare-styleable>
</resources>

サンプルプロジェクトをダウンロード

, , , ,

[Android]Layoutの背景色を動的に変更するサンプル#tryShapeColor00 [Android]縦持ちカメラのプレビュー画面にSurfaceViewをオーバーレイさせ...

One Response to “[Android]TextViewが重いと感じたあなたのためのLabelViewサンプル#tryLabelView00”

  • aa より:


    253行あたりの処理は処理が正常に動作しなくなるので削除。

    252: // セットされている場合は、新しく計算しない
    253: //if (0 < this.mListLineText.size()) { 254: // return ; 255: //}

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です


*

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>