Android material design - How do I place a breadcrumb (navigation) in the toolbar?

北城余情 提交于 2020-01-12 08:01:28

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!