Android如何通過組合的方式自定義View
前言:
自定義View可以分為兩種方式:
- 第一種通過繼承ViewGroup,內(nèi)部通過addView的方式將其他的View組合到一起。
- 第二種則是通過繼承View,重啟View的onMeasure,onLayout,onDraw方法來繪制不規(guī)則圖形,如折線圖等。
本文介紹的是第一種方式通過組合的方式去實現(xiàn)自定義View。
實現(xiàn)自定義View首先要自定義屬性。對于自定義屬性,第一步是在項目res/values文件夾中新建attrs.xml文件,在文件中設(shè)置自定義屬性的名稱和類型,
代碼如下:
<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標(biāo)簽中,name屬性標(biāo)志這個屬性集合的名字,其中的attr標(biāo)志屬性。對于自定義屬性的類型,主要的有以下幾種
string字符串類型 reference引用類型,一般是指向另外的一個資源屬性 color顏色代碼 dimension尺寸 float浮點型 boolean布爾型 integer整型 enum枚舉型
當(dāng)你定義完上面的文件,接下來我們需要在自定義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的屬性內(nèi)容,并返回TypedArray,后續(xù)該命名空間中的所有屬性都可以通過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的屬性,這時候發(fā)現(xiàn)titleTextAppearance是一個引用類型的屬性,在使用 context.obtainStyledAttributes(titleResId, R.styleable.titleTextAppearance)獲取titleTextAppearance中的屬性值,第一個參數(shù)titleResId是titleTextAppearance的資源ID。 最終我們獲取了所有的屬性,這時候就可以開始自定義你的View了。
當(dāng)我們最終完成了所有的代碼,怎么在布局文件中使用呢。對于普通的屬性,如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)
//解析右側(cè)的輸入框?qū)傩?
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))//最多可輸入的字符數(shù)
}
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---當(dāng)做sp在轉(zhuǎn)換一次
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
}
}到此這篇關(guān)于Android如何通過組合的方式自定義View的文章就介紹到這了,更多相關(guān)Android自定義View內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android提高之MediaPlayer播放網(wǎng)絡(luò)音頻的實現(xiàn)方法
這篇文章主要介紹了Android的MediaPlayer播放網(wǎng)絡(luò)音頻的實現(xiàn)方法,很實用的功能,需要的朋友可以參考下2014-08-08
Android 破解視頻App去除廣告功能詳解及解決辦法總結(jié)
這篇文章主要介紹了Android 破解視頻App去除廣告功能詳解及解決辦法總結(jié)的相關(guān)資料,這里對視頻播放原理及破解去除廣告幾種方法進行了總結(jié),需要的朋友可以參考下2016-12-12
Android Naive與WebView的互相調(diào)用詳解
這篇文章主要介紹了Android Naive與WebView的互相調(diào)用詳解的相關(guān)資料,需要的朋友可以參考下2017-05-05

