android多状态视图_在继承的自定义视图中处理Android状态更改

android多状态视图

Works with Non-inherited and Inherited Custom View

与非继承和继承的自定义视图一起使用

In Android, there are some behaviors that we called “State Changes” which can occur from many conditions. Whether the platform might kill the application to reclaim the memory for the currently active app or Configuration Changes which recreated the Context in running Activity with the new configurations. To keep the data in your application from these changes, we have to handle the state in each Activity, Fragment and View.

在Android中,我们将某些行为称为“状态更改”,这些行为可能会在许多情况下发生。 平台是否可能杀死应用程序以回收当前活动应用程序的内存,或者是配置更改(使用新配置在运行Activity时重新创建了上下文)。 为了使应用程序中的数据不受这些更改的影响,我们必须处理每个Activity,Fragment和View中的状态。

Using existing Views from Android Frameworks or 3rd-party Libraries (which handle the state changes correctly), the state changes will be handled by itself. But when you create your own (called “Custom View”) you have to put the additional code for handling the state changes in your Custom View.

使用来自Android框架或第三方库(可正确处理状态更改)的现有视图,状态更改将由其自身处理。 但是,当您创建自己的(称为“自定义视图”)时,必须在“自定义视图”中放置用于处理状态更改的其他代码。

For example, my PostView contains 3 states (title, description and dividerColorResId) inside.

例如,我的PostView包含3个状态( titledescriptiondividerColorResId )。

class PostView : FrameLayout {
    private var title: String? = null
    private var description: String? = null
    private var dividerColorResId: Int = 0
    ...
}

To support the state changes, I have to implement onSaveInstanceState and onRestoreInstanceState and handle all states in there.

为了支持状态更改,我必须实现onSaveInstanceStateonRestoreInstanceState并在那里处理所有状态。

class PostView : FrameLayout {
    private var title: String? = null
    private var description: String? = null
    private var dividerColorResId: Int = 0
    ...
    override fun onSaveInstanceState(): Parcelable? {
        val superState: Parcelable? = super.onSaveInstanceState()
        superState?.let {
            val state = SavedState(superState)
            state.title = this.title
            state.description = this.description
            state.dividerColorResId = this.dividerColorResId
            return state
        } ?: run {
            return superState
        }
    }


    override fun onRestoreInstanceState(state: Parcelable?) {
        when (state) {
            is SavedState -> {
                super.onRestoreInstanceState(state.superState)
                this.title = state.title
                this.description = state.description
                this.dividerColorResId = state.dividerColorResId
                // Restore view's state here
            }
            else -> {
                super.onRestoreInstanceState(state)
            }
        }
    }


    internal class SavedState : BaseSavedState {
        var title: String? = null
        var description: String? = null
        var dividerColorResId: Int = 0


        constructor(superState: Parcelable) : super(superState)


        constructor(source: Parcel) : super(source) {
            title = source.readString()
            description = source.readString()
            dividerColorResId = source.readInt()
        }


        override fun writeToParcel(out: Parcel, flags: Int) {
            super.writeToParcel(out, flags)
            out.writeString(title)
            out.writeString(description)
            out.writeInt(dividerColorResId)
        }


        companion object {
            @JvmField
            val CREATOR: Parcelable.Creator<SavedState> = object : Parcelable.Creator<SavedState> {
                override fun createFromParcel(source: Parcel): SavedState {
                    return SavedState(source)
                }


                override fun newArray(size: Int): Array<SavedState> {
                    return newArray(size)
                }
            }
        }
    }
}

As you see, handling state changes, you need SavedState which extends from BaseSavedState to transform the data to Parcelable when onSaveInstanceState called, and onRestoreInstanceState to restore the data back from Parcelable.

正如你看到的,处理状态的变化,你需要SavedState从延伸BaseSavedState将数据转换到Parcelable时onSaveInstanceState调用, onRestoreInstanceState恢复从Parcelable回数据。

But the Android system does not hold the Parcelable while the state changes directly. Parcelable will be converted to Parcel and held in the system, and convert back to Parcelable when state restoring. That is why we have to declare the CREATOR which create from Parcelable.Creator to convert Parcel back to SavedState (Parcelable) by createFromParcel

但是,当状态直接更改时,Android系统不会保留Parcelable。 Parcelable将转换为Parcel并保留在系统中,并在恢复状态时转换回Parcelable。 这就是为什么我们要声明的CREATOR从创建Parcelable.Creator转换包裹回SavedState由(Parcelable) createFromParcel

This code is a popular example of handling state changes in Custom View.

此代码是在“自定义视图”中处理状态更改的流行示例。

Unfortunately, it is not working with Inherited Custom View.

不幸的是,它不能与Inherited Custom View一起使用

继承的自定义视图,如何? (Inherited Custom View, How?)

Instead of a single (non-inherited) Custom View, separated the code into a base class and derived class.

将代码分成基类和派生类,而不是单个(非继承)自定义视图。

Image for post

The code in BasePostView will be

BasePostView的代码将是

abstract class BasePostView : FrameLayout {
    private var title: String? = null
    private var description: String? = null
	...
    override fun onSaveInstanceState(): Parcelable? {
        val superState: Parcelable? = super.onSaveInstanceState()
        superState?.let {
            val state = SavedState(superState)
            state.title = this.title
            state.description = this.description
            return state
        } ?: run {
            return superState
        }
    }


    override fun onRestoreInstanceState(state: Parcelable?) {
        when (state) {
            is SavedState -> {
                super.onRestoreInstanceState(state.superState)
                this.title = state.title
                this.description = state.description
                // Update view's state here
            }
            else -> {
                super.onRestoreInstanceState(state)
            }
        }
    }


    internal class SavedState : BaseSavedState {
        var title: String? = null
        var description: String? = null


        constructor(superState: Parcelable) : super(superState)


        constructor(source: Parcel) : super(source) {
            title = source.readString()
            description = source.readString()
        }


        override fun writeToParcel(out: Parcel, flags: Int) {
            super.writeToParcel(out, flags)
            out.writeString(title)
            out.writeString(description)
        }


        companion object {
            @JvmField
            val CREATOR: Parcelable.Creator<SavedState> = object : Parcelable.Creator<SavedState> {
                override fun createFromParcel(source: Parcel): SavedState {
                    return SavedState(source)
                }


                override fun newArray(size: Int): Array<SavedState> {
                    return newArray(size)
                }
            }
        }
    }
}

And RegularPostView will be

RegularPostView将是

class RegularPostView : BasePostView {
    private var dividerColorResId: Int = 0
    ...
    override fun onSaveInstanceState(): Parcelable? {
        val superState: Parcelable? = super.onSaveInstanceState()
        superState?.let {
            val state = SavedState(superState)
            state.dividerColorResId = this.dividerColorResId
            return state
        } ?: run {
            return superState
        }
    }


    override fun onRestoreInstanceState(state: Parcelable?) {
        when (state) {
            is SavedState -> {
                super.onRestoreInstanceState(state.superState)
                this.dividerColorResId = state.dividerColorResId
                // Update view's state here
            }
            else -> {
                super.onRestoreInstanceState(state)
            }
        }
    }


    internal class SavedState : BaseSavedState {
        var dividerColorResId: Int = 0


        constructor(superState: Parcelable) : super(superState)


        constructor(source: Parcel) : super(source) {
            dividerColorResId = source.readInt()
        }


        override fun writeToParcel(out: Parcel, flags: Int) {
            super.writeToParcel(out, flags)
            out.writeInt(dividerColorResId)
        }


        companion object {
            @JvmField
            val CREATOR: Parcelable.Creator<SavedState> = object : Parcelable.Creator<SavedState> {
                override fun createFromParcel(source: Parcel): SavedState {
                    return SavedState(source)
                }


                override fun newArray(size: Int): Array<SavedState> {
                    return newArray(size)
                }
            }
        }
    }
}

These 2 classes handle state changes in the same way with a single Custom View. title and description handled by BasePostView and dividerColorResId handled by RegularPostView.

这两个类通过单个“自定义视图”以相同的方式处理状态更改。 titledescriptionBasePostView处理, dividerColorResIdRegularPostView处理。

And everything seems to work well until the app crashes while state changes by the application process.

在状态因应用程序进程而变化之前,应用程序崩溃之前,一切似乎都运行良好。

Image for post
BasePostView.SavedState not found in RegularPostView when restoring from state change by the application process
从应用程序进程从状态更改还原时,在RegularPostView中找不到BasePostView.SavedState

到底是怎么回事? (What is going on?)

Using BaseSavedState for SavedState and Parcelable.Creator for CREATOR in Inherited Custom View only works well when the state changes by configuration changes, not state changes by the application process.

使用BaseSavedStateSavedStateParcelable.CreatorCREATOR在继承的自定义视图,只有当国家通过配置的变化,而不是由应用程序状态的变化而改变效果很好。

In Android, when users put your app in the background for a long time, the Android system might reclaim the memory by killing your application’s process. But also be able to restore the data when the user opens your app again.

在Android中,当用户长时间将您的应用程序置于后台时,Android系统可能会通过终止应用程序的进程来回收内存。 但是当用户再次打开您的应用程序时,也能够还原数据。

class RegularPostView : BasePostView {
    ...


    internal class SavedState : BaseSavedState {
    	...
        constructor(source: Parcel) : super(source) {
            dividerColorResId = source.readInt()
        }
        ...
        companion object {
            @JvmField
            val CREATOR: Parcelable.Creator<SavedState> = object : Parcelable.Creator<SavedState> {
                override fun createFromParcel(source: Parcel): SavedState {
                    return SavedState(source)
                }
                ...
            }
        }
    }
}

When restored from state changes by the application process, ClassLoader in CREATOR is required for Parcel to Parcelable converting. But there is no ClassLoader in the above example. So RegularPostView can not restore the state in BasePostView(from super).

当应用程序从状态更改中恢复时, CREATOR ClassLoader是宗地到宗地可转换的必需项。 但是上面的示例中没有ClassLoader。 所以RegularPostView无法恢复的状态BasePostView (从super )。

Restoring from state changes by the application process, ClassLoader in CREATOR is required for Parcel to Parcelable converting. But there is no ClassLoader in the above example. So RegularPostView can not restore the state in BasePostView (from super).

通过应用程序从状态更改恢复,将宗地转换为可宗地时需要CREATOR ClassLoader。 但是上面的示例中没有ClassLoader。 所以RegularPostView无法恢复的状态BasePostView (从super )。

To test your app in this behavior, just put your app into background, Click at Terminates selected Android Application in Android Studio’s Logcat, then re-open your app again.

要以这种行为测试您的应用程序,只需将您的应用程序置于后台,在Android Studio的Logcat中单击“ Terminates selected Android Application ,然后再次重新打开您的应用程序。

Before talking about how to solve this problem. Let’s talk about…

在谈论如何解决这个问题之前。 让我们来谈谈…

为什么我使用继承的自定义视图而不是单个自定义视图? (Why am I using the Inherited Custom View instead of a single Custom View?)

From the previous example code, you might say “Just keep all code in one class because it’s Custom View, nothing more”.

在前面的示例代码中,您可能会说“将所有代码保留在一个类中,因为它是Custom View,仅此而已”。

My reason for using the Inherited Custom View is the RoundCornerProgressBar library for the customized progress bar.

我使用继承的自定义视图的原因是自定义进度条的RoundCornerProgressBar库

Image for post

In this library, I created 6 types of progress bars with some logic in common. So using Inherited Custom View is more suitable to maintain it in the future. (reusable + maintainable = 👍👍👍)

在这个库中,我创建了6种类型的进度条,它们具有一些共同的逻辑。 因此,使用继承的自定义视图更适合将来进行维护。 (可重用+可维护=👍👍👍)

Image for post
How I designed the class structure in RoundCornerProgressBar library
我如何设计RoundCornerProgressBar库中的类结构

All classes also have their own states to handle. So BaseSavedState and Parcelable.Creator do not work for my library anymore.

所有类也都有自己的状态要处理。 所以BaseSavedStateParcelable.Creator没有为我的图书馆工作了。

Then, let’s get back to the previous code.

然后,让我们回到前面的代码。

在继承的自定义视图中解决此问题 (To solve this problem in Inherited Custom View)

First, replace the BaseSavedState with AbsSavedState (which allows you to ensure the state of all classes along the hierarchy is saved) to all classes in Custom View’s hierarchy.

首先 ,用AbsSavedState (允许您确保保存层次结构中所有类的状态)替换BaseSavedState到“自定义视图”层次结构中的所有类。

abstract class BasePostView : FrameLayout {
    ...
    internal class SavedState : AbsSavedState {
        var title: String? = null
        var description: String? = null


        constructor(superState: Parcelable) : super(superState)


        constructor(source: Parcel, loader: ClassLoader?) : super(source, loader) {
            title = source.readString()
            description = source.readString()
        }


        override fun writeToParcel(out: Parcel, flags: Int) {
            super.writeToParcel(out, flags)
            out.writeString(title)
            out.writeString(description)
        }
        ...
    }
}
class RegularPostView : BasePostView {
    ...
    internal class SavedState : AbsSavedState {
        var dividerColorResId: Int = 0


        constructor(superState: Parcelable) : super(superState)


        constructor(source: Parcel, loader: ClassLoader?) : super(source, loader) {
            dividerColorResId = source.readInt()
        }


        override fun writeToParcel(out: Parcel, flags: Int) {
            super.writeToParcel(out, flags)
            out.writeInt(dividerColorResId)
        }
        ...
    }
}

AbsSavedState also has a constructor(source: Parcel, loader: ClassLoader?) which supports ClassLoader. And must be AbsSavedState from androidx.customview.view.AbsSavedState for backward compatibility because the constructor of android.view.AbsSavedState is available in API level 24 or higher.

AbsSavedState也有一个支持ClassLoader的constructor(source: Parcel, loader: ClassLoader?) 。 并且必须为AbsSavedStateandroidx.customview.view.AbsSavedState以便向后兼容,因为android.view.AbsSavedState的构造函数在API级别24或更高版本中可用。

androidx.customview.view.AbsSavedState is in the CustomView library of Android Jetpack libraries and also included in the AppCompat library.

androidx.customview.view.AbsSavedState位于Android Jetpack库的CustomView库中,并且也包含在AppCompat库中。

Second, replace the Parcelable.Creator with Parcelable.ClassLoader (which allows the Creator to receive the ClassLoader the object is being created in) to all classes in Custom View’s hierarchy.

,更换Parcelable.CreatorParcelable.ClassLoader (允许造物主接收正在创建的对象的类加载器)自定义视图中的层次结构的所有类。

abstract class BasePostView : FrameLayout {
    ...
    internal class SavedState : AbsSavedState {
        ...
        companion object {
            @JvmField
            val CREATOR: Parcelable.ClassLoaderCreator<SavedState> = object : Parcelable.ClassLoaderCreator<SavedState> {
                override fun createFromParcel(source: Parcel, loader: ClassLoader): SavedState {
                    return SavedState(source, loader)
                }


                override fun createFromParcel(source: Parcel): SavedState {
                    return SavedState(source, null)
                }


                override fun newArray(size: Int): Array<SavedState> {
                    return newArray(size)
                }
            }
        }
    }
}
class RegularPostView : BasePostView {
    ...
    internal class SavedState : AbsSavedState {
        ...
        companion object {
            @JvmField
            val CREATOR: Parcelable.ClassLoaderCreator<SavedState> = object : Parcelable.ClassLoaderCreator<SavedState> {
                override fun createFromParcel(source: Parcel, loader: ClassLoader): SavedState {
                    return SavedState(source, loader)
                }


                override fun createFromParcel(source: Parcel): SavedState {
                    return SavedState(source, null)
                }


                override fun newArray(size: Int): Array<SavedState> {
                    return newArray(size)
                }
            }
        }
    }
}

Then, test the Custom View in every state changes behavior again. And it finally works! 🎉🎉🎉

然后,在每种状态下再次测试自定义视图更改行为。 终于成功了! 🎉🎉🎉

结论 (Conclusion)

To handle state changes in Inherited Custom View, Using AbsSavedState and Parcelable.ClassLoaderCreator which allows us to save/restore the state of all classes along the hierarchy.

为了处理状态改变继承自定义视图,使用AbsSavedStateParcelable.ClassLoaderCreator这使我们能够保存/恢复沿着层次结构中所有类的状态。

Even the single Custom View, if your project contains both Custom Views, using AbsSavedState and Parcelable.ClassLoaderCreator in all of them is better than separating the code style between non-inherited and inherited.

即使是单一的自定义视图,如果你的项目包含两个自定义视图,使用AbsSavedStateParcelable.ClassLoaderCreator在所有这些比之间分离的代码风格更好的非继承和继承。

No Inherited Custom View in your project? Don’t worry. BaseSavedState and Parcelable.Creator still work well for your Custom View, no need to replace them.

您的项目中没有继承的自定义视图? 不用担心 BaseSavedStateParcelable.Creator仍然为您的自定义视图,无需更换他们工作得很好。

Finally, the example code of this article. See the link below

最后,本文的示例代码。 见下面的链接

翻译自: https://medium.com/google-developer-experts/handle-android-state-changes-in-inherited-custom-view-60c77a121abf

android多状态视图

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值