package com.anthony.statuslayout;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @author Vlonjat Gashi (vlonjatg)
*/
public class StatusLayout extends RelativeLayout {
private static final String TAG_LOADING = "StatusLayout.TAG_LOADING";
private static final String TAG_EMPTY = "StatusLayout.TAG_EMPTY";
private static final String TAG_ERROR = "StatusLayout.TAG_ERROR";
final String CONTENT = "type_content";
final String LOADING = "type_loading";
final String EMPTY = "type_empty";
final String ERROR = "type_error";
LayoutInflater inflater;
View view;
LayoutParams layoutParams;
Drawable currentBackground;
List<View> contentViews = new ArrayList<>();
RelativeLayout loadingStateRelativeLayout;
ProgressBar loadingStateProgressBar;
RelativeLayout emptyStateRelativeLayout;
ImageView emptyStateImageView;
TextView emptyStateTitleTextView;
TextView emptyStateContentTextView;
RelativeLayout errorStateRelativeLayout;
ImageView errorStateImageView;
TextView errorStateTitleTextView;
TextView errorStateContentTextView;
Button errorStateButton;
int loadingStateProgressBarWidth;
int loadingStateProgressBarHeight;
int loadingStateBackgroundColor;
int emptyStateImageWidth;
int emptyStateImageHeight;
int emptyStateTitleTextSize;
int emptyStateContentTextSize;
int emptyStateTitleTextColor;
int emptyStateContentTextColor;
int emptyStateBackgroundColor;
int errorStateImageWidth;
int errorStateImageHeight;
int errorStateTitleTextSize;
int errorStateContentTextSize;
int errorStateTitleTextColor;
int errorStateContentTextColor;
int errorStateButtonTextColor;
int errorStateBackgroundColor;
private String state = CONTENT;
public StatusLayout(Context context) {
super(context);
}
public StatusLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public StatusLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs);
}
private void init(AttributeSet attrs) {
inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.StatusLayout);
//Loading state attrs
loadingStateProgressBarWidth =
typedArray.getDimensionPixelSize(R.styleable.StatusLayout_loadingProgressBarWidth, 108);
loadingStateProgressBarHeight =
typedArray.getDimensionPixelSize(R.styleable.StatusLayout_loadingProgressBarHeight, 108);
loadingStateBackgroundColor =
typedArray.getColor(R.styleable.StatusLayout_loadingBackgroundColor, Color.TRANSPARENT);
//Empty state attrs
emptyStateImageWidth =
typedArray.getDimensionPixelSize(R.styleable.StatusLayout_emptyImageWidth, 308);
emptyStateImageHeight =
typedArray.getDimensionPixelSize(R.styleable.StatusLayout_emptyImageHeight, 308);
emptyStateTitleTextSize =
typedArray.getDimensionPixelSize(R.styleable.StatusLayout_emptyTitleTextSize, 15);
emptyStateContentTextSize =
typedArray.getDimensionPixelSize(R.styleable.StatusLayout_emptyContentTextSize, 14);
emptyStateTitleTextColor =
typedArray.getColor(R.styleable.StatusLayout_emptyTitleTextColor, Color.BLACK);
emptyStateContentTextColor =
typedArray.getColor(R.styleable.StatusLayout_emptyContentTextColor, Color.BLACK);
emptyStateBackgroundColor =
typedArray.getColor(R.styleable.StatusLayout_emptyBackgroundColor, Color.TRANSPARENT);
//Error state attrs
errorStateImageWidth =
typedArray.getDimensionPixelSize(R.styleable.StatusLayout_errorImageWidth, 308);
errorStateImageHeight =
typedArray.getDimensionPixelSize(R.styleable.StatusLayout_errorImageHeight, 308);
errorStateTitleTextSize =
typedArray.getDimensionPixelSize(R.styleable.StatusLayout_errorTitleTextSize, 15);
errorStateContentTextSize =
typedArray.getDimensionPixelSize(R.styleable.StatusLayout_errorContentTextSize, 14);
errorStateTitleTextColor =
typedArray.getColor(R.styleable.StatusLayout_errorTitleTextColor, Color.BLACK);
errorStateContentTextColor =
typedArray.getColor(R.styleable.StatusLayout_errorContentTextColor, Color.BLACK);
errorStateButtonTextColor =
typedArray.getColor(R.styleable.StatusLayout_errorButtonTextColor, Color.BLACK);
errorStateBackgroundColor =
typedArray.getColor(R.styleable.StatusLayout_errorBackgroundColor, Color.TRANSPARENT);
typedArray.recycle();
currentBackground = this.getBackground();
}
@Override
public void addView(@NonNull View child, int index, ViewGroup.LayoutParams params) {
super.addView(child, index, params);
if (child.getTag() == null || (!child.getTag().equals(TAG_LOADING) &&
!child.getTag().equals(TAG_EMPTY) && !child.getTag().equals(TAG_ERROR))) {
// contentViews.add(child);
contentViews.addAll(getAllChildren(child));
Log.i("StatusLayout", "sub children count of StatusLayout is" + contentViews.size());
}
}
/**
* save all of the direct and indirect children views
*
* @param
*/
private List<View> getAllChildren(View v) {
if (!(v instanceof ViewGroup)) {
List<View> viewArrayList = new ArrayList<>();
viewArrayList.add(v);
return viewArrayList;
} else {
if (((ViewGroup) v).getChildCount() == 0) {
List<View> viewArrayList = new ArrayList<>();
viewArrayList.add(v);
return viewArrayList;
} else {
List<View> result = new ArrayList<>();
ViewGroup vg = (ViewGroup) v;
for (int i = 0; i < vg.getChildCount(); i++) {
View child = vg.getChildAt(i);
List<View> viewArrayList = new ArrayList<>();
viewArrayList.add(v);
viewArrayList.addAll(getAllChildren(child));
result.addAll(viewArrayList);
}
return result;
}
}
}
/**
* Hide all other states and show content
*/
public void showContent() {
switchState(CONTENT, null, null, null, null, null, Collections.<Integer>emptyList());
}
/**
* Hide all other states and show content
*
* @param skipIds Ids of views not to show
*/
public void showContent(List<Integer> skipIds) {
switchState(CONTENT, null, null, null, null, null, skipIds);
}
/**
* Hide content and show the progress bar
*/
public void showLoading() {
switchState(LOADING, null, null, null, null, null, Collections.<Integer>emptyList());
}
/**
* Hide content and show the progress bar
*
* @param skipIds Ids of views to not hide
*/
public void showLoading(List<Integer> skipIds) {
switchState(LOADING, null, null, null, null, null, skipIds);
}
/**
* Show empty view when there are not data to show
*
* @param emptyImageDrawable Drawable to show
* @param emptyTextTitle Title of the empty view to show
* @param emptyTextContent Content of the empty view to show
*/
public void showEmpty(Drawable emptyImageDrawable, String emptyTextTitle, String emptyTextContent) {
switchState(EMPTY, emptyImageDrawable, emptyTextTitle, emptyTextContent, null, null, Collections.<Integer>emptyList());
}
/**
* Show empty view when there are not data to show
*
* @param emptyImageDrawable Drawable to show
* @param emptyTextTitle Title of the empty view to show
* @param emptyTextContent Content of the empty view to show
* @param skipIds Ids of views to not hide
*/
public void showEmpty(Drawable emptyImageDrawable, String emptyTextTitle, String emptyTextContent, List<Integer> skipIds) {
switchState(EMPTY, emptyImageDrawable, emptyTextTitle, emptyTextContent, null, null, skipIds);
}
/**
* Show error view with a button when something goes wrong and prompting the user to try again
*
* @param errorImageDrawable Drawable to show
* @param errorTextTitle Title of the error view to show
* @param errorTextContent Content of the error view to show
* @param errorButtonText Text on the error view button to show
* @param onClickListener Listener of the error view button
*/
public void showError(Drawable errorImageDrawable, String errorTextTitle, String errorTextContent, String errorButtonText, View.OnClickListener onClickListener) {
switchState(ERROR, errorImageDrawable, errorTextTitle, errorTextContent, errorButtonText, onClickListener, Collections.<Integer>emptyList());
}
/**
* Show error view with a button when something goes wrong and prompting the user to try again
*
* @param errorImageDrawable Drawable to show
* @param errorTextTitle Title of the error view to show
* @param errorTextContent Content of the error view to show
* @param errorButtonText Text on the error view button to show
* @param onClickListener Listener of the error view button
* @param skipIds Ids of views to not hide
*/
public void showError(Drawable errorImageDrawable, String errorTextTitle, String errorTextContent, String errorButtonText, View.OnClickListener onClickListener, List<Integer> skipIds) {
switchState(ERROR, errorImageDrawable, errorTextTitle, errorTextContent, errorButtonText, onClickListener, skipIds);
}
/**
* Get which state is set
*
* @return State
*/
public String getState() {
return state;
}
/**
* Check if content is shown
*
* @return boolean
*/
public boolean isContent() {
return state.equals(CONTENT);
}
/**
* Check if loading state is shown
*
* @return boolean
*/
public boolean isLoading() {
return state.equals(LOADING);
}
/**
* Check if empty state is shown
*
* @return boolean
*/
public boolean isEmpty() {
return state.equals(EMPTY);
}
/**
* Check if error state is shown
*
* @return boolean
*/
public boolean isError() {
return state.equals(ERROR);
}
private void switchState(String state, Drawable drawable, String errorText, String errorTextContent,
String errorButtonText, View.OnClickListener onClickListener, List<Integer> skipIds) {
this.state = state;
switch (state) {
case CONTENT:
//Hide all state views to display content
hideLoadingView();
hideEmptyView();
hideErrorView();
setContentVisibility(true, skipIds);
break;
case LOADING:
hideEmptyView();
hideErrorView();
setLoadingView();
setContentVisibility(false, skipIds);
break;
case EMPTY:
hideLoadingView();
hideErrorView();
setEmptyView();
emptyStateImageView.setImageDrawable(drawable);
emptyStateTitleTextView.setText(errorText);
emptyStateContentTextView.setText(errorTextContent);
setContentVisibility(false, skipIds);
break;
case ERROR:
hideLoadingView();
hideEmptyView();
setErrorView();
errorStateImageView.setImageDrawable(drawable);
errorStateTitleTextView.setText(errorText);
errorStateContentTextView.setText(errorTextContent);
errorStateButton.setText(errorButtonText);
errorStateButton.setOnClickListener(onClickListener);
setContentVisibility(false, skipIds);
break;
}
}
private void setLoadingView() {
if (loadingStateRelativeLayout == null) {
view = inflater.inflate(R.layout.progress_loading_view, null);
loadingStateRelativeLayout = (RelativeLayout) view.findViewById(R.id.loadingStateRelativeLayout);
loadingStateRelativeLayout.setTag(TAG_LOADING);
loadingStateProgressBar = (ProgressBar) view.findViewById(R.id.loadingStateProgressBar);
loadingStateProgressBar.getLayoutParams().width = loadingStateProgressBarWidth;
loadingStateProgressBar.getLayoutParams().height = loadingStateProgressBarHeight;
loadingStateProgressBar.requestLayout();
//Set background color if not TRANSPARENT
if (loadingStateBackgroundColor != Color.TRANSPARENT) {
this.setBackgroundColor(loadingStateBackgroundColor);
}
layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
layoutParams.addRule(CENTER_IN_PARENT);
addView(loadingStateRelativeLayout, layoutParams);
} else {
loadingStateRelativeLayout.setVisibility(VISIBLE);
}
}
private void setEmptyView() {
if (emptyStateRelativeLayout == null) {
view = inflater.inflate(R.layout.progress_empty_view, null);
emptyStateRelativeLayout = (RelativeLayout) view.findViewById(R.id.emptyStateRelativeLayout);
emptyStateRelativeLayout.setTag(TAG_EMPTY);
emptyStateImageView = (ImageView) view.findViewById(R.id.emptyStateImageView);
emptyStateTitleTextView = (TextView) view.findViewById(R.id.emptyStateTitleTextView);
emptyStateContentTextView = (TextView) view.findViewById(R.id.emptyStateContentTextView);
//Set empty state image width and height
emptyStateImageView.getLayoutParams().width = emptyStateImageWidth;
emptyStateImageView.getLayoutParams().height = emptyStateImageHeight;
emptyStateImageView.requestLayout();
emptyStateTitleTextView.setTextSize(emptyStateTitleTextSize);
emptyStateContentTextView.setTextSize(emptyStateContentTextSize);
emptyStateTitleTextView.setTextColor(emptyStateTitleTextColor);
emptyStateContentTextView.setTextColor(emptyStateContentTextColor);
//Set background color if not TRANSPARENT
if (emptyStateBackgroundColor != Color.TRANSPARENT) {
this.setBackgroundColor(emptyStateBackgroundColor);
}
layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
layoutParams.addRule(CENTER_IN_PARENT);
addView(emptyStateRelativeLayout, layoutParams);
} else {
emptyStateRelativeLayout.setVisibility(VISIBLE);
}
}
private void setErrorView() {
if (errorStateRelativeLayout == null) {
view = inflater.inflate(R.layout.progress_error_view, null);
errorStateRelativeLayout = (RelativeLayout) view.findViewById(R.id.errorStateRelativeLayout);
errorStateRelativeLayout.setTag(TAG_ERROR);
errorStateImageView = (ImageView) view.findViewById(R.id.errorStateImageView);
errorStateTitleTextView = (TextView) view.findViewById(R.id.errorStateTitleTextView);
errorStateContentTextView = (TextView) view.findViewById(R.id.errorStateContentTextView);
errorStateButton = (Button) view.findViewById(R.id.errorStateButton);
//Set error state image width and height
errorStateImageView.getLayoutParams().width = errorStateImageWidth;
errorStateImageView.getLayoutParams().height = errorStateImageHeight;
errorStateImageView.requestLayout();
errorStateTitleTextView.setTextSize(errorStateTitleTextSize);
errorStateContentTextView.setTextSize(errorStateContentTextSize);
errorStateTitleTextView.setTextColor(errorStateTitleTextColor);
errorStateContentTextView.setTextColor(errorStateContentTextColor);
errorStateButton.setTextColor(errorStateButtonTextColor);
//Set background color if not TRANSPARENT
if (errorStateBackgroundColor != Color.TRANSPARENT) {
this.setBackgroundColor(errorStateBackgroundColor);
}
layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
layoutParams.addRule(CENTER_IN_PARENT);
addView(errorStateRelativeLayout, layoutParams);
} else {
errorStateRelativeLayout.setVisibility(VISIBLE);
}
}
/**
* set children visibility of StatusLayout
*
* @param visible
* @param skipIds
*/
private void setContentVisibility(boolean visible, List<Integer> skipIds) {
boolean parentVisibleFlag = false;
for (View v : contentViews) {
// when visible is true,which means set content Layout, the skipIds component need to be hide
if (visible) {
if (skipIds.contains(v.getId())) {
v.setVisibility(View.INVISIBLE);
}
}
if (!skipIds.contains(v.getId())) {
v.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
}
//set parent visible flag to be visible when one child want to be visible
if (v.getVisibility() == View.VISIBLE) {
parentVisibleFlag = true;
}
//set all of the parent to be visible
if(parentVisibleFlag &&(contentViews.get(contentViews.size()-1)==v)){
setParentToVisible(v);
}
int visibility = v.getVisibility();
Log.i("StatusLayout", v + " view id " + v.getId() + " visibility " + visibility);
}
}
public void setParentToVisible(View v) {
ViewParent mParent = v.getParent();
if (mParent != null && (mParent instanceof ViewGroup)) {
((ViewGroup) mParent).setVisibility(View.VISIBLE);
if (mParent.getParent() != null) {
setParentToVisible((View) mParent);
}
}
}
private void hideLoadingView() {
if (loadingStateRelativeLayout != null) {
loadingStateRelativeLayout.setVisibility(GONE);
//Restore the background color if not TRANSPARENT
if (loadingStateBackgroundColor != Color.TRANSPARENT) {
this.setBackgroundDrawable(currentBackground);
}
}
}
private void hideEmptyView() {
if (emptyStateRelativeLayout != null) {
emptyStateRelativeLayout.setVisibility(GONE);
//Restore the background color if not TRANSPARENT
if (emptyStateBackgroundColor != Color.TRANSPARENT) {
this.setBackgroundDrawable(currentBackground);
}
}
}
private void hideErrorView() {
if (errorStateRelativeLayout != null) {
errorStateRelativeLayout.setVisibility(GONE);
//Restore the background color if not TRANSPARENT
if (errorStateBackgroundColor != Color.TRANSPARENT) {
this.setBackgroundDrawable(currentBackground);
}
}
}
}