android自定義drawable的state屬性

android自定義drawable的state屬性

在drawable中使用selector在開發中太常用了,不過用的最多是根據系統提供的一些狀態來選擇圖片,比如:android:state_checkedandroid:state_pressed等等,其實這些狀態也是可以自定義的,比方說天氣狀況有很多種情況,天晴、多雲、下雨等等,那能不能根據這些狀況來顯示不同的示意圖呢?這裏是指通過 selector的方式來實現。答案是肯定的,下面的效果圖就是用selector來實現的。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-R1Zgi0aC-1584662459361)(https://raw.githubusercontent.com/MingHuang1024/illustrations/master/drawable_state.gif)]

上面動畫中的天氣圖標、背景、文字顏色都是用selector來實現的,沒有在代碼中根據不同天氣設置圖片等,不過文字是在代碼中改變的。

下面來看下怎麼實現:

一、定義表示狀態的屬性

在res-values目錄中新建一個attrs.xml文件(已有則不必新建),定義以下三個屬性,代表天氣中的天晴、多雲、下雨

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="Weather">
    <attr name="state_sunny" format="boolean"/>
    <attr name="state_raining" format="boolean"/>
    <attr name="state_cloudy" format="boolean"/>
    </declare-styleable>
</resources>

二、使用selector定義drawable

在res-drawable中新建一個weather_icon.xml文件,根據天氣狀態來顯示相應的圖片

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" 	
		  xmlns:example="http://schemas.android.com/apk/res-auto">

    <item android:drawable="@drawable/sunny" example:state_sunny="true" />

    <item android:drawable="@drawable/raining" example:state_raining="true" />

    <item android:drawable="@drawable/cloudy" example:state_cloudy="true" />

</selector>

或者根據天氣情況顯示不同的顏色,如下:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" 
		  xmlns:example="http://schemas.android.com/apk/res-auto">

    <item android:drawable="@color/sunnyColor" example:state_sunny="true" />

    <item android:drawable="@color/rainingColor" example:state_raining="true" />

    <item android:drawable="@color/cloudyColor" example:state_cloudy="true" />

</selector>

三、定義組件

接下來定義一個組件,這個組件用於接收不同的天氣狀況,如果該組件或其子view使用到了以上定義的圖片選擇器,這個組件或者其子view就會根據不同的天氣狀況顯示出不的圖片或顏色

爲了達到這個目的,該組件一定要重寫View的onCreateDrawableState()方法,如下:

override fun onCreateDrawableState(extraSpace: Int): IntArray? {
        Log.d("WeatherView", "onCreateDrawableState...")
        // 注意,這裏要給數組添加一個容量,用於儲存自定義的狀態
        val drawableState = super.onCreateDrawableState(extraSpace + 1)
        when (mWeather) {
            Weather.SUNNY -> {
                mergeDrawableStates(drawableState, STATE_SUNNY)
            }
            Weather.RAINING -> {
                mergeDrawableStates(drawableState, STATE_RAINING)
            }
            Weather.CLOUDY -> {
                mergeDrawableStates(drawableState, STATE_CLOUDY)
            }
        }
        Log.d("WeatherView", mWeather.name)
        return drawableState
    }

其作用是創建圖片狀態,在這個方法中將自定義的狀態添加到圖片狀態列表中去,這樣系統就能識別自定義的狀態,使得自定義的圖片選擇器生效。

以上代碼中的STATE_SUNNY等狀態是一個常量,就是第一步中自定義的屬性,不過它是一個數組

private val STATE_SUNNY = intArrayOf(R.attr.state_sunny)
private val STATE_CLOUDY = intArrayOf(R.attr.state_cloudy)
private val STATE_RAINING = intArrayOf(R.attr.state_raining)

Weather是一個枚舉類,代表着天氣的狀況,

enum class Weather {
    SUNNY,
    RAINING,
    CLOUDY
}

可以通過重置mWeather的值來改變組件的狀態。

 fun setWeather(weather: Weather) {
        Log.d("WeatherView", "setWeather...")
        if (mWeather!=weather) {
            mWeather = weather
            // 刷新狀態
            refreshDrawableState()
      	  }
   }

再看一下這個組件的佈局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/weather_bg"
    android:duplicateParentState="true"
    android:orientation="horizontal"
    android:padding="50dp">

    <ImageView
        android:id="@+id/ivIcon"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:layout_gravity="center"
        android:layout_marginTop="20dp"
        android:layout_marginBottom="20dp"
        android:background="@drawable/weather_icon"
        android:duplicateParentState="true" />

    <TextView
        android:id="@+id/tv"
        android:layout_gravity="center_vertical"
        android:layout_marginLeft="30dp"
        android:gravity="center"
        android:textSize="30sp"
        android:layout_width="100dp"
        android:layout_height="80dp"
        android:background="#ffffff"
        android:duplicateParentState="true"
        android:text="天氣"
        android:textColor="@color/text_color" />
</LinearLayout>

這裏有幾個地方需要注意:

1、根佈局一定要設置android:duplicateParentState="true",表示複製父控件的狀態,這樣才能感受到自定義狀態的變化。容易忽略的一點是,以爲xml中的根佈局就是自定義組件的根佈局,如通過以下方式加載佈局:

inflate(context, R.layout.weather_view, this)

其實這裏inflate出來的view是控件的子view,要想子view接受父控件的狀態就要設置duplicateParentState爲true

建議自定義控件的根佈局使用merge,可以減少一層佈局,提高性能。另,使用merge的話不需要設置duplicateParentState爲true。

2、在需要根據狀態來改變背景、src、或文字顏色等的子view中設置前面定義的圖片選擇器,並且這些子view都要設置android:duplicateParentState="true",如:

android:background="@drawable/weather_bg"
android:duplicateParentState="true"
或
android:duplicateParentState="true"
android:text="天氣"
android:textColor="@color/text_color" 

四、使用控件

接下來看一下效果,在activity中引入自定義組件,並用一個按鈕來改變組件的狀態,看看組件內的子view能不能根據狀態選擇不同的背景或文字顏色

<TextView
    android:id="@+id/textView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="60dp"
    android:textSize="25sp"
    android:padding="40dp"
    android:text="點擊改變天氣"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

<com.example.multiplestateview.WeatherView
    android:id="@+id/weatherView"
    android:padding="20dp"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/textView" />

activity中的點擊事件:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    textView.setOnClickListener {
        when (weatherView.getWeather()){
            Weather.SUNNY->{
                weatherView.setWeather(Weather.CLOUDY)
            }
            Weather.CLOUDY->{
                weatherView.setWeather(Weather.RAINING)
            }
            Weather.RAINING->{
                weatherView.setWeather(Weather.SUNNY)
            }
        }
    }
}

好了,這樣就實現了根據自定義的狀態來選擇不同的圖片了,還是挺簡單的,用上的話可以讓代碼看起來清爽不少。

源碼:

demo源碼:https://github.com/MingHuang1024/CustomDrawableState




由於水平有限,如果文中存在錯誤之處,請大家批評指正,歡迎大家一起來分享、探討!

博客:http://blog.csdn.net/MingHuang2017

GitHub:https://github.com/MingHuang1024

Email: [email protected]

誤之處,請大家批評指正,歡迎大家一起來分享、探討!

博客:http://blog.csdn.net/MingHuang2017

GitHub:https://github.com/MingHuang1024

Email: [email protected]

微信:724360018

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章