parent
76f5afb11c
commit
0205abd68e
@ -0,0 +1,221 @@ |
|||||||
|
package io.legado.app.ui.widget.dynamiclayout |
||||||
|
|
||||||
|
import android.content.Context |
||||||
|
import android.graphics.drawable.Drawable |
||||||
|
import android.util.AttributeSet |
||||||
|
import android.view.View |
||||||
|
import android.view.ViewGroup |
||||||
|
import android.widget.FrameLayout |
||||||
|
import android.widget.ProgressBar |
||||||
|
import androidx.appcompat.widget.AppCompatButton |
||||||
|
import androidx.appcompat.widget.AppCompatImageView |
||||||
|
import androidx.appcompat.widget.AppCompatTextView |
||||||
|
import io.legado.app.R |
||||||
|
import kotlinx.android.synthetic.main.view_dynamic.view.* |
||||||
|
|
||||||
|
class DynamicFrameLayout(context: Context, attrs: AttributeSet?) : FrameLayout(context, attrs), ViewSwitcher { |
||||||
|
|
||||||
|
private var errorView: View? = null |
||||||
|
private var errorImage: AppCompatImageView? = null |
||||||
|
private var errorTextView: AppCompatTextView? = null |
||||||
|
private var actionBtn: AppCompatButton? = null |
||||||
|
|
||||||
|
private var progressView: View? = null |
||||||
|
private var progressBar: ProgressBar? = null |
||||||
|
|
||||||
|
private var contentView: View? = null |
||||||
|
|
||||||
|
private var errorIcon: Drawable? = null |
||||||
|
private var emptyIcon: Drawable? = null |
||||||
|
|
||||||
|
private var errorActionDescription: CharSequence? = null |
||||||
|
private var emptyActionDescription: CharSequence? = null |
||||||
|
private var emptyDescription: CharSequence? = null |
||||||
|
|
||||||
|
private var errorAction: Action? = null |
||||||
|
private var emptyAction: Action? = null |
||||||
|
|
||||||
|
private var changeListener: OnVisibilityChangeListener? = null |
||||||
|
|
||||||
|
init { |
||||||
|
View.inflate(context, R.layout.view_dynamic, this) |
||||||
|
|
||||||
|
val a = context.obtainStyledAttributes(attrs, R.styleable.DynamicFrameLayout) |
||||||
|
errorIcon = a.getDrawable(R.styleable.DynamicFrameLayout_errorSrc) |
||||||
|
emptyIcon = a.getDrawable(R.styleable.DynamicFrameLayout_emptySrc) |
||||||
|
|
||||||
|
emptyActionDescription = a.getText(R.styleable.DynamicFrameLayout_emptyActionDescription) |
||||||
|
emptyDescription = a.getText(R.styleable.DynamicFrameLayout_emptyDescription) |
||||||
|
|
||||||
|
errorActionDescription = a.getText(R.styleable.DynamicFrameLayout_errorActionDescription) |
||||||
|
if (errorActionDescription == null) { |
||||||
|
errorActionDescription = context.getString(R.string.dynamic_click_retry) |
||||||
|
} |
||||||
|
a.recycle() |
||||||
|
} |
||||||
|
|
||||||
|
override fun onFinishInflate() { |
||||||
|
super.onFinishInflate() |
||||||
|
if (childCount > 2) { |
||||||
|
contentView = getChildAt(2) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
override fun addView(child: View) { |
||||||
|
if (childCount > 2) { |
||||||
|
throw IllegalStateException("DynamicFrameLayout can host only one direct child") |
||||||
|
} |
||||||
|
|
||||||
|
super.addView(child) |
||||||
|
} |
||||||
|
|
||||||
|
override fun addView(child: View, index: Int) { |
||||||
|
if (childCount > 2) { |
||||||
|
throw IllegalStateException("DynamicFrameLayout can host only one direct child") |
||||||
|
} |
||||||
|
|
||||||
|
super.addView(child, index) |
||||||
|
} |
||||||
|
|
||||||
|
override fun addView(child: View, params: ViewGroup.LayoutParams) { |
||||||
|
if (childCount > 2) { |
||||||
|
throw IllegalStateException("DynamicFrameLayout can host only one direct child") |
||||||
|
} |
||||||
|
|
||||||
|
super.addView(child, params) |
||||||
|
} |
||||||
|
|
||||||
|
override fun addView(child: View, index: Int, params: ViewGroup.LayoutParams) { |
||||||
|
if (childCount > 2) { |
||||||
|
throw IllegalStateException("DynamicFrameLayout can host only one direct child") |
||||||
|
} |
||||||
|
|
||||||
|
super.addView(child, index, params) |
||||||
|
} |
||||||
|
|
||||||
|
override fun showErrorView(message: CharSequence) { |
||||||
|
ensureErrorView() |
||||||
|
|
||||||
|
setViewVisible(errorView, true) |
||||||
|
setViewVisible(contentView, false) |
||||||
|
setViewVisible(progressView, false) |
||||||
|
|
||||||
|
errorTextView?.text = message |
||||||
|
errorImage?.setImageDrawable(errorIcon) |
||||||
|
|
||||||
|
actionBtn?.let { |
||||||
|
it.tag = ACTION_WHEN_ERROR |
||||||
|
it.visibility = View.VISIBLE |
||||||
|
if (errorActionDescription != null) { |
||||||
|
it.text = errorActionDescription |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
dispatchVisibilityChanged(ViewSwitcher.SHOW_ERROR_VIEW) |
||||||
|
} |
||||||
|
|
||||||
|
override fun showErrorView(messageId: Int) { |
||||||
|
showErrorView(resources.getText(messageId)) |
||||||
|
} |
||||||
|
|
||||||
|
override fun showEmptyView() { |
||||||
|
ensureErrorView() |
||||||
|
|
||||||
|
setViewVisible(errorView, true) |
||||||
|
setViewVisible(contentView, false) |
||||||
|
setViewVisible(progressView, false) |
||||||
|
|
||||||
|
errorTextView?.text = emptyDescription |
||||||
|
errorImage?.setImageDrawable(emptyIcon) |
||||||
|
|
||||||
|
actionBtn?.let { |
||||||
|
it.tag = ACTION_WHEN_EMPTY |
||||||
|
if (errorActionDescription != null) { |
||||||
|
it.visibility = View.VISIBLE |
||||||
|
it.text = errorActionDescription |
||||||
|
} else { |
||||||
|
it.visibility = View.INVISIBLE |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
dispatchVisibilityChanged(ViewSwitcher.SHOW_EMPTY_VIEW) |
||||||
|
} |
||||||
|
|
||||||
|
override fun showProgressView() { |
||||||
|
ensureProgressView() |
||||||
|
|
||||||
|
setViewVisible(errorView, false) |
||||||
|
setViewVisible(contentView, false) |
||||||
|
setViewVisible(progressView, true) |
||||||
|
|
||||||
|
dispatchVisibilityChanged(ViewSwitcher.SHOW_PROGRESS_VIEW) |
||||||
|
} |
||||||
|
|
||||||
|
override fun showContentView() { |
||||||
|
setViewVisible(errorView, false) |
||||||
|
setViewVisible(contentView, true) |
||||||
|
setViewVisible(progressView, false) |
||||||
|
|
||||||
|
dispatchVisibilityChanged(ViewSwitcher.SHOW_CONTENT_VIEW) |
||||||
|
} |
||||||
|
|
||||||
|
fun setOnVisibilityChangeListener(listener: OnVisibilityChangeListener) { |
||||||
|
changeListener = listener |
||||||
|
} |
||||||
|
|
||||||
|
fun setErrorAction(action: Action) { |
||||||
|
errorAction = action |
||||||
|
} |
||||||
|
|
||||||
|
fun setEmptyAction(action: Action) { |
||||||
|
emptyAction = action |
||||||
|
} |
||||||
|
|
||||||
|
private fun setViewVisible(view: View?, visible: Boolean) { |
||||||
|
view?.let { |
||||||
|
it.visibility = if (visible) View.VISIBLE else View.INVISIBLE |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun ensureErrorView() { |
||||||
|
if (errorView == null) { |
||||||
|
errorView = errorViewStub.inflate() |
||||||
|
errorImage = errorView?.findViewById(R.id.iv_error_image) |
||||||
|
errorTextView = errorView?.findViewById(R.id.tv_error_message) |
||||||
|
actionBtn = errorView?.findViewById(R.id.btn_error_retry) |
||||||
|
|
||||||
|
actionBtn?.setOnClickListener { |
||||||
|
when (it.tag) { |
||||||
|
ACTION_WHEN_EMPTY -> emptyAction?.onAction(this@DynamicFrameLayout) |
||||||
|
ACTION_WHEN_ERROR -> errorAction?.onAction(this@DynamicFrameLayout) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun ensureProgressView() { |
||||||
|
if (progressView == null) { |
||||||
|
progressView = progressViewStub.inflate() |
||||||
|
progressBar = progressView?.findViewById(R.id.loading_progress) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun dispatchVisibilityChanged(@ViewSwitcher.Visibility visibility: Int) { |
||||||
|
changeListener?.onVisibilityChanged(visibility) |
||||||
|
} |
||||||
|
|
||||||
|
interface Action { |
||||||
|
fun onAction(switcher: ViewSwitcher) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
interface OnVisibilityChangeListener { |
||||||
|
|
||||||
|
fun onVisibilityChanged(@ViewSwitcher.Visibility visibility: Int) |
||||||
|
} |
||||||
|
|
||||||
|
companion object { |
||||||
|
private const val ACTION_WHEN_ERROR = "ACTION_WHEN_ERROR" |
||||||
|
private const val ACTION_WHEN_EMPTY = "ACTION_WHEN_EMPTY" |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
package io.legado.app.ui.widget.dynamiclayout |
||||||
|
|
||||||
|
import androidx.annotation.IntDef |
||||||
|
import androidx.annotation.StringRes |
||||||
|
|
||||||
|
interface ViewSwitcher { |
||||||
|
|
||||||
|
companion object{ |
||||||
|
const val SHOW_CONTENT_VIEW = 0 |
||||||
|
const val SHOW_ERROR_VIEW = 1 |
||||||
|
const val SHOW_EMPTY_VIEW = 2 |
||||||
|
const val SHOW_PROGRESS_VIEW = 3 |
||||||
|
} |
||||||
|
|
||||||
|
@Retention(AnnotationRetention.SOURCE) |
||||||
|
@IntDef(SHOW_CONTENT_VIEW, SHOW_ERROR_VIEW, SHOW_EMPTY_VIEW, SHOW_PROGRESS_VIEW) |
||||||
|
annotation class Visibility |
||||||
|
|
||||||
|
fun showErrorView(message: CharSequence) |
||||||
|
|
||||||
|
fun showErrorView(@StringRes messageId: Int) |
||||||
|
|
||||||
|
fun showEmptyView() |
||||||
|
|
||||||
|
fun showProgressView() |
||||||
|
|
||||||
|
fun showContentView() |
||||||
|
|
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<merge xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="match_parent"> |
||||||
|
|
||||||
|
<ViewStub |
||||||
|
android:id="@+id/errorViewStub" |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_gravity="center" |
||||||
|
android:layout="@layout/view_error" /> |
||||||
|
|
||||||
|
<ViewStub |
||||||
|
android:id="@+id/progressViewStub" |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_gravity="center" |
||||||
|
android:layout="@layout/view_loading" /> |
||||||
|
|
||||||
|
</merge> |
@ -0,0 +1,27 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:gravity="center_horizontal" |
||||||
|
android:orientation="vertical"> |
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView |
||||||
|
android:id="@+id/iv_error_image" |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="wrap_content"/> |
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView |
||||||
|
android:id="@+id/tv_error_message" |
||||||
|
style="@style/Style.Text.Primary.Normal" |
||||||
|
android:paddingTop="16dp" |
||||||
|
android:paddingBottom="16dp" /> |
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatButton |
||||||
|
android:id="@+id/btn_error_retry" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:backgroundTint="@color/colorAccent" |
||||||
|
android:minWidth="96dp" |
||||||
|
android:text="@string/dynamic_click_retry" /> |
||||||
|
|
||||||
|
</LinearLayout> |
@ -0,0 +1,19 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:gravity="center_horizontal" |
||||||
|
android:orientation="vertical"> |
||||||
|
|
||||||
|
<androidx.core.widget.ContentLoadingProgressBar |
||||||
|
android:id="@+id/loading_progress" |
||||||
|
android:layout_width="48dp" |
||||||
|
android:layout_height="48dp" |
||||||
|
style="@style/Widget.AppCompat.ProgressBar"/> |
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView |
||||||
|
android:id="@+id/tv_loading_message" |
||||||
|
style="@style/Style.Text.Second.Normal.Wrap" |
||||||
|
android:paddingTop="16dp" |
||||||
|
android:text="@string/dynamic_loading"/> |
||||||
|
</LinearLayout> |
@ -1,16 +1,8 @@ |
|||||||
<?xml version="1.0" encoding="utf-8"?> |
<?xml version="1.0" encoding="utf-8"?> |
||||||
<com.google.android.material.appbar.AppBarLayout |
<androidx.appcompat.widget.Toolbar |
||||||
xmlns:android="http://schemas.android.com/apk/res/android" |
xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
xmlns:app="http://schemas.android.com/apk/res-auto" |
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||||
android:id="@+id/appBar" |
|
||||||
android:layout_width="match_parent" |
|
||||||
android:layout_height="wrap_content" |
|
||||||
android:theme="@style/AppTheme.AppBarOverlay"> |
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar |
|
||||||
android:id="@+id/toolbar" |
android:id="@+id/toolbar" |
||||||
android:layout_width="match_parent" |
android:layout_width="match_parent" |
||||||
android:layout_height="wrap_content" |
android:layout_height="wrap_content" |
||||||
app:popupTheme="@style/AppTheme.PopupOverlay"/> |
app:popupTheme="@style/AppTheme.PopupOverlay"/> |
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout> |
|
@ -0,0 +1,40 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<resources> |
||||||
|
|
||||||
|
<declare-styleable name="TitleBar"> |
||||||
|
<attr name="attachToActivity" format="boolean" /> |
||||||
|
<attr name="showNavigationIcon" format="boolean" /> |
||||||
|
<attr name="navigationIcon" format="reference" /> |
||||||
|
<attr name="navigationContentDescription" format="reference|string" /> |
||||||
|
<attr name="navigationIconTint" format="color|reference" /> |
||||||
|
<attr name="navigationIconTintMode" format="enum"> |
||||||
|
<enum name="clear" value="0" /> |
||||||
|
<enum name="src" value="1" /> |
||||||
|
<enum name="dst" value="2" /> |
||||||
|
<enum name="src_over" value="3" /> |
||||||
|
<enum name="dst_over" value="4" /> |
||||||
|
<enum name="src_in" value="5" /> |
||||||
|
<enum name="dst_in" value="6" /> |
||||||
|
<enum name="src_out" value="7" /> |
||||||
|
<enum name="dst_out" value="8" /> |
||||||
|
<enum name="src_atop" value="9" /> |
||||||
|
<enum name="dst_atop" value="10" /> |
||||||
|
<enum name="xor" value="11" /> |
||||||
|
<enum name="darken" value="16" /> |
||||||
|
<enum name="lighten" value="17" /> |
||||||
|
<enum name="multiply" value="13" /> |
||||||
|
<enum name="screen" value="14" /> |
||||||
|
<enum name="add" value="12" /> |
||||||
|
<enum name="overlay" value="15" /> |
||||||
|
</attr> |
||||||
|
</declare-styleable> |
||||||
|
|
||||||
|
<declare-styleable name="DynamicFrameLayout"> |
||||||
|
<attr name="errorSrc" format="reference" /> |
||||||
|
<attr name="emptySrc" format="reference" /> |
||||||
|
<attr name="errorActionDescription" format="string|reference" /> |
||||||
|
<attr name="emptyActionDescription" format="string|reference" /> |
||||||
|
<attr name="emptyDescription" format="string|reference" /> |
||||||
|
</declare-styleable> |
||||||
|
|
||||||
|
</resources> |
Loading…
Reference in new issue