Android如何通過組合的方式自定義View
前言:
自定義View可以分為兩種方式:
- 第一種通過繼承ViewGroup,內(nèi)部通過addView的方式將其他的View組合到一起。
- 第二種則是通過繼承View,重啟View的onMeasure,onLayout,onDraw方法來繪制不規(guī)則圖形,如折線圖等。
本文介紹的是第一種方式通過組合的方式去實(shí)現(xiàn)自定義View。
實(shí)現(xiàn)自定義View首先要自定義屬性。對(duì)于自定義屬性,第一步是在項(xiàng)目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)志這個(gè)屬性集合的名字,其中的attr標(biāo)志屬性。對(duì)于自定義屬性的類型,主要的有以下幾種
string字符串類型 reference引用類型,一般是指向另外的一個(gè)資源屬性 color顏色代碼 dimension尺寸 float浮點(diǎn)型 boolean布爾型 integer整型 enum枚舉型
當(dāng)你定義完上面的文件,接下來我們需要在自定義View中解析它們,從而獲得用戶傳遞進(jìn)來的屬性。 屬性的解析可以使用以下代碼完成
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是屬性類型。
但是引用類型除外,因?yàn)橐妙愋椭羞€包含了其他屬性,所以需要如下代碼去提取屬性。
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的屬性,這時(shí)候發(fā)現(xiàn)titleTextAppearance是一個(gè)引用類型的屬性,在使用 context.obtainStyledAttributes(titleResId, R.styleable.titleTextAppearance)獲取titleTextAppearance中的屬性值,第一個(gè)參數(shù)titleResId是titleTextAppearance的資源ID。 最終我們獲取了所有的屬性,這時(shí)候就可以開始自定義你的View了。
當(dāng)我們最終完成了所有的代碼,怎么在布局文件中使用呢。對(duì)于普通的屬性,如String Int等就和平常一樣,但是對(duì)于引用類型,我們需要在style.xml文件中定義資源文件
<com.slowtd.tcommon.InputItemLayout android:layout_marginTop="10dp" android:layout_width="match_parent" android:layout_height="55dp" app:hint="請(qǐng)輸入密碼" 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>
下面的代碼是一個(gè)簡(jiǎn)單的自定義輸入框代碼,供大家參考,配合上面的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)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
android 獲取視頻,圖片縮略圖的具體實(shí)現(xiàn)
android 獲取視頻,圖片縮略圖的具體實(shí)現(xiàn),需要的朋友可以參考一下2013-06-06Android WebView如何判斷是否滾動(dòng)到底部
大家好,本篇文章主要講的是Android WebView如何判斷是否滾動(dòng)到底部,感興趣的同學(xué)趕快來看一看吧,對(duì)你有幫助的話記得收藏一下2022-01-01Android中socket通信的簡(jiǎn)單實(shí)現(xiàn)
這篇文章主要為大家詳細(xì)介紹了Android中socket通信的簡(jiǎn)單實(shí)現(xiàn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-04-04Android提高之MediaPlayer播放網(wǎng)絡(luò)音頻的實(shí)現(xiàn)方法
這篇文章主要介紹了Android的MediaPlayer播放網(wǎng)絡(luò)音頻的實(shí)現(xiàn)方法,很實(shí)用的功能,需要的朋友可以參考下2014-08-08Android實(shí)現(xiàn)啟動(dòng)頁(yè)倒計(jì)時(shí)效果
這篇文章主要介紹了Android實(shí)現(xiàn)啟動(dòng)頁(yè)倒計(jì)時(shí)效果的示例代碼,幫助大家更好的理解和學(xué)習(xí)使用Android進(jìn)行開發(fā),感興趣的朋友可以了解下2021-04-04Android ProgressBar進(jìn)度條使用詳解
ProgressBar進(jìn)度條,分為旋轉(zhuǎn)進(jìn)度條和水平進(jìn)度條,進(jìn)度條的樣式根據(jù)需要自定義,這篇文章主要介紹了Android ProgressBar進(jìn)度條使用方法,感興趣的小伙伴們可以參考一下2016-02-02Android 破解視頻App去除廣告功能詳解及解決辦法總結(jié)
這篇文章主要介紹了Android 破解視頻App去除廣告功能詳解及解決辦法總結(jié)的相關(guān)資料,這里對(duì)視頻播放原理及破解去除廣告幾種方法進(jìn)行了總結(jié),需要的朋友可以參考下2016-12-12Android Naive與WebView的互相調(diào)用詳解
這篇文章主要介紹了Android Naive與WebView的互相調(diào)用詳解的相關(guān)資料,需要的朋友可以參考下2017-05-05微信小程序電商常用倒計(jì)時(shí)實(shí)現(xiàn)實(shí)例
這篇文章主要介紹了微信小程序電商常用倒計(jì)時(shí)實(shí)現(xiàn)實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-06-06Android 三行代碼實(shí)現(xiàn)高斯模糊效果
這篇文章主要介紹了Android 三行代碼實(shí)現(xiàn)高斯模糊效果,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09