Android自定義視圖一:擴(kuò)展現(xiàn)有的視圖豪娜,添加新的XML屬性

這個(gè)系列是老外寫的,干貨哟楷!翻譯出來一起學(xué)習(xí)瘤载。如有不妥,不吝賜教卖擅!

  1. Android自定義視圖一:擴(kuò)展現(xiàn)有的視圖鸣奔,添加新的XML屬性
  2. Android自定義視圖二:如何繪制內(nèi)容
  3. Android自定義視圖三:給自定義視圖添加“流暢”的動(dòng)畫
  4. Android自定義視圖四:定制onMeasure強(qiáng)制顯示為方形

簡(jiǎn)介

這個(gè)系列詳細(xì)的介紹了如何穿件Android自定義視圖。主要涉及的內(nèi)容有如何繪制內(nèi)容惩阶,layout和measure的原理挎狸,如何繼承實(shí)現(xiàn)view group以及如何給其子視圖添加動(dòng)畫。第一篇主要講述如何擴(kuò)展和使用現(xiàn)有的視圖断楷,以及如何添加特有的XML屬性锨匆。

特定的任務(wù)使用特定的視圖

Android提供的view都是比較通用的,哪里都可以用冬筒。但是在開發(fā)應(yīng)用的過程中需要對(duì)這些通用的view加以修改恐锣。很多時(shí)候這些代碼都添加到了Activity中茅主,這樣是的Activity的代碼雜亂,影響維護(hù)侥蒙。

假設(shè)你在開發(fā)一個(gè)最用用戶訓(xùn)練數(shù)據(jù)的應(yīng)用暗膜。比如用戶的總運(yùn)動(dòng)時(shí)長(zhǎng),總運(yùn)動(dòng)距離以及不同的運(yùn)動(dòng)類型等鞭衩。為了友好的把數(shù)據(jù)展現(xiàn)給用戶学搜,你需要根據(jù)用戶運(yùn)動(dòng)的時(shí)間長(zhǎng)度做不同的處理。比如他運(yùn)動(dòng)了2378秒论衍,那么顯示的肯定是48分鐘瑞佩。18550秒,那顯示的肯定是5小時(shí)9分鐘坯台。

創(chuàng)建一個(gè)自定義視圖

處理上面的問題最好就是定義一個(gè)view炬丸。這個(gè)view里包含了處理上面時(shí)間的功能。我們來創(chuàng)建一個(gè)自定義view:DurationTextView蜒蕾。

class DurationTextView : TextView {
    constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet) {
    }
}

TextView和其他的view一樣有三個(gè)構(gòu)造函數(shù):一個(gè)是只需要Context的稠炬,一個(gè)是上面給出的需要兩個(gè)參數(shù)ContextAttributeSet,還有一個(gè)需要這兩個(gè)參數(shù)之外的關(guān)于style的參數(shù)咪啡。一般來說首启,上面給出的構(gòu)造方法就可以滿足。下面在布局中使用上面定義的view撤摸。

<demo.customview.customviewdemo.Views.DurationTextView
    android:id="@+id/duration_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

注意毅桃,這里需要給出自定義視圖的全名稱。

添加展示邏輯

現(xiàn)在這個(gè)視圖和標(biāo)準(zhǔn)的TextView沒什么太大的區(qū)別准夷。我們?yōu)檫@個(gè)view添加一個(gè)duration屬性來設(shè)置和讀取duration值钥飞。

var duration: Float
get() = _duration
set(value) {
    _duration = value

    var durationInMinutes: Int = Math.round(_duration / 60)
    var hours: Int = durationInMinutes / 60
    var minutes: Int = durationInMinutes % 60

    var hourText: String = ""
    var minuteText: String = ""

    if (hours > 0) {
        hourText = "$hours ${if (hours == 1) "hour" else "hours"}"
    }

    if (minutes > 0) {
        minuteText = "$minutes ${if (minutes == 1) "minute" else "minutes"}"
    }

    if (hours == 0 && minutes == 0) {
        minuteText = "Less than 1 minute"
    }

    var durationText = TEXT_TEMPLATE.format(hourText, minuteText)

    text = durationText
}

companion object {
    val TEXT_TEMPLATE = "Duration: %s %s"
}

這個(gè)方法接受一個(gè)Float型的參數(shù),訓(xùn)練時(shí)間的秒數(shù)衫嵌。然后通過上面的轉(zhuǎn)換邏輯把這個(gè)秒數(shù)轉(zhuǎn)成用戶友好的文字值读宙。最后賦值給TextView的text。轉(zhuǎn)換的邏輯非常簡(jiǎn)單渐扮,就是把秒數(shù)轉(zhuǎn)為分鐘數(shù)论悴,最后轉(zhuǎn)為小時(shí)和分鐘的值。分鐘和小時(shí)數(shù)如果大于1的時(shí)候單位就顯示為minutes和hours墓律,等于1的時(shí)候?yàn)閙inute和hour膀估。

用起來

現(xiàn)在就可以試試效果了。在Activity的onCreate()方法里添加下面的代碼:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    var durationTextView1 = findViewById(R.id.duration_view1) as DurationTextView
    durationTextView1.duration = 2378.0f

    var durationTextView2 = findViewById(R.id.duration_view2) as DurationTextView
    durationTextView2.duration = 3670.0f

    var durationTextView3 = findViewById(R.id.duration_view3) as DurationTextView
    durationTextView3.duration = 18550.0f
}

運(yùn)行結(jié)果如下圖:



看起來還不錯(cuò)吧耻讽。

添加XML屬性

如果我們能夠這個(gè)view添加一個(gè)方法來設(shè)置文本展示的模板察纯,就像設(shè)定duration一樣。但是,這個(gè)template其實(shí)并不會(huì)想duration一樣需要經(jīng)常的設(shè)置饼记,更多的是一個(gè)類似于常數(shù)一樣的存在香伴。所以對(duì)于添加一個(gè)方法來說,添加一個(gè)XML屬性更加合適具则。

首先添加values/attrs.xml文件即纲,XML屬性就在這個(gè)文件中定義。我們只需要添加一個(gè)字符串類型的博肋,名稱為template的屬性低斋。添加之后attrs.xml文件看起來是這樣的:

<resources>
    <declare-styleable name="TemplateTextView">
        <attr name="template" format="string" />
    </declare-styleable>
</resources>

我們聲明了一個(gè)名稱為TemplateTextViewdeclare-styleable節(jié)點(diǎn)。名字可以任意起匪凡,不過最好還是在哪個(gè)view里使用就叫做什么膊畴。這里叫做TemplateTextView是因?yàn)楹竺孢@個(gè)XML屬性會(huì)用與很多其他的自定義view中。來看看是怎么使用這個(gè)屬性的病游。

<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="demo.customview.customviewdemo.MainActivity">

    <demo.customview.customviewdemo.Views.DurationTextView
        android:id="@+id/duration_view1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Customized"
        android:textSize="20sp"
        app:template="%s was spent running" />
        
        <!-- ...... -->

</LinearLayout>

在格式化代碼(快捷鍵Ctrl+Alt+L唇跨,或者Cmd+Alt+L)后在LinearLayout中會(huì)增加一行xmlns:app="http://schemas.android.com/apk/res-auto"這一句聲明了一個(gè)namespace,這樣APP就可以理解我們剛剛在自定義view的布局中添加的屬性了衬衬。當(dāng)然需要使用這條聲明里指定的app為前綴:app:template="%s was spent running"买猖。

現(xiàn)在就需要我們?cè)诖a里讀取并應(yīng)用新添加的屬性值了。首先添加一個(gè)var template:String? = null的屬性滋尉,準(zhǔn)備接收解析的字符串模板政勃。

var attributes = context.obtainStyledAttributes(attributeSet, R.styleable.TemplateTextView)
template = attributes.getString(R.styleable.TemplateTextView_template)

if (template == null || !(template?.contains("%s", ignoreCase = true) ?: false)) {
    template = TEXT_TEMPLATE
}

attributes.recycle()

第一行使用了AttributeSet類型的參數(shù)attributeSet,這個(gè)參數(shù)包含了在attrs.xml文件中定義的全部的屬性兼砖。 方法obtainStyledAttributes()主要做了兩件事:

  1. 過濾全部的自定義屬性,并把這些屬性的定義和給定的值關(guān)聯(lián)起來既棺。
  2. 返回你在第二個(gè)參數(shù)所指定的屬性組合讽挟。

R.styleable.TemplateTextView就是我們自己定義的屬性數(shù)組,這里只有一個(gè)元素丸冕。R.styleable.TemplateTextView_template是我們自定義屬性數(shù)組里的那個(gè)叫做template的元素耽梅。然后我們使用typed array來獲取這個(gè)屬性的值。如果沒有設(shè)置模板胖烛,或者模板中不包含處理字符串的%s的話就是用我們之前定義的默認(rèn)的文字模板來代替眼姐。

但是有一點(diǎn)需要格外注意:不管有沒有獲取到屬性值,都要回收TypedArray對(duì)象佩番,attributes.recycle()众旗。

上下兩個(gè)分別設(shè)定了不同的template,中間的不設(shè)定趟畏,運(yùn)行一下看看修改后的效果如何:


對(duì)于大多數(shù)的Android視圖XML屬性都有對(duì)應(yīng)的方法來通過代碼的方式設(shè)置對(duì)應(yīng)的值贡歧。對(duì)于自定義視圖是否需要這么做,主要取決于你打算怎么用。添加一個(gè)方法也沒什么問題利朵。

下一篇主要簡(jiǎn)述如何繪制視圖的內(nèi)容律想,并自定義一個(gè)顯示折線圖的視圖。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末绍弟,一起剝皮案震驚了整個(gè)濱河市技即,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌樟遣,老刑警劉巖而叼,帶你破解...
    沈念sama閱讀 216,919評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異年碘,居然都是意外死亡澈歉,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門屿衅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來埃难,“玉大人,你說我怎么就攤上這事涤久∥谐荆” “怎么了?”我有些...
    開封第一講書人閱讀 163,316評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵响迂,是天一觀的道長(zhǎng)考抄。 經(jīng)常有香客問我,道長(zhǎng)蔗彤,這世上最難降的妖魔是什么川梅? 我笑而不...
    開封第一講書人閱讀 58,294評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮然遏,結(jié)果婚禮上贫途,老公的妹妹穿的比我還像新娘。我一直安慰自己待侵,他們只是感情好丢早,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,318評(píng)論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著秧倾,像睡著了一般怨酝。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上那先,一...
    開封第一講書人閱讀 51,245評(píng)論 1 299
  • 那天农猬,我揣著相機(jī)與錄音,去河邊找鬼售淡。 笑死盛险,一個(gè)胖子當(dāng)著我的面吹牛瞄摊,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播苦掘,決...
    沈念sama閱讀 40,120評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼换帜,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了鹤啡?” 一聲冷哼從身側(cè)響起惯驼,我...
    開封第一講書人閱讀 38,964評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎递瑰,沒想到半個(gè)月后祟牲,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,376評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡抖部,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,592評(píng)論 2 333
  • 正文 我和宋清朗相戀三年说贝,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片慎颗。...
    茶點(diǎn)故事閱讀 39,764評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡乡恕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出俯萎,到底是詐尸還是另有隱情傲宜,我是刑警寧澤,帶...
    沈念sama閱讀 35,460評(píng)論 5 344
  • 正文 年R本政府宣布夫啊,位于F島的核電站函卒,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏撇眯。R本人自食惡果不足惜报嵌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,070評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望熊榛。 院中可真熱鬧沪蓬,春花似錦、人聲如沸来候。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,697評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)营搅。三九已至,卻和暖如春梆砸,著一層夾襖步出監(jiān)牢的瞬間转质,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,846評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工帖世, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留休蟹,地道東北人沸枯。 一個(gè)月前我還...
    沈念sama閱讀 47,819評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像赂弓,于是被迫代替她去往敵國(guó)和親绑榴。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,665評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容