@@ -2,123 +2,132 @@ package cn.numeron.stateful.layout
22
33import android.content.Context
44import android.util.AttributeSet
5+ import android.view.LayoutInflater
56import android.view.View
67import android.view.ViewGroup
78import android.view.animation.AnimationUtils
89import android.widget.FrameLayout
910import cn.numeron.common.State
1011
11- class StatefulLayout @JvmOverloads constructor(c : Context , a : AttributeSet ? = null , i : Int = 0 ) : FrameLayout(c, a, i) {
12-
13- private var previousState: State ? = null
12+ class StatefulLayout @JvmOverloads constructor(
13+ c : Context ,
14+ a : AttributeSet ? = null ,
15+ i : Int = R .attr.statefulLayoutStyle,
16+ s : Int = R .style.StatefulLayoutStyle
17+ ) : FrameLayout(c, a, i, s) {
1418
1519 var state: State = State .Empty
1620 set(value) {
1721 if (value != field) {
18- previousState = field
1922 field = value
2023 }
2124 changeView()
2225 }
2326
24- private val emptyViews
25- get() = findChildrenByState(State .Empty )
26-
27- private val loadingViews
28- get() = findChildrenByState(State .Loading )
29-
30- private val failureViews
31- get() = findChildrenByState(State .Failure )
32-
33- private val contentViews
34- get() = findChildrenByState(State .Success )
35-
36- private val defaultState: Int
3727 private val emptyLayoutId: Int
3828 private val loadingLayoutId: Int
3929 private val failureLayoutId: Int
4030
41- private var animationEnabled: Boolean
31+ private var animationEnabled = true
4232
4333 init {
44- val tArray = c.obtainStyledAttributes(a, R .styleable.StatefulLayout )
34+ val tArray = c.obtainStyledAttributes(a, R .styleable.StatefulLayout , i, s )
4535
4636 emptyLayoutId = tArray.getResourceId(
4737 R .styleable.StatefulLayout_stateful_empty_layout ,
4838 R .layout.state_empty_layout
4939 )
50-
5140 failureLayoutId = tArray.getResourceId(
5241 R .styleable.StatefulLayout_stateful_failure_layout ,
5342 R .layout.state_failure_layout
5443 )
55-
5644 loadingLayoutId = tArray.getResourceId(
5745 R .styleable.StatefulLayout_stateful_loading_layout ,
5846 R .layout.state_loading_layout
5947 )
6048
61- // 分配默认状态
62- defaultState = tArray.getInt(R .styleable.StatefulLayout_state , 0 )
49+ // 获取默认状态
50+ val defaultState = tArray.getInt(R .styleable.StatefulLayout_state , 0 )
51+ // 设置默认的状态
52+ state = State .values().first {
53+ it.ordinal == defaultState
54+ }
6355 // 是否启动状态切换动画
64- animationEnabled = tArray.getBoolean(R .styleable.StatefulLayout_animation_enabled , true )
56+ animationEnabled =
57+ tArray.getBoolean(R .styleable.StatefulLayout_animation_enabled , animationEnabled)
6558
6659 tArray.recycle()
6760 }
6861
6962 override fun onFinishInflate () {
7063 super .onFinishInflate()
71- if (emptyViews.isEmpty()) {
72- View .inflate(context, emptyLayoutId, this )
64+ // 根据拥有的状态,决定加载剩余的状态布局
65+ val (hasEmpty, hasLoading, hasFailure) = checkState()
66+ val inflater = LayoutInflater .from(context)
67+ if (! hasEmpty) {
68+ val emptyLayout = inflater.inflate(emptyLayoutId, this , false )
69+ addView(emptyLayout, resetLayoutParamsState(emptyLayout, State .Empty ))
7370 }
74- if (loadingViews.isEmpty()) {
75- View .inflate(context, loadingLayoutId, this )
71+ if (! hasLoading) {
72+ val loadingLayout = inflater.inflate(loadingLayoutId, this , false )
73+ addView(loadingLayout, resetLayoutParamsState(loadingLayout, State .Loading ))
7674 }
77- if (failureViews.isEmpty()) {
78- View .inflate(context, failureLayoutId, this )
75+ if (! hasFailure) {
76+ val failureLayout = inflater.inflate(failureLayoutId, this , false )
77+ addView(failureLayout, resetLayoutParamsState(failureLayout, State .Failure ))
7978 }
80- state = State .values().first {
81- it.ordinal == defaultState
79+ }
80+
81+ private fun checkState (): Triple <Boolean , Boolean , Boolean > {
82+ var hasEmpty = false
83+ var hasLoading = false
84+ var hasFailure = false
85+ repeat(childCount) {
86+ val childView = getChildAt(it)
87+ val layoutParams = childView.layoutParams as LayoutParams
88+ val state = layoutParams.state
89+ if (! hasEmpty) {
90+ hasEmpty = state == State .Empty
91+ }
92+ if (! hasLoading) {
93+ hasLoading = state == State .Loading
94+ }
95+ if (! hasFailure) {
96+ hasFailure = state == State .Failure
97+ }
8298 }
99+ return Triple (hasEmpty, hasLoading, hasFailure)
83100 }
84101
85- private fun findChildrenByState (state : State ): List <View > {
86- return (0 .. childCount)
87- .mapNotNull(::getChildAt)
88- .filter {
89- val layoutParams = it.layoutParams
90- layoutParams is LayoutParams && layoutParams.state == state
102+ private fun resetLayoutParamsState (view : View , state : State ): LayoutParams {
103+ val oldLayoutParams = view.layoutParams
104+ if (oldLayoutParams is LayoutParams ) {
105+ if (oldLayoutParams.state != state) {
106+ oldLayoutParams.state = state
91107 }
108+ return oldLayoutParams
109+ }
110+ val layoutParams = generateLayoutParams(oldLayoutParams)
111+ layoutParams.state = state
112+ return layoutParams
92113 }
93114
94115 private fun changeView () {
95- if (previousState != State .Loading && state == State .Loading ) {
96- loadingViews.forEach(::show)
97- } else {
98- loadingViews.forEach(::hide)
99- }
100- if (previousState != State .Success && state == State .Success ) {
101- contentViews.forEach(::show)
102- } else {
103- contentViews.forEach(::hide)
104- }
105- if (previousState != State .Failure && state == State .Failure ) {
106- failureViews.forEach(::show)
107- } else {
108- failureViews.forEach(::hide)
109- }
110- if (previousState != State .Empty && state == State .Empty ) {
111- emptyViews.forEach(::show)
112- } else {
113- emptyViews.forEach(::hide)
116+ repeat(childCount) { index ->
117+ val childView = getChildAt(index)
118+ val childLayoutParams = childView.layoutParams as LayoutParams
119+ if (childLayoutParams.state == this .state) {
120+ show(childView, childLayoutParams)
121+ } else {
122+ hide(childView, childLayoutParams)
123+ }
114124 }
115125 }
116126
117- private fun show (view : View ) {
127+ private fun show (view : View , layoutParams : LayoutParams ) {
118128 if (view.visibility != VISIBLE ) {
119129 view.visibility = VISIBLE
120- val layoutParams = view.layoutParams
121- if (animationEnabled && layoutParams is LayoutParams ) {
130+ if (animationEnabled) {
122131 view.clearAnimation()
123132 view.startAnimation(
124133 AnimationUtils .loadAnimation(
@@ -130,11 +139,10 @@ class StatefulLayout @JvmOverloads constructor(c: Context, a: AttributeSet? = nu
130139 }
131140 }
132141
133- private fun hide (view : View ) {
134- if (view.visibility != GONE ) {
135- view.visibility = GONE
136- val layoutParams = view.layoutParams
137- if (animationEnabled && layoutParams is LayoutParams ) {
142+ private fun hide (view : View , layoutParams : LayoutParams ) {
143+ if (view.visibility == VISIBLE ) {
144+ view.visibility = if (layoutParams.invisible) INVISIBLE else GONE
145+ if (animationEnabled) {
138146 view.clearAnimation()
139147 view.startAnimation(
140148 AnimationUtils .loadAnimation(
@@ -146,61 +154,77 @@ class StatefulLayout @JvmOverloads constructor(c: Context, a: AttributeSet? = nu
146154 }
147155 }
148156
149- override fun generateDefaultLayoutParams (): FrameLayout .LayoutParams {
157+ override fun checkLayoutParams (p : ViewGroup .LayoutParams ? ): Boolean {
158+ return p is LayoutParams
159+ }
160+
161+ override fun generateDefaultLayoutParams (): LayoutParams {
150162 return LayoutParams (
151163 ViewGroup .LayoutParams .MATCH_PARENT ,
152164 ViewGroup .LayoutParams .MATCH_PARENT
153165 )
154166 }
155167
156- override fun generateLayoutParams (attrs : AttributeSet ? ): FrameLayout . LayoutParams {
168+ override fun generateLayoutParams (attrs : AttributeSet ? ): LayoutParams {
157169 return LayoutParams (context, attrs)
158170 }
159171
160- override fun generateLayoutParams (lp : ViewGroup .LayoutParams ? ): ViewGroup . LayoutParams {
172+ override fun generateLayoutParams (lp : ViewGroup .LayoutParams ? ): LayoutParams {
161173 return when (lp) {
162- is LayoutParams -> LayoutParams (lp)
163174 is MarginLayoutParams -> LayoutParams (lp)
164175 is ViewGroup .LayoutParams -> LayoutParams (lp)
165176 else -> generateDefaultLayoutParams()
166177 }
167178 }
168179
169- inner class LayoutParams : FrameLayout .LayoutParams {
180+ override fun addView (child : View , index : Int , params : ViewGroup .LayoutParams ) {
181+ super .addView(child, index, params)
182+ if (params is LayoutParams ) {
183+ // 根据当前的状态与child的布局属性,决定child是否显示
184+ child.visibility = when {
185+ state == params.state -> VISIBLE
186+ params.invisible -> INVISIBLE
187+ else -> GONE
188+ }
189+ }
190+ }
191+
192+ class LayoutParams : FrameLayout .LayoutParams {
170193
171- val state: State
194+ var invisible = false
195+ var state = State .Success
172196 var showAnimationResId = R .anim.anim_state_layout_show
173197 var hideAnimationResId = R .anim.anim_state_layout_hide
174198
175199 constructor (context: Context , attrs: AttributeSet ? ) : super (context, attrs) {
176200 val typedArray =
177201 context.obtainStyledAttributes(attrs, R .styleable.StatefulLayout_Layout )
202+ invisible = typedArray.getBoolean(
203+ R .styleable.StatefulLayout_Layout_stateful_invisible ,
204+ invisible
205+ )
178206 val stateValue = typedArray.getInt(R .styleable.StatefulLayout_Layout_layout_state , 3 )
179- this .state = State .values()[stateValue]
180- showAnimationResId = typedArray.getResourceId(R .styleable.StatefulLayout_Layout_stateful_animation_show , showAnimationResId)
181- hideAnimationResId = typedArray.getResourceId(R .styleable.StatefulLayout_Layout_stateful_animation_hide , hideAnimationResId)
207+ state = State .values()[stateValue]
208+ showAnimationResId = typedArray.getResourceId(
209+ R .styleable.StatefulLayout_Layout_stateful_animation_show ,
210+ showAnimationResId
211+ )
212+ hideAnimationResId = typedArray.getResourceId(
213+ R .styleable.StatefulLayout_Layout_stateful_animation_hide ,
214+ hideAnimationResId
215+ )
182216 typedArray.recycle()
183217 }
184218
185- constructor (width: Int , height: Int , s : State ) : super (width, height) {
186- this .state = s
219+ constructor (width: Int , height: Int , state : State ) : super (width, height) {
220+ this .state = state
187221 }
188222
189- constructor (width: Int , height: Int ) : this (width, height, State . Success )
223+ constructor (width: Int , height: Int ) : super (width, height)
190224
191- constructor (source: ViewGroup .LayoutParams ) : super (source) {
192- this .state = State .Success
193- }
194-
195- constructor (source: MarginLayoutParams ) : super (source) {
196- this .state = State .Success
197- }
225+ constructor (source: ViewGroup .LayoutParams ) : super (source)
198226
199- constructor (source: LayoutParams ) : super (source) {
200- this .state = source.state
201- this .showAnimationResId = source.showAnimationResId
202- this .hideAnimationResId = source.hideAnimationResId
203- }
227+ constructor (source: MarginLayoutParams ) : super (source)
204228
205229 }
206230
0 commit comments