Skip to content

Instantly share code, notes, and snippets.

@kibao
Created November 13, 2016 09:14
Show Gist options
  • Save kibao/64be470d43686b873273428263ccdb5e to your computer and use it in GitHub Desktop.
Save kibao/64be470d43686b873273428263ccdb5e to your computer and use it in GitHub Desktop.
PageContainer - Container for fragments based on TabHost & FragmentTabHost. Designed for use with BottomNavigationView
import android.content.Context;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.util.AttributeSet;
import android.widget.FrameLayout;
import java.util.ArrayList;
import java.util.List;
public class PageContainer extends FrameLayout {
private Context context;
private FragmentManager fragmentManager;
private boolean attached = false;
private List<PageInfo> items = new ArrayList<>();
private PageInfo lastPage;
private int currentPage = -1;
private OnPageChangeListener onPageChangeListener;
static class PageInfo {
@NonNull
final String tag;
@NonNull
final Class<?> clss;
@Nullable
final Bundle args;
Fragment fragment;
PageInfo(@NonNull String _tag, @NonNull Class<?> _class, @Nullable Bundle _args) {
tag = _tag;
clss = _class;
args = _args;
}
}
static class SavedState extends BaseSavedState {
String curPage;
SavedState(Parcelable superState) {
super(superState);
}
SavedState(Parcel in) {
super(in);
curPage = in.readString();
}
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeString(curPage);
}
@Override
public String toString() {
return "PageContainer.SavedState{"
+ Integer.toHexString(System.identityHashCode(this))
+ " curPage=" + curPage + "}";
}
public static final Parcelable.Creator<SavedState> CREATOR
= new Parcelable.Creator<SavedState>() {
@Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
@Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
public PageContainer(Context context) {
super(context);
}
public PageContainer(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setup(Context context, FragmentManager fragmentManager) {
this.context = context;
this.fragmentManager = fragmentManager;
}
public void addPage(@NonNull String tag, @NonNull Class<?> clss) {
addPage(tag, clss, null);
}
public void addPage(@NonNull String tag, @NonNull Class<?> clss,
@Nullable Bundle args) {
final PageInfo info = new PageInfo(tag, clss, args);
if (attached) {
// If we are already attached to the window, then check to make
// sure this page's fragment is inactive if it exists. This shouldn't
// normally happen.
info.fragment = fragmentManager.findFragmentByTag(tag);
if (info.fragment != null && !info.fragment.isDetached()) {
final FragmentTransaction ft = fragmentManager.beginTransaction();
ft.detach(info.fragment);
ft.commit();
}
}
items.add(info);
if (currentPage == -1) {
setCurrentPage(0);
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
final String currentTag = getCurrentPageTag();
// Go through all pages and make sure their fragments match
// the correct state.
FragmentTransaction ft = null;
for (int i = 0, count = items.size(); i < count; i++) {
final PageInfo page = items.get(i);
page.fragment = fragmentManager.findFragmentByTag(page.tag);
if (page.fragment != null && !page.fragment.isDetached()) {
if (page.tag.equals(currentTag)) {
// The fragment for this page is already there and
// active, and it is what we really want to have
// as the current page. Nothing to do.
lastPage = page;
} else {
// This fragment was restored in the active state,
// but is not the current page. Deactivate it.
if (ft == null) {
ft = fragmentManager.beginTransaction();
}
ft.detach(page.fragment);
}
}
}
// We are now ready to go. Make sure we are switched to the correct page.
attached = true;
ft = doPageChanged(currentTag, ft);
if (ft != null) {
ft.commit();
fragmentManager.executePendingTransactions();
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
attached = false;
}
@Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
ss.curPage = getCurrentPageTag();
return ss;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
setCurrentPageByTag(ss.curPage);
}
@Nullable
private FragmentTransaction doPageChanged(@Nullable String tag,
@Nullable FragmentTransaction ft) {
final PageInfo newPage = getPageInfoForTag(tag);
if (lastPage != newPage) {
if (ft == null) {
ft = fragmentManager.beginTransaction();
}
if (lastPage != null) {
if (lastPage.fragment != null) {
ft.detach(lastPage.fragment);
}
}
if (newPage != null) {
if (newPage.fragment == null) {
newPage.fragment = Fragment.instantiate(context,
newPage.clss.getName(), newPage.args);
ft.add(getContainerId(), newPage.fragment, newPage.tag);
} else {
ft.attach(newPage.fragment);
}
}
lastPage = newPage;
}
return ft;
}
@Nullable
private PageInfo getPageInfoForTag(String pageId) {
for (int i = 0, count = items.size(); i < count; i++) {
final PageInfo page = items.get(i);
if (page.tag.equals(pageId)) {
return page;
}
}
return null;
}
public String getCurrentPageTag() {
if (currentPage >= 0 && currentPage < items.size()) {
return items.get(currentPage).tag;
}
return null;
}
public void setCurrentPageByTag(String tag) {
int i;
for (i = 0; i < items.size(); i++) {
if (items.get(i).tag.equals(tag)) {
setCurrentPage(i);
break;
}
}
}
public void setCurrentPage(int index) {
if (index < 0 || index >= items.size()) {
return;
}
if (index == currentPage) {
return;
}
// notify old page content
// if (currentPage != -1) {
// items.get(currentPage).mContentStrategy.tabClosed();
// }
currentPage = index;
final PageInfo pageInfo = items.get(index);
if (attached) {
final FragmentTransaction ft = doPageChanged(pageInfo.tag, null);
if (ft != null) {
ft.commit();
}
}
invokeOnPageChangeListener();
}
private int getContainerId() {
return getId();
}
/**
* Register a callback to be invoked when the selected state of any of the items
* in this list changes
*
* @param l The callback that will run
*/
public void setOnPageChangedListener(OnPageChangeListener l) {
onPageChangeListener = l;
}
private void invokeOnPageChangeListener() {
if (onPageChangeListener != null) {
onPageChangeListener.onPageChanged(getCurrentPageTag());
}
}
/**
* Interface definition for a callback to be invoked when page changed
*/
public interface OnPageChangeListener {
void onPageChanged(String pageId);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment