/* * 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. * *
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; // 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()); } } // 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(resolveSize(maxWidth, widthMeasureSpec), resolveSize(maxHeight, heightMeasureSpec)); } @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); } } }; }