|
1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- |
|
2 * This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 package org.mozilla.gecko.home; |
|
7 |
|
8 import org.mozilla.gecko.R; |
|
9 |
|
10 import android.content.Context; |
|
11 import android.content.res.TypedArray; |
|
12 import android.graphics.Canvas; |
|
13 import android.graphics.drawable.Drawable; |
|
14 import android.util.AttributeSet; |
|
15 import android.view.LayoutInflater; |
|
16 import android.view.View; |
|
17 import android.view.ViewTreeObserver; |
|
18 import android.view.accessibility.AccessibilityEvent; |
|
19 import android.widget.LinearLayout; |
|
20 import android.widget.TextView; |
|
21 |
|
22 /** |
|
23 * {@code TabMenuStripLayout} is the view that draws the {@code HomePager} |
|
24 * tabs that are displayed in {@code TabMenuStrip}. |
|
25 */ |
|
26 class TabMenuStripLayout extends LinearLayout |
|
27 implements View.OnFocusChangeListener { |
|
28 |
|
29 private HomePager.OnTitleClickListener onTitleClickListener; |
|
30 private Drawable strip; |
|
31 private View selectedView; |
|
32 |
|
33 // Data associated with the scrolling of the strip drawable. |
|
34 private View toTab; |
|
35 private View fromTab; |
|
36 private float progress; |
|
37 |
|
38 // This variable is used to predict the direction of scroll. |
|
39 private float prevProgress; |
|
40 |
|
41 TabMenuStripLayout(Context context, AttributeSet attrs) { |
|
42 super(context, attrs); |
|
43 |
|
44 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabMenuStrip); |
|
45 final int stripResId = a.getResourceId(R.styleable.TabMenuStrip_strip, -1); |
|
46 a.recycle(); |
|
47 |
|
48 if (stripResId != -1) { |
|
49 strip = getResources().getDrawable(stripResId); |
|
50 } |
|
51 |
|
52 setWillNotDraw(false); |
|
53 } |
|
54 |
|
55 void onAddPagerView(String title) { |
|
56 final TextView button = (TextView) LayoutInflater.from(getContext()).inflate(R.layout.tab_menu_strip, this, false); |
|
57 button.setText(title.toUpperCase()); |
|
58 |
|
59 addView(button); |
|
60 button.setOnClickListener(new ViewClickListener(getChildCount() - 1)); |
|
61 button.setOnFocusChangeListener(this); |
|
62 } |
|
63 |
|
64 void onPageSelected(final int position) { |
|
65 selectedView = getChildAt(position); |
|
66 |
|
67 // Callback to measure and draw the strip after the view is visible. |
|
68 ViewTreeObserver vto = selectedView.getViewTreeObserver(); |
|
69 if (vto.isAlive()) { |
|
70 vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { |
|
71 @Override |
|
72 public void onGlobalLayout() { |
|
73 selectedView.getViewTreeObserver().removeGlobalOnLayoutListener(this); |
|
74 |
|
75 if (strip != null) { |
|
76 strip.setBounds(selectedView.getLeft(), |
|
77 selectedView.getTop(), |
|
78 selectedView.getRight(), |
|
79 selectedView.getBottom()); |
|
80 } |
|
81 |
|
82 prevProgress = position; |
|
83 } |
|
84 }); |
|
85 } |
|
86 } |
|
87 |
|
88 // Page scroll animates the drawable and its bounds from the previous to next child view. |
|
89 void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { |
|
90 if (strip == null) { |
|
91 return; |
|
92 } |
|
93 |
|
94 setScrollingData(position, positionOffset); |
|
95 |
|
96 if (fromTab == null || toTab == null) { |
|
97 return; |
|
98 } |
|
99 |
|
100 final int fromTabLeft = fromTab.getLeft(); |
|
101 final int fromTabRight = fromTab.getRight(); |
|
102 |
|
103 final int toTabLeft = toTab.getLeft(); |
|
104 final int toTabRight = toTab.getRight(); |
|
105 |
|
106 strip.setBounds((int) (fromTabLeft + ((toTabLeft - fromTabLeft) * progress)), |
|
107 0, |
|
108 (int) (fromTabRight + ((toTabRight - fromTabRight) * progress)), |
|
109 getHeight()); |
|
110 invalidate(); |
|
111 } |
|
112 |
|
113 /* |
|
114 * position + positionOffset goes from 0 to 2 as we scroll from page 1 to 3. |
|
115 * Normalized progress is relative to the the direction the page is being scrolled towards. |
|
116 * For this, we maintain direction of scroll with a state, and the child view we are moving towards and away from. |
|
117 */ |
|
118 void setScrollingData(int position, float positionOffset) { |
|
119 if (position >= getChildCount() - 1) { |
|
120 return; |
|
121 } |
|
122 |
|
123 final float currProgress = position + positionOffset; |
|
124 |
|
125 if (prevProgress > currProgress) { |
|
126 toTab = getChildAt(position); |
|
127 fromTab = getChildAt(position + 1); |
|
128 progress = 1 - positionOffset; |
|
129 } else { |
|
130 toTab = getChildAt(position + 1); |
|
131 fromTab = getChildAt(position); |
|
132 progress = positionOffset; |
|
133 } |
|
134 |
|
135 prevProgress = currProgress; |
|
136 } |
|
137 |
|
138 @Override |
|
139 public void onDraw(Canvas canvas) { |
|
140 super.onDraw(canvas); |
|
141 |
|
142 if (strip != null) { |
|
143 strip.draw(canvas); |
|
144 } |
|
145 } |
|
146 |
|
147 @Override |
|
148 public void onFocusChange(View v, boolean hasFocus) { |
|
149 if (v == this && hasFocus && getChildCount() > 0) { |
|
150 selectedView.requestFocus(); |
|
151 return; |
|
152 } |
|
153 |
|
154 if (!hasFocus) { |
|
155 return; |
|
156 } |
|
157 |
|
158 int i = 0; |
|
159 final int numTabs = getChildCount(); |
|
160 |
|
161 while (i < numTabs) { |
|
162 View view = getChildAt(i); |
|
163 if (view == v) { |
|
164 view.requestFocus(); |
|
165 if (isShown()) { |
|
166 // A view is focused so send an event to announce the menu strip state. |
|
167 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); |
|
168 } |
|
169 break; |
|
170 } |
|
171 |
|
172 i++; |
|
173 } |
|
174 } |
|
175 |
|
176 void setOnTitleClickListener(HomePager.OnTitleClickListener onTitleClickListener) { |
|
177 this.onTitleClickListener = onTitleClickListener; |
|
178 } |
|
179 |
|
180 private class ViewClickListener implements OnClickListener { |
|
181 private final int mIndex; |
|
182 |
|
183 public ViewClickListener(int index) { |
|
184 mIndex = index; |
|
185 } |
|
186 |
|
187 @Override |
|
188 public void onClick(View view) { |
|
189 if (onTitleClickListener != null) { |
|
190 onTitleClickListener.onTitleClicked(mIndex); |
|
191 } |
|
192 } |
|
193 } |
|
194 } |