1.基本样式展示
2.场景分析
1.分析该 View 的通用性,在哪些场景下可以使用?
1.在登陆,注册等界面比较常用。
2.左侧为文本,右侧也为文本的控件也可以复用。
2.根据通用性,需要为这个控件定义哪些属性?
1.通过分析 ==> 左侧需要显示 Title,右侧可以输入文字,支持提示等。
2.该布局适合用线性布局,方向为水平方向。
3.左侧 文本 跟 右侧 文本显示的文字样式 不一样,应该支持配置 text 属性 style。
4.右侧为输入框,支持文本及密码等输入,应该支持: 输入类型的设置
5.需要支持左侧 Title 设置固定宽度,右侧占满余下等属性设置。
6.可以设置下划线等。
3.根据上述属性分析,可列举出自定义View的基本属性。
<!-- 自定义 样式-->
<declare-styleable name="InputItemLayout">
<!-- 右侧默认提示-->
<attr name="hint" format="string" />
<!-- 左侧显示的内容-->
<attr name="title" format="string" />
<!-- 右侧文本最大输入长度-->
<attr name="maxInputLength" format="integer" />
<!-- 输入类型,字符,密码,数字-->
<attr name="inputType" format="enum">
<enum name="text" value="0" />
<enum name="password" value="1" />
<enum name="number" value="2" />
</attr>
<!-- 输入文本的 text的 样式 styleable-->
<attr name="inputTextAppearance" format="reference" />
<!-- 标题的 text 的样式 styleable-->
<attr name="titleTextAppearance" format="reference" />
<!-- 上划线 -->
<attr name="topLineAppearance" format="reference" />
<!-- 下划线 -->
<attr name="bottomLineAppearance" format="reference" />
</declare-styleable>
输入文本的 text 的基本样式:
<!-- 提示文本样式-->
<declare-styleable name="inputTextAppearance">
<attr name="hintColor" format="color" />
<attr name="inputColor" format="color" />
<attr name="textSize" format="dimension" />
</declare-styleable>
<!-- 标题文本样式-->
<declare-styleable name="titleTextAppearance">
<attr name="titleColor" format="color" />
<attr name="titleSize" format="dimension" />
<attr name="minWidth" format="dimension" />
</declare-styleable>
<!-- 分割线样式-->
<declare-styleable name="lineAppearance">
<attr name="color" format="color" />
<attr name="height" format="dimension" />
<attr name="leftMargin" format="dimension" />
<attr name="rightMargin" format="dimension" />
<!-- 是否可见-->
<attr name="enable" format="boolean" />
</declare-styleable>
3.控件布局分析
1.根据上面分析,采用 LinearLayout 作为根布局,水平摆放。
2.左侧Title 用一个 TextView,右侧实体文本用一个 EditText
4.代码实现
下面就开始解析在 arrts.xml 中自定义的属性,并将它们设置给对应的 View
- 解析 InputItemLayout 的基本属性。
- 解析顶部和底部的线条(根据是否配置了来设置)
/**
* author : shengping.tian
* time : 2021/07/05
* desc : 账号密码自定义样式数据框
* version: 1.0
*/
class InputItemLayout : LinearLayout {
//左边标题文本
private lateinit var titleTextView: TextView
//右边输入文本
private lateinit var editTextView: EditText
//底部线条
private var bottomLine: Line
//上方线条
private var topLine: Line
//上方线条画笔
private val topPaint = Paint(Paint.ANTI_ALIAS_FLAG)
//底部线条画笔
private val bottomPaint = Paint(Paint.ANTI_ALIAS_FLAG)
constructor(context: Context) : this(context, null)
constructor(context: Context, attributeSet: AttributeSet?) : this(context, attributeSet, 0)
constructor(context: Context, attributeSet: AttributeSet?, defStyle: Int) : super(context, attributeSet, defStyle) {
//1.LinearLayout 支持设置一个 drawable 用作 item 之间的分隔线
dividerDrawable = ColorDrawable()
//2.Set how dividers should be shown between items in this layout
// SHOW_DIVIDER_NONE,
// SHOW_DIVIDER_BEGINNING,
// SHOW_DIVIDER_MIDDLE,
// SHOW_DIVIDER_END
showDividers = SHOW_DIVIDER_BEGINNING
orientation = HORIZONTAL
//3.加载自定义属性,获取完记得回收
val array = context.obtainStyledAttributes(attributeSet, R.styleable.InputItemLayout)
//3.1 解析 title 属性
val title = array.getString(R.styleable.InputItemLayout_title)
val titleResId = array.getResourceId(R.styleable.InputItemLayout_titleTextAppearance, 0)
parseTitleStyle(title, titleResId)
//3.2 解析右侧的输入框
val hint = array.getString(R.styleable.InputItemLayout_hint)
val inputResId = array.getResourceId(R.styleable.InputItemLayout_inputTextAppearance, 0)
val maxInputLength = array.getInteger(R.styleable.InputItemLayout_maxInputLength, 20)
//输入类型,数字,密码,文本等。
val inputType = array.getInteger(R.styleable.InputItemLayout_inputType, 0)
parseInputStyle(hint, inputResId, inputType, maxInputLength)
//3.3 解析上下分割线
val topResId = array.getResourceId(R.styleable.InputItemLayout_topLineAppearance, 0)
val bottomResId = array.getResourceId(R.styleable.InputItemLayout_bottomLineAppearance, 0)
topLine = parseLineStyle(topResId)
bottomLine = parseLineStyle(bottomResId)
//上边线是否可见
if (topLine.enable) {
topPaint.color = topLine.color
topPaint.style = Paint.Style.FILL_AND_STROKE
topPaint.strokeWidth = topLine.height
}
if (bottomLine.enable) {
bottomPaint.color = bottomLine.color
bottomPaint.style = Paint.Style.FILL_AND_STROKE
bottomPaint.strokeWidth = bottomLine.height
}
array.recycle()
}
fun getTileView(): TextView {
return titleTextView
}
fun getEditText(): EditText {
return editTextView
}
/**
* 画 上边界线 和 下边界线
*/
override fun draw(canvas: Canvas?) {
super.draw(canvas)
if (topLine.enable) {
canvas?.drawLine(
topLine.leftMargin,
0f,
measuredWidth - topLine.rightMargin,
0f,
topPaint
)
}
if (bottomLine.enable) {
canvas?.drawLine(
bottomLine.leftMargin,
height - bottomLine.height,
measuredWidth - bottomLine.rightMargin,
height - bottomLine.height,
bottomPaint
)
}
}
/**
* 解析横线 style
*/
@SuppressLint("CustomViewStyleable")
private fun parseLineStyle(resId: Int): Line {
val array = context.obtainStyledAttributes(resId, R.styleable.lineAppearance)
val line = Line().apply {
color = array.getColor(
R.styleable.lineAppearance_color,
resources.getColor(R.color.color_d1d2)
)
height = array.getDimensionPixelOffset(R.styleable.lineAppearance_height, 0).toFloat()
leftMargin =
array.getDimensionPixelOffset(R.styleable.lineAppearance_leftMargin, 0).toFloat()
rightMargin =
array.getDimensionPixelOffset(R.styleable.lineAppearance_rightMargin, 0).toFloat()
enable = array.getBoolean(R.styleable.lineAppearance_enable, false)
}
array.recycle()
return line
}
/**
* 解析右侧 输入文本
* @hint 提示文本
* @inputResId 文本样式
* @inputType 文本输入类型 字符型,数字型,密码型
*/
@SuppressLint("CustomViewStyleable")
private fun parseInputStyle(hintStr: String?, inputResId: Int, type: Int, maxInputLength: Int) {
val array = context.obtainStyledAttributes(inputResId, R.styleable.inputTextAppearance)
val hintColor = array.getColor(
R.styleable.inputTextAppearance_hintColor,
ContextCompat.getColor(context, R.color.color_d1d2)
)
val inputColor = array.getColor(
R.styleable.inputTextAppearance_inputColor,
ContextCompat.getColor(context, R.color.color_565)
)
//px
val textSize = array.getDimensionPixelSize(
R.styleable.inputTextAppearance_textSize,
applyUnit(TypedValue.COMPLEX_UNIT_SP, 15f)
)
editTextView = EditText(context).apply {
//最多可输入的字符数
filters = arrayOf(InputFilter.LengthFilter(maxInputLength))
setPadding(0, 0, 0, 0)
val params = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT)
params.weight = 1f//设置 editText 的权重
layoutParams = params
hint = hintStr
setTextColor(inputColor)
setHintTextColor(hintColor)
gravity = LEFT or CENTER
setBackgroundColor(Color.TRANSPARENT)
setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize.toFloat())
when (type) {
0 -> {
inputType = InputType.TYPE_CLASS_TEXT
}
1 -> {
inputType = InputType.TYPE_TEXT_VARIATION_PASSWORD or InputType.TYPE_CLASS_TEXT
}
2 -> {
inputType = InputType.TYPE_CLASS_NUMBER
}
}
}
addView(editTextView)
array.recycle()
}
/**
* 解析 title 属性
*/
@SuppressLint("CustomViewStyleable")
private fun parseTitleStyle(title: String?, titleResId: Int) {
val array = context.obtainStyledAttributes(titleResId, R.styleable.titleTextAppearance)
val titleColor = array.getColor(
R.styleable.titleTextAppearance_titleColor,
resources.getColor(R.color.color_565)
)
//转换 sp to px
val titleSize = array.getDimensionPixelSize(
R.styleable.titleTextAppearance_titleSize,
applyUnit(TypedValue.COMPLEX_UNIT_SP, 15f)
)
val minWidth = array.getDimensionPixelOffset(R.styleable.titleTextAppearance_minWidth, 0)
//创建左侧的 title 的 TextView
titleTextView = TextView(context).apply {
text = title
setTextSize(TypedValue.COMPLEX_UNIT_PX, titleSize.toFloat()) //sp---当做sp在转换一次
setTextColor(titleColor)
layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
setMinWidth(minWidth)
gravity = LEFT or CENTER
//添加到布局中
}
addView(titleTextView)
array.recycle()
}
/**
* 转换像素
*/
private fun applyUnit(applyUnit: Int, value: Float): Int {
return TypedValue.applyDimension(applyUnit, value, resources.displayMetrics).toInt()
}
/**
* 分割线样式封装类
*/
inner class Line {
var color = 0
var height = 0f
var leftMargin = 0f
var rightMargin = 0f
var enable = false
}
}
5.在 layout 中使用
在布局中使用定义好的 InputItemLayout
<?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:orientation="vertical"
tools:context="com.tsp.test.view.ViewShowActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:padding="10dp"
android:text="登录界面" />
<com.tsp.android.hiui.view.InputItemLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="10dp"
android:paddingLeft="16dp"
app:bottomLineAppearance="@style/leftMargin_lineAppearance"
app:hint="请输入用户名"
app:inputTextAppearance="@style/inputTextAppearance"
app:inputType="text"
app:title="用户名"
app:titleTextAppearance="@style/titleTextAppearance"
app:topLineAppearance="@style/lineAppearance" />
<com.tsp.android.hiui.view.InputItemLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:paddingLeft="16dp"
app:bottomLineAppearance="@style/lineAppearance"
app:hint="请输入密码"
app:inputTextAppearance="@style/inputTextAppearance"
app:inputType="password"
app:title="密码"
app:titleTextAppearance="@style/titleTextAppearance" />
<com.google.android.material.button.MaterialButton
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_margin="@dimen/dp_10"
android:backgroundTint="@color/color_127"
android:text="登录"
android:textColor="@color/color_eee"
android:textSize="16sp"
app:cornerRadius="@dimen/dp_6" />
</LinearLayout>
其中 titleTextAppearance 等样式定义如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- 标题文本样式-->
<style name="titleTextAppearance">
<item name="titleColor">@color/color_565</item>
<item name="titleSize">15sp</item>
<item name="minWidth">100dp</item>
</style>
<!--输入文本样式-->
<style name="inputTextAppearance">
<item name="hintColor">@color/color_C1B</item>
<item name="inputColor">@color/color_565</item>
<item name="textSize">15sp</item>
</style>
<style name="lineAppearance">
<item name="color">@color/color_C1B</item>
<item name="height">0.5dp</item>
<item name="leftMargin">0dp</item>
<item name="rightMargin">0dp</item>
<item name="enable">true</item>
</style>
<style name="leftMargin_lineAppearance" parent="lineAppearance">
<item name="leftMargin">10dp</item>
</style>
</resources>
6.小结
这是一个基础简单的自定义 View 的基本步骤,通过分析 View 的 样式及 属性,选择合适的 View 构建成我们想要的样式,当然,具体的拓展属性,还需要根据业务场景去做修改。同时也是为了提高控件的复用率。