網站首頁 編程語言 正文
前言:
自定義View可以分為兩種方式:
- 第一種通過繼承ViewGroup,內部通過addView的方式將其他的View組合到一起。
- 第二種則是通過繼承View,重啟View的onMeasure,onLayout,onDraw方法來繪制不規則圖形,如折線圖等。
本文介紹的是第一種方式通過組合的方式去實現自定義View。
實現自定義View首先要自定義屬性。對于自定義屬性,第一步是在項目res/values文件夾中新建attrs.xml文件,在文件中設置自定義屬性的名稱和類型,
代碼如下:
<resources> <declare-styleable name="InputItemLayout"> <attr name="hint" format="string"></attr> <attr name="title" format="string"/> <attr name="inputType" format="enum"> <enum name="text" value="0"/> <enum name="password" value="1"/> <enum name="number" value="2"/> </attr> <attr name="inputTextAppearance" format="reference"/> <attr name="titleTextAppearance" format="reference"/> <attr name="topLineAppearance" format="reference"/> <attr name="bottomLineAppearance" format="reference"/> </declare-styleable> <declare-styleable name="inputTextAppearance"> <attr name="hintColor" format="color" /> <attr name="inputColor" format="color" /> <attr name="textSize" format="dimension" /> <attr name="maxInputLength" format="integer" /> </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> </resources>
自定義屬性都需要包裹在declare-styleable標簽中,name屬性標志這個屬性集合的名字,其中的attr標志屬性。對于自定義屬性的類型,主要的有以下幾種
string字符串類型 reference引用類型,一般是指向另外的一個資源屬性 color顏色代碼 dimension尺寸 float浮點型 boolean布爾型 integer整型 enum枚舉型
當你定義完上面的文件,接下來我們需要在自定義View中解析它們,從而獲得用戶傳遞進來的屬性。 屬性的解析可以使用以下代碼完成
val array = context.obtainStyledAttributes(attributeSet, R.styleable.InputItemLayout)
val title = array.getString(R.styleable.InputItemLayout_title)
val titleResId = array.getResourceId(R.styleable.InputItemLayout_titleTextAppearance, 0)
上面的代碼中,第一句是通過obtainStyledAttributes解析上面XML文件中屬性名為InputItemLayout的屬性內容,并返回TypedArray,后續該命名空間中的所有屬性都可以通過TypedArray.getXX()來獲得XX是屬性類型。
但是引用類型除外,因為引用類型中還包含了其他屬性,所以需要如下代碼去提取屬性。
val array1 = context.obtainStyledAttributes(attributeSet, R.styleable.InputItemLayout)
val titleResId = array1.getResourceId(R.styleable.InputItemLayout_titleTextAppearance, 0)
val array = context.obtainStyledAttributes(titleResId, R.styleable.titleTextAppearance)
val titleColor = array.getColor(
R.styleable.titleTextAppearance_titleColor,
resources.getColor(R.color.color_565)
)
如上代碼,我們先獲取在InputItemLayout屬性中titleTextAppearance的屬性,這時候發現titleTextAppearance是一個引用類型的屬性,在使用 context.obtainStyledAttributes(titleResId, R.styleable.titleTextAppearance)獲取titleTextAppearance中的屬性值,第一個參數titleResId是titleTextAppearance的資源ID。 最終我們獲取了所有的屬性,這時候就可以開始自定義你的View了。
當我們最終完成了所有的代碼,怎么在布局文件中使用呢。對于普通的屬性,如String Int等就和平常一樣,但是對于引用類型,我們需要在style.xml文件中定義資源文件
<com.slowtd.tcommon.InputItemLayout
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="55dp"
app:hint="請輸入密碼"
app:title="密碼"
app:inputType="text"
app:titleTextAppearance="@style/titleTextAppearance"
app:inputTextAppearance="@style/inputTextAppearance_limitLength"
app:topLineAppearance="@style/lineAppearance"
app:bottomLineAppearance="@style/lineAppearance"
/>
<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="inputTextAppearance_limitLength" parent="inputTextAppearance"> <item name="maxInputLength">4</item> </style> <style name="titleTextAppearance"> <item name="titleColor">@color/color_565</item> <item name="titleSize">15sp</item> <item name="minWidth">100dp</item> </style> <style name="lineAppearance"> <item name="color">@color/black</item> <item name="height">2dp</item> <item name="leftMargin">0dp</item> <item name="rightMargin">0dp</item> <item name="enable">true</item> </style>
下面的代碼是一個簡單的自定義輸入框代碼,供大家參考,配合上面的XML屬性資源就可以使用了。
class InputItemLayout : LinearLayout {
private lateinit var titleView: TextView
private lateinit var editText: EditText
private var bottomLine: Line
private var topLine: Line
private var topPaint = Paint(Paint.ANTI_ALIAS_FLAG)
private var 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
) {
dividerDrawable = ColorDrawable()
showDividers = SHOW_DIVIDER_BEGINNING
//去加載 去讀取 自定義sytle屬性
orientation = HORIZONTAL
val array = context.obtainStyledAttributes(attributeSet, R.styleable.InputItemLayout)
//解析title 屬性
val title = array.getString(R.styleable.InputItemLayout_title)
val titleResId = array.getResourceId(R.styleable.InputItemLayout_titleTextAppearance, 0)
parseTitleStyle(title, titleResId)
//解析右側的輸入框屬性
val hint = array.getString(R.styleable.InputItemLayout_hint)
val inputResId = array.getResourceId(R.styleable.InputItemLayout_inputTextAppearance, 0)
val inputType = array.getInteger(R.styleable.InputItemLayout_inputType, 0)
parseInputStyle(hint, inputResId, inputType)
//上下分割線屬性
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()
}
@SuppressLint("CustomViewStyleable")
private fun parseLineStyle(resId: Int): Line {
val line = Line()
val array = context.obtainStyledAttributes(resId, R.styleable.lineAppearance)
line.color =
array.getColor(
R.styleable.lineAppearance_color,
ContextCompat.getColor(context, R.color.color_d1d2)
)
line.height = array.getDimensionPixelOffset(R.styleable.lineAppearance_height, 0).toFloat()
line.leftMargin =
array.getDimensionPixelOffset(R.styleable.lineAppearance_leftMargin, 0).toFloat()
line.rightMargin =
array.getDimensionPixelOffset(R.styleable.lineAppearance_rightMargin, 0).toFloat()
line.enable = array.getBoolean(R.styleable.lineAppearance_enable, false)
array.recycle()
return line
}
inner class Line {
var color = 0
var height = 0f
var leftMargin = 0f
var rightMargin = 0f;
var enable: Boolean = false
}
@SuppressLint("CustomViewStyleable")
private fun parseInputStyle(hint: String?, resId: Int, inputType: Int) {
val array = context.obtainStyledAttributes(resId, 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)
)
val maxInputLength = array.getInteger(R.styleable.inputTextAppearance_maxInputLength, 0)
editText = EditText(context)
if (maxInputLength > 0) {
editText.filters = arrayOf(InputFilter.LengthFilter(maxInputLength))//最多可輸入的字符數
}
editText.setPadding(0, 0, 0, 0)
val params = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT)
params.weight = 1f
editText.layoutParams = params
editText.hint = hint
editText.setTextColor(inputColor)
editText.setHintTextColor(hintColor)
editText.gravity = LEFT or (CENTER)
editText.setBackgroundColor(Color.TRANSPARENT)
editText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize.toFloat())
/**
* <enum name="text" value="0"></enum>
* <enum name="password" value="1"></enum>
* <enum name="number" value="2"></enum>
*/
if (inputType == 0) {
editText.inputType = InputType.TYPE_CLASS_TEXT
} else if (inputType == 1) {
editText.inputType =
InputType.TYPE_TEXT_VARIATION_PASSWORD or (InputType.TYPE_CLASS_TEXT)
} else if (inputType == 2) {
editText.inputType = InputType.TYPE_CLASS_NUMBER
}
addView(editText)
array.recycle()
}
@SuppressLint("CustomViewStyleable")
private fun parseTitleStyle(title: String?, resId: Int) {
val array = context.obtainStyledAttributes(resId, R.styleable.titleTextAppearance)
val titleColor = array.getColor(
R.styleable.titleTextAppearance_titleColor,
resources.getColor(R.color.color_565)
)
//px
val titleSize = array.getDimensionPixelSize(
R.styleable.titleTextAppearance_titleSize,
applyUnit(TypedValue.COMPLEX_UNIT_SP, 15f)
)
val minWidth = array.getDimensionPixelOffset(R.styleable.titleTextAppearance_minWidth, 0)
titleView = TextView(context)
titleView.setTextSize(TypedValue.COMPLEX_UNIT_PX, titleSize.toFloat()) //sp---當做sp在轉換一次
titleView.setTextColor(titleColor)
titleView.layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT)
titleView.minWidth = minWidth
titleView.gravity = LEFT or (CENTER)
titleView.text = title
addView(titleView)
array.recycle()
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(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
)
}
}
private fun applyUnit(applyUnit: Int, value: Float): Int {
return TypedValue.applyDimension(applyUnit, value, resources.displayMetrics).toInt()
}
fun getTitleView(): TextView {
return titleView
}
fun getEditText(): EditText {
return editText
}
}
原文鏈接:https://juejin.cn/post/7142758407844397063
相關推薦
- 2022-05-25 Entity?Framework?Core對Web項目生成數據庫表_實用技巧
- 2022-03-30 C++歸并排序算法詳解_C 語言
- 2023-04-21 numpy.insert()的具體使用方法_python
- 2022-07-21 python:實現balanced parentheses平衡括號表達式算法(附完整源碼)
- 2022-09-20 C#使用winform實現進度條效果_C#教程
- 2023-01-17 C#?wpf利用附加屬性實現界面上定義裝飾器_C#教程
- 2022-07-06 python?pandas遍歷每行并累加進行條件過濾方式_python
- 2023-12-24 http中的get和post方法的區別
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支