Bug 1317446 - 3. Add TabStripDividerItem for TabStripView dividers and spacing. r?sebastian
MozReview-Commit-ID: 5OC58tEGQrM
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/tabs/TabStripDividerItem.java
@@ -0,0 +1,90 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.tabs;
+
+import org.mozilla.gecko.R;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+
+class TabStripDividerItem extends RecyclerView.ItemDecoration {
+ private final int margin;
+ private final int dividerWidth;
+ private final int dividerHeight;
+ private final int dividerPaddingBottom;
+ private final Paint dividerPaint;
+
+ TabStripDividerItem(Context context) {
+ margin = (int) context.getResources().getDimension(R.dimen.tablet_tab_strip_item_margin);
+ dividerWidth = (int) context.getResources().getDimension(R.dimen.tablet_tab_strip_divider_width);
+ dividerHeight = (int) context.getResources().getDimension(R.dimen.tablet_tab_strip_divider_height);
+ dividerPaddingBottom = (int) context.getResources().getDimension(R.dimen.tablet_tab_strip_divider_padding_bottom);
+
+ dividerPaint = new Paint();
+ dividerPaint.setColor(ContextCompat.getColor(context, R.color.tablet_tab_strip_divider_color));
+ dividerPaint.setStyle(Paint.Style.FILL_AND_STROKE);
+ }
+
+ /**
+ * Return whether a divider should be drawn on the left side of the tab represented by
+ * {@code view}.
+ */
+ private static boolean drawLeftDividerForView(View view, RecyclerView parent) {
+ final int position = parent.getChildAdapterPosition(view);
+ // No left divider if this is tab 0 or this tab is currently pressed.
+ if (position == 0 || view.isPressed()) {
+ return false;
+ }
+
+ final int selectedPosition = ((TabStripView) parent).getPositionForSelectedTab();
+ // No left divider if this tab or the previous tab is the current selected tab.
+ if (selectedPosition != RecyclerView.NO_POSITION &&
+ (position == selectedPosition || position == selectedPosition + 1)) {
+ return false;
+ }
+
+ final RecyclerView.ViewHolder holder = parent.findViewHolderForAdapterPosition(position - 1);
+ // No left divider if the previous tab is currently pressed.
+ if (holder != null && holder.itemView.isPressed()) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
+ final int position = parent.getChildAdapterPosition(view);
+ final int leftOffset = position == 0 ? 0 : margin;
+ final int rightOffset = position == parent.getAdapter().getItemCount() - 1 ? 0 : margin;
+
+ outRect.set(leftOffset, 0, rightOffset, 0);
+ }
+
+ @Override
+ public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
+ final int childCount = parent.getChildCount();
+ if (childCount == 0) {
+ return;
+ }
+
+ for (int i = 0; i < childCount; i++) {
+ final View child = parent.getChildAt(i);
+ if (drawLeftDividerForView(child, parent)) {
+ final float left = child.getLeft() + child.getTranslationX() + Math.abs(margin);
+ final float top = child.getTop() + child.getTranslationY();
+ final float dividerTop = top + child.getHeight() - dividerHeight - dividerPaddingBottom;
+ final float dividerBottom = dividerTop + dividerHeight;
+ c.drawRect(left - dividerWidth, dividerTop, left, dividerBottom, dividerPaint);
+ }
+ }
+ }
+}
--- a/mobile/android/base/java/org/mozilla/gecko/tabs/TabStripView.java
+++ b/mobile/android/base/java/org/mozilla/gecko/tabs/TabStripView.java
@@ -46,26 +46,28 @@ public class TabStripView extends Recycl
fadingEdgePaint = new Paint();
final Resources resources = getResources();
fadingEdgeSize =
resources.getDimensionPixelOffset(R.dimen.tablet_tab_strip_fading_edge_size);
animatorListener = new TabAnimatorListener();
+ setChildrenDrawingOrderEnabled(true);
+
adapter = new TabStripAdapter(context);
setAdapter(adapter);
final LinearLayoutManager layoutManager = new LinearLayoutManager(context);
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
setLayoutManager(layoutManager);
setItemAnimator(new TabStripItemAnimator(ANIM_TIME_MS));
- // TODO add item decoration.
+ addItemDecoration(new TabStripDividerItem(context));
}
/* package */ void refreshTabs() {
// Store a different copy of the tabs, so that we don't have
// to worry about accidentally updating it on the wrong thread.
final List<Tab> tabs = new ArrayList<>();
for (final Tab tab : Tabs.getInstance().getTabsInOrder()) {
@@ -119,18 +121,22 @@ public class TabStripView extends Recycl
}
}
/* package */ void updateTab(Tab tab) {
adapter.notifyTabChanged(tab);
}
+ public int getPositionForSelectedTab() {
+ return adapter.getPositionForTab(Tabs.getInstance().getSelectedTab());
+ }
+
private void updateSelectedPosition() {
- final int selected = adapter.getPositionForTab(Tabs.getInstance().getSelectedTab());
+ final int selected = getPositionForSelectedTab();
if (selected != -1) {
scrollToPosition(selected);
}
}
public void refresh() {
// This gets called after a rotation. Without the delay the scroll can fail to scroll far
// enough if the selected position is the last or next to last position (and there are
@@ -145,16 +151,55 @@ public class TabStripView extends Recycl
@Override
public void onChildAttachedToWindow(View child) {
// Make sure we didn't miss any resets after animations etc.
child.setTranslationX(0);
child.setTranslationY(0);
}
+ /**
+ * Return the position of the currently selected tab relative to the tabs currently visible in
+ * the tabs list, or -1 if the currently selected tab isn't visible in the tabs list.
+ */
+ private int getRelativeSelectedPosition(int visibleTabsCount) {
+ final int selectedPosition = getPositionForSelectedTab();
+ final LinearLayoutManager layoutManager = (LinearLayoutManager) getLayoutManager();
+ final int firstVisiblePosition = layoutManager.findFirstVisibleItemPosition();
+ final int relativeSelectedPosition = selectedPosition - firstVisiblePosition;
+ if (relativeSelectedPosition < 0 || relativeSelectedPosition > visibleTabsCount - 1) {
+ return -1;
+ }
+
+ return relativeSelectedPosition;
+ }
+
+ @Override
+ protected int getChildDrawingOrder(int childCount, int i) {
+ final int relativeSelectedPosition = getRelativeSelectedPosition(childCount);
+ if (relativeSelectedPosition == -1) {
+ // The selected tab isn't visible, so we don't need to adjust drawing order.
+ return i;
+ }
+
+ // Explanation of the input parameters: there are childCount tabs visible, and right now
+ // we're returning which of those tabs to draw i'th, for some i between 0 and
+ // childCount - 1.
+ if (i == childCount - 1) {
+ // Draw the selected tab last.
+ return relativeSelectedPosition;
+ } else if (i >= relativeSelectedPosition) {
+ // Draw the tabs after the selected tab one iteration earlier than normal.
+ return i + 1;
+ } else {
+ // Draw the tabs before the selected tab in their normal order.
+ return i;
+ }
+ }
+
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (w == oldw) {
return;
}
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -829,16 +829,17 @@ else:
max_sdk_version = 999
# Only bother to include new tablet code if we're building for tablet-capable
# OS releases.
if max_sdk_version >= 11:
gbjar.sources += ['java/org/mozilla/gecko/' + x for x in [
'tabs/TabStrip.java',
'tabs/TabStripAdapter.java',
+ 'tabs/TabStripDividerItem.java',
'tabs/TabStripItemAnimator.java',
'tabs/TabStripItemView.java',
'tabs/TabStripView.java'
]]
gbjar.extra_jars += [
OBJDIR + '/../javaaddons/javaaddons-1.0.jar',
'gecko-R.jar',
--- a/mobile/android/base/resources/values/colors.xml
+++ b/mobile/android/base/resources/values/colors.xml
@@ -140,9 +140,11 @@
<color name="action_bar_bg_color">@color/toolbar_grey</color>
<color name="activity_stream_divider">#FFD2D2D2</color>
<color name="activity_stream_subtitle">#FF919191</color>
<color name="activity_stream_timestamp">#FFD3D3D3</color>
<color name="activity_stream_icon">#FF919191</color>
+ <color name="tablet_tab_strip_divider_color">#555555</color>
+
</resources>
--- a/mobile/android/base/resources/values/dimens.xml
+++ b/mobile/android/base/resources/values/dimens.xml
@@ -35,16 +35,20 @@
<!-- This is the system default for the vertical padding for the divider of the TabWidget.
Used to mimic the divider padding on the tablet tabs panel back button. -->
<dimen name="tab_panel_divider_vertical_padding">12dp</dimen>
<dimen name="tablet_tab_strip_height">48dp</dimen>
<dimen name="tablet_tab_strip_item_width">208dp</dimen>
<dimen name="tablet_tab_strip_fading_edge_size">15dp</dimen>
+ <dimen name="tablet_tab_strip_item_margin">-14dp</dimen>
+ <dimen name="tablet_tab_strip_divider_width">1dp</dimen>
+ <dimen name="tablet_tab_strip_divider_height">30dp</dimen>
+ <dimen name="tablet_tab_strip_divider_padding_bottom">6dp</dimen>
<dimen name="tablet_browser_toolbar_menu_item_width">56dp</dimen>
<!-- Padding combines with an 18dp image to form the menu item width and height. -->
<dimen name="tablet_browser_toolbar_menu_item_padding_horizontal">19dp</dimen>
<dimen name="tablet_browser_toolbar_menu_item_inset_vertical">5dp</dimen>
<dimen name="tablet_browser_toolbar_menu_item_inset_horizontal">3dp</dimen>
<dimen name="tablet_tab_strip_button_inset">5dp</dimen>
<!-- Dimensions used by Favicons and FaviconView -->