Android 테마 전환 (theme), 언어 전환, 사용자 정의 속성 (attr) 값 동적 가져오기

1. 개발 환경: 안드로이드 스튜디오 3.4.0,kotlin 구현 2.개발 준비 ①values에 자원 파일을 추가합니다 ["new"→"Values resouce file"]는 "custom theme attrs"로 정의되며 내용은 다음과 같습니다.
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--   app    format:-->
    <attr name="custom_attr_app_bg" format="color|reference" />
    <!--   app       format:-->
    <attr name="custom_attr_app_title_layout_bg" format="color|reference" />
    <!--         Drawable format:-->
    <attr name="custom_attr_user_photo_place_holder" format="color|reference" />
    <!--          format:-->
    <attr name="custom_attr_nickname_text_color" format="color|reference" />
    <!--          format:-->
    <attr name="custom_attr_remark_text_color" format="color|reference" />
    <!--            format:-->
    <attr name="custom_attr_user_photo_alpha" format="dimension|reference" />

    <attr name="titlebar_bg" format="color|reference"/>
    <attr name="titlebar_text_color" format="color|reference"/>
    <attr name="body_bg" format="color|reference"/>
    <attr name="body_text_color" format="color|reference"/>
    <attr name="title" format="string"/>
</resources>

② 리소스 파일 "styles"에 다음 코드 추가【테마 추가】
<!---->
    <style name="theme_day" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="titlebar_bg">#1EABF0</item>
        <item name="titlebar_text_color">#FFFFFF</item>
        <item name="body_bg">#F3F3F3</item>
        <item name="body_text_color">#333333</item>
    </style>
    
    <!---->
    <style name="theme_night" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="titlebar_bg">#000000</item>
        <item name="titlebar_text_color">#FFFFFF</item>
        <item name="body_bg">#888888</item>
        <item name="body_text_color">#EAEAEA</item>
    </style>

    <!-- theme_day.chi_sim     theme_day -->
    <style name="theme_day.chi_sim">
        <item name="title">    </item>
    </style>
    <style name="theme_night.eng">
        <item name="title">TopicConfig</item>
    </style>

3. 관찰자 인터페이스 추가하기
package com.zjhj.maxapp.theme

interface ThemeChangeObserver {
    /**
     *       
     */
    fun loadingCurrentTheme()

    /**
     *        
     */
    fun notifyThemeChanged()
}

4.BaseActivity에서 인터페이스 구현
package com.zjhj.maxapp.base

import android.content.Context
import android.os.Bundle
import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity
import com.zjhj.maxapp.App
import com.zjhj.maxapp.R
import com.zjhj.maxapp.theme.ThemeChangeObserver
import com.zjhj.maxapp.utils.L //      


/**
 * CreateTime 2020/4/2 09:10
 * Author LiuShiHua
 * Description:
 */
abstract class BaseActivity : AppCompatActivity(), ThemeChangeObserver {

    private val KEY_THEME_TAG = "myThemeTag"
    private var isChangeTheme: Boolean = false
    override fun onCreate(savedInstanceState: Bundle?) {
        window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
        window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
        setThemeBeforeCreate()
        super.onCreate(savedInstanceState)
        setContentView()
        initView()
        initData()
        getData()
    }

    /**
     */
    private fun setThemeBeforeCreate() {
        App.registerObserver(this)//   Acitivity      
        loadingCurrentTheme()
    }

    /**
     *       
     *    super.onCreate(savedInstanceState)        
     */
    override fun loadingCurrentTheme() {
        when (getThemeTag()) {
            1 -> setTheme(R.style.theme_day_chi_sim)
            -1 -> setTheme(R.style.theme_night_eng)
        }
        L.d("loadingCurrentTheme:" + this::class.java)
    }

    abstract fun setContentView()
    abstract fun initView()
    abstract fun initData()
    abstract fun getData()

    /**
     *         
     */
    protected open fun getThemeTag(): Int {
        val preferences = getSharedPreferences("MaxTheme", Context.MODE_PRIVATE)
        return preferences.getInt(KEY_THEME_TAG, 1)
    }

    /**
     *       
     *    sharedprferences 
     */
    protected open fun setThemeTag(tag: Int) {
        L.d("setThemeTag:" + tag)
        val preferences = getSharedPreferences("MaxTheme", Context.MODE_PRIVATE)
        val edit = preferences.edit()
        edit.putInt(KEY_THEME_TAG, tag)
        edit.commit()
        App.notifyByThemeChanged()
    }

    /**
     *        
     */
    override fun onDestroy() {
        App.unregisterObserver(this)
        super.onDestroy()
    }
}

5. APP에서 관찰자 관리
package com.zjhj.maxapp

import android.app.Application
import android.content.Context
import com.zjhj.maxapp.theme.ThemeChangeObserver


/**
 * CreateTime 2020/4/9 09:40
 * Author LiuShiHua
 * Description:
 */
class App : Application() {


    companion object {//       
        lateinit var context: Context
        get

        private var mThemeChangeObserverStack: MutableList<ThemeChangeObserver>? = null

        /**
         *   observer  
         */
        private fun obtainThemeChangeObserverStack(): MutableList<ThemeChangeObserver>? {
            if (mThemeChangeObserverStack == null) mThemeChangeObserverStack = ArrayList()
            return mThemeChangeObserverStack
        }

        /**
         *       observer
         */
        fun registerObserver(observer: ThemeChangeObserver?) {
            if (observer == null || obtainThemeChangeObserverStack()!!.contains(observer)) return
            obtainThemeChangeObserverStack()!!.add(observer)
        }

        /**
         *       observer
         */
        fun unregisterObserver(observer: ThemeChangeObserver?) {
            if (observer == null || !obtainThemeChangeObserverStack()!!.contains(observer)) return
            obtainThemeChangeObserverStack()!!.remove(observer)
        }

        /**
         *             UI   
         */
        fun notifyByThemeChanged() {
            val observers: List<ThemeChangeObserver>? = obtainThemeChangeObserverStack()
            for (observer in observers!!) {
                observer.loadingCurrentTheme() //
                observer.notifyThemeChanged() //
            }
        }
    }

    override fun onCreate() {
        super.onCreate()
        context = this
    }
}

5.BaseActivity 인스턴스에서 교체 주제 호출
package com.zjhj.maxapp

import com.zjhj.maxapp.base.BaseActivity
import kotlinx.android.synthetic.main.activity_theme.*

class ThemeActivity : BaseActivity() {
    override fun setContentView() {
        setContentView(R.layout.activity_theme)
    }

    override fun initView() {
        changeTheme.setOnClickListener {
            if (getThemeTag()==1) {//    
                setThemeTag(-1)
            } else {
                setThemeTag(1)
            }
            recreate()//      
        }
    }

    override fun initData() {

    }

    override fun getData() {

    }

    override fun notifyThemeChanged() {

    }
}


6. 설명: 인터넷에서 recreate()를 사용하여 페이지를 재구성하면 된다는 조언이 있습니다. 실제적으로 많은 Activity는 Recreate()를 사용하여 페이지를 재구성할 수 없거나 이전의 페이지 정보를 저장하고 싶어서 페이지를 재구성하지 않으려고 합니다. 저는 Activity에서 notify ThemeChanged에서 해당 주제의 속성 값을 다시 설정합니다[느낌 비교 low]
...
 override fun notifyThemeChanged() {
        val typedValue = TypedValue()
        //        titlebar_bg   ,   typedValue
        theme.resolveAttribute(R.attr.titlebar_bg, typedValue, true)
        //     【  :       ,            】
        toolBar.setBackgroundColor(typedValue.data)
    }
...

실현 효과: 테마 설정도 테마를 바꾼 후 다른 페이지와 이 변화

좋은 웹페이지 즐겨찾기