package com.lewis.widget.ui.view; import android.app.Activity; import android.content.Context; import android.graphics.PorterDuff; import android.os.Handler; import android.os.Looper; import android.support.annotation.AttrRes; import android.support.annotation.ColorRes; import android.support.annotation.DrawableRes; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.StringRes; import android.support.v4.content.ContextCompat; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.RelativeLayout; import android.widget.TextView; import com.lewis.widget.ui.R; import com.lewis.widget.ui.Status; import com.lewis.widget.ui.listener.OnRetryBtnClickListener; /** * Created by Lewis on 2017/9/25. * Description: Display a view of different status that includes Loading, dialogLoading, empty, error, * normal and custom etc. */ public class StatusView extends FrameLayout { /** * default mills to show the content view by delay when loading too fast */ private static final int DEFAULT_DELAY_TIME = 600; private Status mCurrentStatus = Status.NORMAL; private View mLoadingView, mDialogLoadingView, mEmptyView, mErrorView, mOtherView; /** * Dialog Loading */ private TextView mDialogText; private RelativeLayout mDialogParentLayout; private LinearLayout mDialogLayout; private ProgressBar mDialogProgressBar; /** * Normal Loading */ private LinearLayout mLoadingLayout; private ProgressBar mLoadingProgress; private TextView mLoadingText; /** * Empty View */ private LinearLayout mEmptyLayout; private ImageView mEmptyIcon; private TextView mEmptyTitle, mEmptySubheading; /** * Error View */ private LinearLayout mErrorLayout; private ImageView mErrorIcon; private TextView mErrorTitle, mErrorSubheading; private Button mErrorBtn; private OnRetryBtnClickListener mOnRetryBtnClickListener; private long mStartTime, mLoadingTime; private long mDelayMills = DEFAULT_DELAY_TIME; private Handler mDelayHandler = new Handler(Looper.getMainLooper()); public StatusView(@NonNull Context context) { this(context, null); } public StatusView(@NonNull Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public StatusView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs); } public static StatusView initInFragment(Context context, View view) { return initInFragment(context, (ViewGroup) view.getParent()); } public static StatusView initInFragment(Context context, ViewGroup parentView) { if (context == null) { throw new RuntimeException("you can not init StatusView with null context"); } if (parentView == null) { throw new RuntimeException("Can not get the parent of Fragment's view"); } StatusView statusView = new StatusView(context); parentView.addView(statusView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); return statusView; } public static StatusView initInActivity(Activity context) { if (context == null) { throw new RuntimeException("you can not init StatusView with null context"); } ViewGroup contentView = context.getWindow().getDecorView().findViewById(android.R.id.content); StatusView statusView = new StatusView(context); contentView.addView(statusView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); return statusView; } private void init(Context context, AttributeSet attrs) { mLoadingView = inflate(context, R.layout.view_loading, null); mDialogLoadingView = inflate(context, R.layout.view_dialog_loading, null); mEmptyView = inflate(context, R.layout.view_empty, null); mErrorView = inflate(context, R.layout.view_error, null); mLoadingLayout = mLoadingView.findViewById(R.id.loading_layout); mLoadingText = mLoadingView.findViewById(R.id.loading_text); mLoadingProgress = mLoadingView.findViewById(R.id.loading_progress); mDialogParentLayout = mDialogLoadingView.findViewById(R.id.dialog_loading_content_layout); mDialogLayout = mDialogLoadingView.findViewById(R.id.dialog_loading_layout); mDialogProgressBar = mDialogLoadingView.findViewById(R.id.dialog_loading_progress); mDialogText = mDialogLoadingView.findViewById(R.id.dialog_loading_text); mEmptyLayout = mEmptyView.findViewById(R.id.empty_content); mEmptyIcon = mEmptyView.findViewById(R.id.empty_icon); mEmptySubheading = mEmptyView.findViewById(R.id.empty_text_subheading); mEmptyTitle = mEmptyView.findViewById(R.id.empty_text_title); mErrorLayout = mErrorView.findViewById(R.id.error_layout); mErrorIcon = mErrorView.findViewById(R.id.error_icon); mErrorBtn = mErrorView.findViewById(R.id.error_btn); mErrorTitle = mErrorView.findViewById(R.id.error_text_title); mErrorSubheading = mErrorView.findViewById(R.id.error_text_subheading); mErrorBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { if (mOnRetryBtnClickListener != null) { mOnRetryBtnClickListener.onClick(view); } } }); } private void setProgressBarColor(ProgressBar dialogProgressBar, @ColorRes int resId) { if (dialogProgressBar.isIndeterminate()) { dialogProgressBar.getIndeterminateDrawable().setColorFilter(ContextCompat.getColor( getContext(), resId), PorterDuff.Mode.SRC_IN); } else { dialogProgressBar.getProgressDrawable().setColorFilter(ContextCompat.getColor( getContext(), resId), PorterDuff.Mode.SRC_IN); } } public void setStatus(Status status) { if (mCurrentStatus == status) { return; } mCurrentStatus = status; mLoadingTime = 0; setDelayCall(status, new OnDelayCallBackListener() { @Override public void call(Status status) { // this call will setup the delayMills by the status removeAllViews(); addStatusView(status); } }); } private void addStatusView(Status status) { switch (status) { case LOADING: addView(mLoadingView); break; case DIALOG_LOADING: addView(mDialogLoadingView); break; case EMPTY: addView(mEmptyView); break; case ERROR: addView(mErrorView); break; case NORMAL: break; case OTHER: if (mOtherView == null) { throw new RuntimeException("You can not set other view with a null custom view"); } addView(mOtherView); break; default: break; } } private boolean isLoadingStatus(Status status) { return status == Status.LOADING || status == Status.DIALOG_LOADING; } public Status getCurrentStatus() { return mCurrentStatus; } public void setLoadingView(View loadingView) { if (loadingView == null) { throw new RuntimeException("loadingView can not be null in setLoadingView"); } mLoadingView = loadingView; mLoadingView.setClickable(true); } public void setDialogLoadingView(View dialogLoadingView) { if (dialogLoadingView == null) { throw new RuntimeException("dialogLoadingView can not be null in setDialogLoadingView"); } mDialogLoadingView = dialogLoadingView; mDialogLoadingView.setClickable(true); } public void setEmptyView(View emptyView) { if (emptyView == null) { throw new RuntimeException("emptyView can not be null in setEmptyView"); } mEmptyView = emptyView; mEmptyView.setClickable(true); } public void setErrorView(View errorView) { if (errorView == null) { throw new RuntimeException("errorView can not be null in setErrorView"); } mErrorView = errorView; mErrorView.setClickable(true); } public void setOtherView(View otherView) { if (otherView == null) { throw new RuntimeException("your custom view can not be null"); } mOtherView = otherView; mOtherView.setClickable(true); } public void addCustomView(View customView) { setOtherView(customView); setStatus(Status.OTHER); } public void setLoadingViewBackgroundColor(@ColorRes int resId) { if (mLoadingLayout != null) { mDialogLayout.setBackgroundColor(ContextCompat.getColor(getContext(), resId)); } } public void setDialogLoadingViewBackgroundColor(@ColorRes int resId) { if (mDialogParentLayout != null) { mDialogLayout.setBackgroundColor(ContextCompat.getColor(getContext(), resId)); } } public void setLoadingDialogBackground(@DrawableRes int resId) { if (mDialogLayout != null) { mDialogLayout.setBackground(ContextCompat.getDrawable(getContext(), resId)); } } public void setOnRetryBtnClickListener(OnRetryBtnClickListener onRetryBtnClickListener) { mOnRetryBtnClickListener = onRetryBtnClickListener; } public void setDialogLoadingText(@StringRes int resId) { setDialogLoadingText(getContext().getString(resId)); } public void setDialogLoadingText(String text) { if (mDialogText != null) { mDialogText.setText(text); } } public void setDialogProgressBarColor(@ColorRes int resId) { if (mDialogProgressBar != null) { setProgressBarColor(mDialogProgressBar, resId); } } public void setLoadingText(String text) { if (mLoadingText != null) { mLoadingText.setText(text); } } public void setLoadingText(@StringRes int resId) { setLoadingText(getContext().getString(resId)); } public void setProgressBarColor(@ColorRes int resId) { if (mLoadingProgress != null) { setProgressBarColor(mLoadingProgress, resId); } } public void setEmptyViewIcon(int resId) { if (mEmptyIcon != null) { mEmptyIcon.setImageResource(resId); } } public void setErrorViewIcon(int resId) { if (mErrorIcon != null) { mErrorIcon.setImageResource(resId); } } public void setDialogText(TextView dialogText) { mDialogText = dialogText; } public void setEmptyTitle(String emptyTitle) { if (mEmptyTitle != null) { mEmptyTitle.setText(emptyTitle); } } public void setEmptySubheading(String emptySubheading) { if (mEmptySubheading != null) { mEmptySubheading.setText(emptySubheading); } } public void setErrorTitle(String errorTitle) { if (mEmptyTitle != null) { mErrorTitle.setText(errorTitle); } } public void setErrorSubheading(String errorSubheading) { if (mErrorSubheading != null) { mErrorSubheading.setText(errorSubheading); } } public void setMarginTop(int top) { LayoutParams layoutParams = (LayoutParams) getLayoutParams(); if (layoutParams == null) { throw new RuntimeException("StatusView is not added in a layout"); } layoutParams.topMargin = top; requestLayout(); } public void setMarginBottom(int bottom) { LayoutParams layoutParams = (LayoutParams) getLayoutParams(); if (layoutParams == null) { throw new RuntimeException("StatusView is not added in a layout"); } layoutParams.bottomMargin = bottom; requestLayout(); } public void setDelayMillsForLoading(long timeMills) { mDelayMills = timeMills; } public void resetDefaultDelayTime() { setDelayMillsForLoading(DEFAULT_DELAY_TIME); } private void setDelayCall(final Status status, final OnDelayCallBackListener listener) { if (isLoadingStatus(status)) { // when is Loading status, init the start time mStartTime = System.currentTimeMillis(); } else { // when is not loading status, init the loading time mLoadingTime = System.currentTimeMillis() - mStartTime; } mDelayHandler.postDelayed(new Runnable() { @Override public void run() { if (listener != null) { listener.call(status); } } }, !isLoadingStatus(status) && mLoadingTime < mDelayMills ? mDelayMills : 0); } private interface OnDelayCallBackListener { void call(Status status); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); mDelayHandler.removeCallbacksAndMessages(null); } }