Android自定义控件及属性

时间:2022-05-13 20:37:34

在开发中,现有的控件并不能完全满足我们的需求,往往需要我们去进行自定义控件的设计。

在这里我们先了解一个属性,attrs中发 format。

format有多种可选值设置:

1. reference:参考某一资源ID。

(1)属性定义:

<declare-styleable name = "名称">

<attr name = "background" format = "reference" />

</declare-styleable>

(2)属性使用:

<ImageView

android:layout_width = "42dip"

android:layout_height = "42dip"

android:background = "@drawable/图片ID"/>

2. color:颜色值

<declare-styleable name = "名称">

<attr name = "textColor" format = "color" />

</declare-styleable>

3. boolean:布尔值

<declare-styleable name = "名称">

<attr name = "focusable" format = "boolean" />

</declare-styleable>

4. dimension:尺寸值。

<declare-styleable name = "名称">

<attr name = "layout_width" format = "dimension" />

</declare-styleable>

5. float:浮点值。

6. integer:整型值。

7. string:字符串

8. fraction:百分数。

9. enum:枚举值

10. flag:位或运算

注意:

属性定义时可以指定多种类型值。

(1)属性定义:

<declare-styleable name = "名称">

<attr name = "background" format = "reference|color" />

</declare-styleable>

(2)属性使用:

<ImageView

android:layout_width = "42dip"

android:layout_height = "42dip"

android:background = "@drawable/图片ID|#00FF00"/>


接下来我们就可以正式进行XML属性的自定义了

一、在res/values/attrs文件中定义属性

<declare-styleable name="PasswordInputView">
<!--边框圆角值-->
<attr name="pivBorderRadius" format="dimension"/>
<!--边框颜色-->
<attr name="pivBorderColor" format="color|reference"/>
<!--边框宽度-->
<attr name="pivBorderWidth" format="dimension"/>
<!--圆角尺寸-->
<attr name="pivPasswordRadius" format="dimension"/>
<!--密码颜色-->
<attr name="pivPasswordColor" format="color"/>
<!--密码大小-->
<attr name="pivPasswordWidth" format="dimension"/>
<!--密码长度-->
<attr name="pivPasswordLength" format="integer"/>
<!--密码文本-->
<attr name="pivTextSize" format="dimension"/>
<attr name="pivTextColor" format="color"/>
<attr name="pivIsPass" format="boolean"/>
</declare-styleable>

二、定义MyView继承View与自定义属性相关联(在构造方法中关联)

public PasswordInputView(Context context, AttributeSet attrs) {
super(context, attrs);

TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.PasswordInputView, 0, 0);
borderColor = a.getColor(R.styleable.PasswordInputView_pivBorderColor, borderColor);
borderWidth = a.getDimension(R.styleable.PasswordInputView_pivBorderWidth, borderWidth);
borderRadius = a.getDimension(R.styleable.PasswordInputView_pivBorderRadius, borderRadius);
passwordLength = a.getInt(R.styleable.PasswordInputView_pivPasswordLength, passwordLength);
passwordColor = a.getColor(R.styleable.PasswordInputView_pivPasswordColor, passwordColor);
passwordWidth = a.getDimension(R.styleable.PasswordInputView_pivPasswordWidth, passwordWidth);
passwordRadius = a.getDimension(R.styleable.PasswordInputView_pivPasswordRadius, passwordRadius);

textSize = (int) a.getDimension(R.styleable.PasswordInputView_pivTextSize, textSize);
textColor = a.getColor(R.styleable.PasswordInputView_pivTextColor, textColor);
isPass = a.getBoolean(R.styleable.PasswordInputView_pivIsPass, isPass);

a.recycle();

borderPaint.setStrokeWidth(borderWidth);
borderPaint.setColor(borderColor);
passwordPaint.setStrokeWidth(passwordWidth);
passwordPaint.setStyle(Paint.Style.FILL);
passwordPaint.setColor(passwordColor);

InputFilter[] filters = {new InputFilter.LengthFilter(passwordLength)};
setFilters(filters);
}

注意:为防止使用时属性值未设置,在初始化时将个属性进行默认值的设定


PasswordInputView的具体代码如下

package com.laobai.view;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.text.InputFilter;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.widget.EditText;

import com.laobai.R;

import static android.graphics.Paint.ANTI_ALIAS_FLAG;
/**
*改控件为自定义的密码输入框,可设置明文及密码
*/
@SuppressLint("AppCompatCustomView")
public class PasswordInputView extends EditText {

private static final int defaultContMargin = 5;
private static final int defaultSplitLineWidth = 3;
/**
* 边框颜色
*/
private int borderColor = 0xFFCCCCCC;
private float borderWidth = 2;
private float borderRadius = 0;

private int passwordLength = 4;
private int passwordColor = 0xFFCCCCCC;
private float passwordWidth = 8;
private float passwordRadius = 3;

int textSize = 15;
int textColor = Color.BLACK;

boolean isPass = true;

private Paint passwordPaint = new Paint(ANTI_ALIAS_FLAG);
private Paint borderPaint = new Paint(ANTI_ALIAS_FLAG);

private int textLength;
String passText;

public PasswordInputView(Context context, AttributeSet attrs) {
super(context, attrs);

TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.PasswordInputView, 0, 0);
borderColor = a.getColor(R.styleable.PasswordInputView_pivBorderColor, borderColor);
borderWidth = a.getDimension(R.styleable.PasswordInputView_pivBorderWidth, borderWidth);
borderRadius = a.getDimension(R.styleable.PasswordInputView_pivBorderRadius, borderRadius);
passwordLength = a.getInt(R.styleable.PasswordInputView_pivPasswordLength, passwordLength);
passwordColor = a.getColor(R.styleable.PasswordInputView_pivPasswordColor, passwordColor);
passwordWidth = a.getDimension(R.styleable.PasswordInputView_pivPasswordWidth, passwordWidth);
passwordRadius = a.getDimension(R.styleable.PasswordInputView_pivPasswordRadius, passwordRadius);

textSize = (int) a.getDimension(R.styleable.PasswordInputView_pivTextSize, textSize);
textColor = a.getColor(R.styleable.PasswordInputView_pivTextColor, textColor);
isPass = a.getBoolean(R.styleable.PasswordInputView_pivIsPass, isPass);

a.recycle();

borderPaint.setStrokeWidth(borderWidth);
borderPaint.setColor(borderColor);
passwordPaint.setStrokeWidth(passwordWidth);
passwordPaint.setStyle(Paint.Style.FILL);
passwordPaint.setColor(passwordColor);

InputFilter[] filters = {new InputFilter.LengthFilter(passwordLength)};
setFilters(filters);
}

@Override
protected void onDraw(Canvas canvas) {
int width = getWidth();
int height = getHeight();

// 外边框
RectF rect = new RectF(0, 0, width, height);
borderPaint.setColor(borderColor);
canvas.drawRoundRect(rect, borderRadius, borderRadius, borderPaint);

// 内容区
RectF rectIn = new RectF(rect.left + defaultContMargin, rect.top + defaultContMargin,
rect.right - defaultContMargin, rect.bottom - defaultContMargin);
borderPaint.setColor(Color.WHITE);
canvas.drawRoundRect(rectIn, borderRadius, borderRadius, borderPaint);

// 分割线
borderPaint.setColor(borderColor);
borderPaint.setStrokeWidth(defaultSplitLineWidth);
for (int i = 1; i < passwordLength; i++) {
float x = width * i / passwordLength;
canvas.drawLine(x, 0, x, height, borderPaint);
}

float cx, cy = height / 2;
float half = width / passwordLength / 2;
if (isPass) {
// 密码
for (int i = 0; i < textLength; i++) {
cx = width * i / passwordLength + half;
canvas.drawCircle(cx, cy, passwordWidth, passwordPaint);
}
} else {
passwordPaint.setTextSize(textSize);
passwordPaint.setColor(textColor);
passwordPaint.setTextAlign(Paint.Align.CENTER);

Paint.FontMetrics fontMetrics = passwordPaint.getFontMetrics();
float top = fontMetrics.top;//为基线到字体上边框的距离,即上图中的top
float bottom = fontMetrics.bottom;//为基线到字体下边框的距离,即上图中的bottom

int baseLineY = (int) (rect.centerY() - top / 2 - bottom / 2);//基线中间点的y轴计算公式
if (!TextUtils.isEmpty(passText)) {
for (int i = 0; i < textLength; i++) {
cx = width * i / passwordLength + half;
canvas.drawText(String.valueOf(passText.charAt(i)), cx, baseLineY, passwordPaint);
}
}
}
}

@Override
protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
super.onTextChanged(text, start, lengthBefore, lengthAfter);
passText = text.toString();
textLength = passText.length();
invalidate();
}

public int getBorderColor() {
return borderColor;
}

public void setBorderColor(int borderColor) {
this.borderColor = borderColor;
borderPaint.setColor(borderColor);
invalidate();
}

public float getBorderWidth() {
return borderWidth;
}

public void setBorderWidth(float borderWidth) {
this.borderWidth = borderWidth;
borderPaint.setStrokeWidth(borderWidth);
invalidate();
}

public float getBorderRadius() {
return borderRadius;
}

public void setBorderRadius(float borderRadius) {
this.borderRadius = borderRadius;
invalidate();
}

public int getPasswordLength() {
return passwordLength;
}

public void setPasswordLength(int passwordLength) {
this.passwordLength = passwordLength;
invalidate();
}

public int getPasswordColor() {
return passwordColor;
}

public void setPasswordColor(int passwordColor) {
this.passwordColor = passwordColor;
passwordPaint.setColor(passwordColor);
invalidate();
}

public float getPasswordWidth() {
return passwordWidth;
}

public void setPasswordWidth(float passwordWidth) {
this.passwordWidth = passwordWidth;
passwordPaint.setStrokeWidth(passwordWidth);
invalidate();
}

public float getPasswordRadius() {
return passwordRadius;
}

public void setPasswordRadius(float passwordRadius) {
this.passwordRadius = passwordRadius;
invalidate();
}

public void setIsPass(boolean isPass){
this.isPass = isPass;
invalidate();
}
public void setTextSize(int textSize){
this.textSize = textSize;
invalidate();
}
public void setTextColor(int textColor){
this.textColor = textColor;
invalidate();
}
}

三、XML布局文件中的应用

要使用自定义属性必须加上前缀:xmlns:app="http://schemas.android.com/apk/res-auto"

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/backcolor_white"
android:gravity="center_horizontal"
android:orientation="vertical">

<com.view.PasswordInputView
android:id="@+id/code_edit"
android:layout_width="270dp"
android:layout_height="45dp"
android:layout_marginTop="10dp"
android:inputType="number"
android:lines="1"
android:visibility="gone"
app:pivBorderColor="#dddddd"
app:pivBorderWidth="0.5dp"
app:pivIsPass="false"
app:pivPasswordLength="6"
app:pivPasswordWidth="10dp"
app:pivTextColor="#333333"
app:pivTextSize="@dimen/sp_text_16"
tools:text="125635"
tools:visibility="visible" />

</LinearLayout>