问题
I am trying to figure out how to create a breadcrumb in my android app's (material design) toolbar.
This can be seen in the latest Dropbox android application, in which the breadcrumb is used to display the navigation of the files.
I have also found an image (which I took from another similar question), which can be seen here:
Can you guide me on how I can do this?
回答1:
I don't think you need a library. If you create a View (maybe a FrameLayout) and then you set it as background the same color of the Toolbar and then you put a TextView inside it, then it's done. You even can extend the TextView class and override some methods to animate the change. You can pass the current String of the breadcrumb via intents between activities and then each knows how to update it.
回答2:
I created a working example of this for an app, thought I would share because it became more confusing than it needed to be lol. Maybe it could save some devs a lot of effort that should not be needed :)
Add a HorizontalScrollView to your AppBarLayout (layout/examplebreadcrumbs_layout)
<android.support.v7.widget.AppBarLayout
android:id="@+id/app_bar"
style="@style/HeaderBar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?colorPrimary"
android:minHeight="?attr/actionBarSize"
app:contentInsetStart="72dp"
app:navigationContentDescription="up" />
<HorizontalScrollView android:id="@+id/breadcrumb_wrapper"
android:layout_width="match_parent"
android:layout_height="50dp"
android:scrollbars="none">
<LinearLayout android:id="@+id/breadcrumbs"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:gravity="center_vertical"
android:paddingEnd="@dimen/activity_horizontal_margin_2"
android:paddingStart="@dimen/activity_horizontal_margin_2"/>
</HorizontalScrollView>
</android.support.v7.widget.AppBarLayout>
Create a LinearLayout that will be used for each 'breadcrumb'. You will inflate the same layout for each. (layout/breadcrumb_text)
<LinearLayout android:id="@+id/crumb"
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="match_parent">
<TextView
android:id="@+id/crumb_arrow"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:visibility="gone"
android:gravity="center_vertical"
android:text="@string/greater_than"
android:padding="4dp"
android:fontFamily="sans-serif-condensed"
android:textSize="20sp"
android:textColor="@color/breadcrumb_text_dim"
android:textAllCaps="true"
tools:visibility="visible"/>
<TextView
android:id="@+id/crumb_text"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:padding="4dp"
android:gravity="center_vertical"
android:visibility="gone"
android:fontFamily="sans-serif"
android:textAllCaps="true"
android:textSize="14sp"
android:textColor="@color/breadcrumb_text"
android:background="?selectableItemBackground"/>
</LinearLayout>
ExampleBreadcrumbs that demonstrates the basic create/updates/scroll to methods that you could easily add to your app flow. Is not 100% copy paste because your app will be a little different depending on what activity and setup your breadcrumbs would be added to. If you need anything else to get this working... let me know.
public class ExampleBreadcrumbs extends AppCompatActivity {
private static final String ROOT_NODE = "root";
private SparseArray<Long> levelNodeIds = new SparseArray<>();
private SparseArray<String> levelNodeNames = new SparseArray<>();
public int currentLevel = 0;
ViewGroup breadcrumbBar;
HorizontalScrollView breadcrumbWrapper;
String[] parents = new String[] {"Parent Item 1", "Parent Item 2",
"Parent Item 3", "Parent Item 4"};
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.examplebreadcrumbs_layout);
breadcrumbWrapper = ((HorizontalScrollView)findViewById(R.id.breadcrumb_wrapper));
breadcrumbBar = (ViewGroup) findViewById(R.id.breadcrumbs);
// TODO setup your adapter or whatever
createParentNodes();
}
@Override public void onBackPressed() {
switch (currentLevel) {
case 0:
super.onBackPressed();
break;
case 1:
createParentNodes();
adapter.notifyDataSetChanged();
break;
default:
// Get values from level you are requesting, not current.
final long id = levelNodeIds.get(currentLevel - 1);
final String name = levelNodeNames.get(currentLevel - 1);
getChildren(id, name, -1);
break;
}
}
@Override public void onItemClick(View itemView, final String name) {
long nodeId = (Long) itemView.getTag();
getChildren(nodeId, name, 1);
}
private void getChildren(final long nodeId, final String name, final int difference) {
if (nodeId != 0) {
// Example case using retrofit like setup
getChildren(nodeId, new Callback<NodesResponse>() {
@Override
public void onResponse(Call<NodesResponse> call, Response<NodesResponse> response) {
if (response.body() != null) {
if (response.body().getNodeObjs() != null && !response.body().getNodeObjs()
.isEmpty()) {
createChildNodes(nodeId, name, response.body().getNodeObjs(), difference);
}
}
}
@Override
public void onFailure(Call<NodesResponse> call, Throwable t) { // TODO}
});
} else {
createParentNodes();
adapter.notifyDataSetChanged();
}
}
private void createParentNodes() {
currentLevel = 0;
levelNodeIds.put(currentLevel, 0L);
levelNodeNames.put(currentLevel, ROOT_NODE);
listItems.clear();
int count = 0;
for(String parent : parents) {
listItems.add(new NodeObj(nodeId[count], parent, parentDescriptions[count]));
count++;
}
adapter.notifyDataSetChanged();
levelNodeNames.put(currentLevel, ROOT_NODE);
updateBreadcrumbs(true);
}
private void createChildNodes(long id, String name, List<NodeObj> NodeObjs, int difference) {
listItems.clear();
for (NodeObj obj : NodeObjs) {
listItems.add(new NodeObj(obj.getnodeId(), obj.getNodeTitle(), obj.getNodeDescription()));
}
adapter.notifyDataSetChanged();
currentLevel = currentLevel + difference;
levelNodeIds.put(currentLevel, id);
levelNodeNames.put(currentLevel, name);
// If forward, it will create new node along with updating other nodes.
updateBreadcrumbs(difference > 0);
}
private void updateBreadcrumbs(boolean createNew) {
if (createNew) {
createBreadcrumb();
}
// Loops and updates all breadcrumb nodes
updateBreadcrumbNodes();
//printDebug();
}
private void createBreadcrumb() {
boolean needToCreate = true;
// Remove view if one exists.
ViewGroup nodeToReplace = (ViewGroup) breadcrumbBar.getChildAt(currentLevel);
if (nodeToReplace != null) {
TextView toReplaceTextView = (TextView) nodeToReplace.findViewById(R.id.crumb_text);
// If node history is not the same, remove view and after.
if (toReplaceTextView.getText().equals(levelNodeNames.get(currentLevel))) {
needToCreate = false;
scrollIfBreadcrumbNotViewable(nodeToReplace);
} else {
needToCreate = true;
// Removes all nodes >= current level
for (int i = breadcrumbBar.getChildCount() - 1; i >= currentLevel; i--) {
breadcrumbBar.removeViewAt(i);
}
}
}
if (needToCreate) {
// Inflate new breadcrumb node.
final ViewGroup crumbLayout = (ViewGroup) LayoutInflater.from(this)
.inflate(R.layout.breadcrumb_text, appBarLayout, false);
crumbLayout.setTag(levelNodeIds.get(currentLevel));
// Sets name of node (color gets updated in 'updateBreadcrumbNodes').
TextView crumbTextView = (TextView) crumbLayout.findViewById(R.id.crumb_text);
crumbTextView.setVisibility(View.VISIBLE);
crumbTextView.setText(levelNodeNames.get(currentLevel));
View crumbArrow = crumbLayout.findViewById(R.id.crumb_arrow);
crumbArrow.setVisibility(levelNodeNames.get(currentLevel).equals(ROOT_NODE) ? View.GONE : View.VISIBLE);
// Add new breadcrumb node to bar.
breadcrumbBar.addView(crumbLayout, currentLevel);
scrollIfBreadcrumbNotViewable(crumbLayout);
// Create click listener to jump to history.
crumbLayout.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
int viewIndex = breadcrumbBar.indexOfChild(v);
if (viewIndex == currentLevel) return;
TextView crumbText = (TextView) v.findViewById(R.id.crumb_text);
int index = levelNodeNames.indexOfValue(crumbText.getText().toString());
long nodeId = (Long) v.getTag();
getChildren(nodeId, levelNodeNames.get(index), viewIndex - currentLevel);
}
});
}
}
private void updateBreadcrumbNodes() {
for (int i = 0; i < breadcrumbBar.getChildCount(); i++) {
boolean activeNode = i == (currentLevel);
ViewGroup node = (ViewGroup) breadcrumbBar.getChildAt(i);
TextView crumbTextView = ((TextView) node.findViewById(R.id.crumb_text));
crumbTextView.setTextColor(activeNode ? ContextCompat.getColor(this, R.color.breadcrumb_text) :
ContextCompat.getColor(this, R.color.breadcrumb_text_dim));
}
breadcrumbWrapper.fullScroll(HorizontalScrollView.FOCUS_BACKWARD);
}
private void scrollIfBreadcrumbNotViewable(final ViewGroup view) {
Rect scrollBounds = new Rect();
toolbar.getHitRect(scrollBounds);
if (!view.getLocalVisibleRect(scrollBounds) || scrollBounds.width() < view.getWidth()) {
view.postDelayed(new Runnable() {
public void run() {
breadcrumbWrapper.smoothScrollTo(view.getLeft(), 0);
}
}, 100L);
}
}
void printDebug() {
System.out.println("** DEBUG ** | level: '" + currentLevel + "' | node: id='" + levelNodeIds.get(currentLevel) + "' name='" + levelNodeNames.get(currentLevel) + "' |");
}
}
回答3:
I know that this is fairly old but this seems just as good as any place to post this. There is a BreadcrumbsView via Github out there that seems to fit the need for purpose. Hope it helps anyone else looking for a similar solution.
来源:https://stackoverflow.com/questions/31161333/android-material-design-how-do-i-place-a-breadcrumb-navigation-in-the-toolba