[Android]シンプルなカスタム制御プロセス

Androidカスタムコンポーネントには、一般に3つの実装方法があります:
1.組み合わせコントロール:名前が示すように、組み合わせコントロールはいくつかの小さなコントロールを組み合わせて新しいコントロールを形成します。これらの小さなコントロールのほとんどは組み込みのコントロールです。
2つ目は、自己描画コントロール:自己描画コントロールとは、ペイントとキャンバスで完全に描画されます。これは、onDraw()メソッドで描画し、onMeasure()メソッドで測定します。コンテナーがonLayout()メソッドに配置されている場合各サブコンポーネント。
3.継承されたコントロール:既存のコントロールを継承し、新しいコントロールを作成し、継承された親コントロールの特性を保持し、新しい特性を導入することを意味します。
自己描画コントロールには、カスタムコンポーネントカスタムコンテナの 2つのタイプもあります。カスタムコンポーネントはViewクラスを継承し、カスタムコンテナがViewGrounpを継承する場合、今日、カスタムコンポーネントの主な分析は、より実用的な例を示すことです。最も単純なTextViewを描画するために最初に頭に浮かぶのは、canvas.drawText()メソッドです。さらに、ステップバイステップである必要があります:
(1))MyTextViewクラスを作成すると、Viewを継承し、3つの構築メソッドを書き換えます。もちろん、onDraw()メソッドとonMeasure()メソッドがあり、次のコードがあります。

public class MyTextView extends View{
    
    
/**
* 文本颜色
*/
private int textColor;
/**
* 字体大小
*/
private float textSize;
/**
* 文本
*/
private String text;

/**
* 绘制时控制
*/
private Paint mPaint;
private Rect mBoud;

public MyTextView(Context context) {
    
    
this(context, null);
}

public MyTextView(Context context, AttributeSet attrs) {
    
    
this(context, attrs, 0);
}

public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) {
    
    
super(context, attrs, defStyleAttr);
}

@Override
protected void onDraw(Canvas canvas) {
    
    
super.onDraw(canvas);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    
    
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

(2)次に、これはTextViewであるため、text、color、textSizeなどの属性が必要であると考える必要があります。これらの属性を値ディレクトリのattrs.xmlで定義します。これにより、MyTextViewをxmlに導入してこれらの値を直接操作し、次のようにこれらの属性を3つのパラメーターを持つコンストラクションメソッドのコントロールに関連付けることができます。

attrs.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyTextView">
<attr name="textColor" format="color|reference"/>
<attr name="textSize" format="dimension|reference"/>
<attr name="text" format="string|reference"/>
</declare-styleable>
</resources>
public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) {
    
    
	super(context, attrs, defStyleAttr);
	TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs,R.styleable.MyTextView, 0, 0);
	textColor = typedArray.getColor(R.styleable.MyTextView_textColor, 0);
	textSize = typedArray.getDimensionPixelSize(R.styleable.MyTextView_textSize, 0);
	text = typedArray.getString(R.styleable.MyTextView_text);
	typedArray.recycle();
}

(3)プロパティが定義されているので、ペイントブラシを使用してこれらのプロパティをConstructionメソッドで設定し、次にonDrawメソッドでキャンバスを使用してテキストビューを描画します。

public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) {
    
    
	super(context, attrs, defStyleAttr);
	TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.MyTextView, 0, 0);
	textColor = typedArray.getColor(R.styleable.MyTextView_textColor, 0);
	textSize = typedArray.getDimensionPixelSize(R.styleable.MyTextView_textSize, 0);
	text = typedArray.getString(R.styleable.MyTextView_text);
	typedArray.recycle();

 

	mPaint = new Paint();
	// 设置画笔为抗锯齿
	mPaint.setAntiAlias(true);
	mPaint.setColor(textColor);
	mPaint.setTextSize(textSize);
	//获取绘制文本的宽和高
	mBoud = new Rect();
	mPaint.getTextBounds(text, 0, text.length(), mBoud);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    
    
	int width = measureWidth(widthMeasureSpec);
	int height = measureHeight(heightMeasureSpec);
	setMeasuredDimension(width, height);
}

private int measureWidth(int widthMeasureSpec){
    
    
	int mode = MeasureSpec.getMode(widthMeasureSpec);
	int size = MeasureSpec.getSize(widthMeasureSpec);
	int width = 0;

	if(mode == MeasureSpec.EXACTLY){
    
    
	//如果是math_parent或确定尺寸
		width = size;
	}else if(mode == MeasureSpec.AT_MOST){
    
    
	//如果是wrap_parent
		width = getPaddingLeft() + mBound.width() + getPaddingRight();
	}
	return width;
}

private int measureHeight(int heightMeasureSpec){
    
    
	int mode = MeasureSpec.getMode(heightMeasureSpec);
	int size = MeasureSpec.getSize(heightMeasureSpec);
	int height = 0;
	if(mode == MeasureSpec.EXACTLY){
    
    
	//如果是math_parent或确定尺寸
		height = size;
	}else if(mode == MeasureSpec.AT_MOST){
    
    
		//如果是wrap_parent
		height = getPaddingTop() + mBound.height() + getPaddingBottom();
	}
	return height;
}

@Override
protected void onDraw(Canvas canvas) {
    
    
	super.onDraw(canvas);
	//canvas.drawText(text, x, y, paint); //第一个参数是文本,第四个参数是画笔,第二个参数x默认是字符串的左边在屏幕的位置, 第三个参数y是这个字符文本baseline基线在屏幕上的位置,不是这个字符的中心在屏幕的位置
	Log.v("MyTextView", "getWidth:"+getWidth()/2);
	Log.v("MyTextView", "mBoud.width:"+mBoud.width()/2);
	Log.v("MyTextView", "getHeight"+getHeight()/2);
	Log.v("MyTextView", "mBoud.height:"+mBoud.height()/2);
	Paint.FontMetricsInt fontMetrics = mPaint.getFontMetricsInt();
	int baseline = (getMeasuredHeight() / 2) - ((fontMetrics.bottom + fontMetrics.top) / 2);
	Log.v("MyTextView", "baseline:"+baseline);
	Log.v("MyTextView", "getMeasuredHeight:"+getMeasuredHeight());
	Log.v("MyTextView", "fontMetrics.top:"+fontMetrics.top);
	Log.v("MyTextView", "fontMetrics.bottom:"+fontMetrics.bottom);
	Log.v("MyTextView", "fontMetrics.ascent:"+fontMetrics.ascent);
	Log.v("MyTextView", "fontMetrics.descent:"+fontMetrics.descent);
	canvas.drawText(text, getWidth()/2 - mBoud.width()/2, baseline, mPaint);
}

onMeasureメソッドについて説明する

1.まず、コンポーネントの幅と高さを測定するとき、コンポーネントの親コンテナーによって呼び出されます。最初に、コンテナーはコンテナー内のすべての子コンポーネントをトラバースし、幅と高さを1つずつ測定します。ここで、カスタムtextViewの親コンテナーは最も外側のRelativeLayout
2.次に、MeasureSpecクラスを理解する必要があります。このクラスは、幅と高さの寸法とモードを取得するために、getMode(int measureSpec)とgetSize(int measureSpec)の2つのメソッドを提供します。このクラスは、 3つの定数は:MeasureSpec.EXACTLY、MeasureSpec.AT_MOSTは、MeasureSpec.UNSPECIFIED;
MeasureSpec.EXACTLY:一定サイズはTextViewに指定されているとき、またはmath_parent
MeasureSpec.AT_MOST:TextViewにはwrap_contentである場合
のTextViewに定義された幅と高さを有していない。MeasureSpec.UNSPECIFIED

おすすめ

転載: blog.csdn.net/sinat_36955332/article/details/108658471