Skip to content

Instantly share code, notes, and snippets.

@talenguyen
Last active September 28, 2015 07:16
Show Gist options
  • Save talenguyen/3a33a6a01c8ae218e7c2 to your computer and use it in GitHub Desktop.
Save talenguyen/3a33a6a01c8ae218e7c2 to your computer and use it in GitHub Desktop.
Android ContentLayout. The utility class to switch showing content/loading/error/empty efficient way
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ContentLayout">
<attr name="content_layout"
format="reference"/>
<attr name="error_layout"
format="reference"/>
<attr name="empty_layout"
format="reference"/>
<attr name="loading_layout"
format="reference"/>
</declare-styleable>
</resources>
/**
* Umbala
*
* Created by Giang Nguyen on 9/26/15.
* Copyright (c) 2015 Umbala. All rights reserved.
*/
package co.umbala.umbala.ui.widget;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.support.annotation.LayoutRes;
import android.support.v7.internal.widget.ViewStubCompat;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
import co.umbala.umbala.R;
public class ContentLayout extends FrameLayout {
private static final int DURATION = 200;
private View vContent;
private View vLoading;
private View vError;
private View vEmpty;
private View showingView;
private ViewStubCompat vsError;
private ViewStubCompat vsEmpty;
private ViewStubCompat vsLoading;
private ViewStubCompat vsContent;
public ContentLayout(Context context) {
this(context, null, 0);
}
public ContentLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ContentLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
final TypedArray a = context.getTheme()
.obtainStyledAttributes(attrs, R.styleable.ContentLayout, defStyleAttr, 0);
try {
int layoutId;
layoutId = a.getResourceId(R.styleable.ContentLayout_content_layout, 0);
if (layoutId != 0) {
vsContent = new ViewStubCompat(context, null);
vsContent.setLayoutResource(layoutId);
addView(vsContent);
}
layoutId = a.getResourceId(R.styleable.ContentLayout_error_layout, 0);
if (layoutId != 0) {
setErrorLayout(layoutId);
}
layoutId = a.getResourceId(R.styleable.ContentLayout_loading_layout, 0);
if (layoutId != 0) {
setLoadingLayout(layoutId);
}
layoutId = a.getResourceId(R.styleable.ContentLayout_empty_layout, 0);
if (layoutId != 0) {
setEmptyLayout(layoutId);
}
} finally {
a.recycle();
}
}
private void setLoadingLayout(int layoutId) {
if (vsLoading != null) {
// Remove the old View
removeView(vsLoading);
}
vsLoading = new ViewStubCompat(getContext(), null);
vsLoading.setLayoutResource(layoutId);
addView(vsLoading);
}
public void setErrorLayout(@LayoutRes int layoutId) {
if (vsError != null) {
removeView(vsError);
}
vsError = new ViewStubCompat(getContext(), null);
vsError.setLayoutResource(layoutId);
addView(vsError);
}
public void setEmptyLayout(@LayoutRes int layoutId) {
if (vsEmpty != null) {
removeView(vsEmpty);
}
vsEmpty = new ViewStubCompat(getContext(), null);
vsEmpty.setLayoutResource(layoutId);
addView(vsEmpty);
}
public View getContentView() {
if (vContent == null) {
vContent = inflateEmptyView(vsContent);
}
return vContent;
}
public View getErrorView() {
if (vError == null) {
vError = inflateEmptyView(vsError);
}
return vError;
}
public void showContent() {
if (vContent == null) {
vContent = inflateEmptyView(vsContent);
}
if (showingView == vContent) {
return;
}
crossFade(vContent, showingView);
showingView = vContent;
}
public void showError() {
if (vError == null) {
vError = inflateEmptyView(vsError);
}
if (showingView == vError) {
return;
}
crossFade(vError, showingView);
showingView = vError;
}
public void showLoading() {
if (vLoading == null) {
vLoading = inflateEmptyView(vsLoading);
}
if (showingView == vLoading) {
return;
}
crossFade(vLoading, showingView);
showingView = vLoading;
}
public void showEmpty() {
if (vEmpty == null) {
vEmpty = inflateEmptyView(vsEmpty);
}
if (showingView == vEmpty) {
return;
}
// Show empty
crossFade(vEmpty, showingView);
showingView = vEmpty;
}
private View inflateEmptyView(ViewStubCompat viewStub) {
if (viewStub == null) {
throw new NullPointerException("Must specify the layout in xml");
}
return viewStub.inflate();
}
private void crossFade(View showView, final View hideView) {
if (showView == null && hideView == null) {
return;
}
if (showView == null) {
hide(hideView);
} else if (hideView == null) {
show(showView);
} else {
final Animator hideAnimator = hideAnimator(hideView);
final Animator showAnimator = showAnimator(showView);
final AnimatorSet animatorSet = new AnimatorSet().setDuration(200);
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
hideView.setVisibility(GONE);
}
});
showView.setVisibility(VISIBLE);
animatorSet.playTogether(showAnimator, hideAnimator);
animatorSet.start();
}
}
private void hide(final View view) {
final Animator animator = hideAnimator(view);
animator.setDuration(DURATION).addListener(new AnimatorListenerAdapter() {
@Override public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
// Clear side-effect by this callback
animator.removeListener(this);
view.setVisibility(GONE);
}
});
animator.start();
}
private void show(final View view) {
view.setVisibility(VISIBLE);
showAnimator(view).setDuration(DURATION).start();
}
private Animator hideAnimator(View target) {
return ObjectAnimator.ofFloat(target, "alpha", 0f);
}
private Animator showAnimator(View target) {
return ObjectAnimator.ofFloat(target, "alpha", 1f);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment