blob: 139095f05acba0e973324084e8b4282018ab4336 [file] [log] [blame]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app;
import android.animation.LayoutTransition;
import android.app.FragmentManager.BackStackEntry;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
/**
* Helper class for showing "bread crumbs" representing the fragment
* stack in an activity. This is intended to be used with
* {@link ActionBar#setCustomNavigationMode(View)
* ActionBar.setCustomNavigationMode(View)} to place the bread crumbs in
* the navigation area of the action bar.
*
* <p>The default style for this view is
* {@link android.R.style#Widget_FragmentBreadCrumbs}.
*/
public class FragmentBreadCrumbs extends ViewGroup
implements FragmentManager.OnBackStackChangedListener {
Activity mActivity;
LayoutInflater mInflater;
LinearLayout mContainer;
// Hahah
BackStackRecord mTopEntry;
public FragmentBreadCrumbs(Context context) {
this(context, null);
}
public FragmentBreadCrumbs(Context context, AttributeSet attrs) {
this(context, attrs, android.R.style.Widget_FragmentBreadCrumbs);
}
public FragmentBreadCrumbs(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/**
* Attach the bread crumbs to their activity. This must be called once
* when creating the bread crumbs.
*/
public void setActivity(Activity a) {
mActivity = a;
mInflater = (LayoutInflater)a.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mContainer = (LinearLayout)mInflater.inflate(
com.android.internal.R.layout.fragment_bread_crumbs,
this, false);
addView(mContainer);
a.getFragmentManager().addOnBackStackChangedListener(this);
updateCrumbs();
setLayoutTransition(new LayoutTransition());
}
/**
* Set a custom title for the bread crumbs. This will be the first entry
* shown at the left, representing the root of the bread crumbs. If the
* title is null, it will not be shown.
*/
public void setTitle(CharSequence title, CharSequence shortTitle) {
if (title == null) {
mTopEntry = null;
} else {
mTopEntry = new BackStackRecord((FragmentManagerImpl)
mActivity.getFragmentManager());
mTopEntry.setBreadCrumbTitle(title);
mTopEntry.setBreadCrumbShortTitle(shortTitle);
}
updateCrumbs();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// Eventually we should implement our own layout of the views,
// rather than relying on a linear layout.
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
int childRight = mPaddingLeft + child.getMeasuredWidth() - mPaddingRight;
int childBottom = mPaddingTop + child.getMeasuredHeight() - mPaddingBottom;
child.layout(mPaddingLeft, mPaddingTop, childRight, childBottom);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int count = getChildCount();
int maxHeight = 0;
int maxWidth = 0;
int measuredChildState = 0;
// Find rightmost and bottom-most child
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
measureChild(child, widthMeasureSpec, heightMeasureSpec);
maxWidth = Math.max(maxWidth, child.getMeasuredWidth());
maxHeight = Math.max(maxHeight, child.getMeasuredHeight());
measuredChildState = combineMeasuredStates(measuredChildState,
child.getMeasuredState());
}
}
// Account for padding too
maxWidth += mPaddingLeft + mPaddingRight;
maxHeight += mPaddingTop + mPaddingBottom;
// Check against our minimum height and width
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, measuredChildState),
resolveSizeAndState(maxHeight, heightMeasureSpec,
measuredChildState<<MEASURED_HEIGHT_STATE_SHIFT));
}
@Override
public void onBackStackChanged() {
updateCrumbs();
}
void updateCrumbs() {
FragmentManager fm = mActivity.getFragmentManager();
int numEntries = fm.countBackStackEntries();
int numViews = mContainer.getChildCount();
for (int i = mTopEntry != null ? -1 : 0; i < numEntries; i++) {
BackStackEntry bse = i == -1 ? mTopEntry : fm.getBackStackEntry(i);
int viewI = mTopEntry != null ? i + 1 : i;
if (viewI < numViews) {
View v = mContainer.getChildAt(viewI);
Object tag = v.getTag();
if (tag != bse) {
for (int j = viewI; j < numViews; j++) {
mContainer.removeViewAt(viewI);
}
numViews = viewI;
}
}
if (viewI >= numViews) {
View item = mInflater.inflate(
com.android.internal.R.layout.fragment_bread_crumb_item,
this, false);
TextView text = (TextView)item.findViewById(com.android.internal.R.id.title);
text.setText(bse.getBreadCrumbTitle());
item.setTag(bse);
if (viewI == 0) {
text.setCompoundDrawables(null, null, null, null);
}
mContainer.addView(item);
item.setOnClickListener(mOnClickListener);
}
}
int viewI = mTopEntry != null ? numEntries + 1 : numEntries;
numViews = mContainer.getChildCount();
while (numViews > viewI) {
mContainer.removeViewAt(numViews-1);
numViews--;
}
}
private OnClickListener mOnClickListener = new OnClickListener() {
public void onClick(View v) {
if (v.getTag() instanceof BackStackEntry) {
BackStackEntry bse = (BackStackEntry) v.getTag();
mActivity.getFragmentManager().popBackStack(bse.getId(),
bse == mTopEntry? FragmentManager.POP_BACK_STACK_INCLUSIVE : 0);
}
}
};
}