1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/base/home/TabMenuStripLayout.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,194 @@ 1.4 +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +package org.mozilla.gecko.home; 1.10 + 1.11 +import org.mozilla.gecko.R; 1.12 + 1.13 +import android.content.Context; 1.14 +import android.content.res.TypedArray; 1.15 +import android.graphics.Canvas; 1.16 +import android.graphics.drawable.Drawable; 1.17 +import android.util.AttributeSet; 1.18 +import android.view.LayoutInflater; 1.19 +import android.view.View; 1.20 +import android.view.ViewTreeObserver; 1.21 +import android.view.accessibility.AccessibilityEvent; 1.22 +import android.widget.LinearLayout; 1.23 +import android.widget.TextView; 1.24 + 1.25 +/** 1.26 + * {@code TabMenuStripLayout} is the view that draws the {@code HomePager} 1.27 + * tabs that are displayed in {@code TabMenuStrip}. 1.28 + */ 1.29 +class TabMenuStripLayout extends LinearLayout 1.30 + implements View.OnFocusChangeListener { 1.31 + 1.32 + private HomePager.OnTitleClickListener onTitleClickListener; 1.33 + private Drawable strip; 1.34 + private View selectedView; 1.35 + 1.36 + // Data associated with the scrolling of the strip drawable. 1.37 + private View toTab; 1.38 + private View fromTab; 1.39 + private float progress; 1.40 + 1.41 + // This variable is used to predict the direction of scroll. 1.42 + private float prevProgress; 1.43 + 1.44 + TabMenuStripLayout(Context context, AttributeSet attrs) { 1.45 + super(context, attrs); 1.46 + 1.47 + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabMenuStrip); 1.48 + final int stripResId = a.getResourceId(R.styleable.TabMenuStrip_strip, -1); 1.49 + a.recycle(); 1.50 + 1.51 + if (stripResId != -1) { 1.52 + strip = getResources().getDrawable(stripResId); 1.53 + } 1.54 + 1.55 + setWillNotDraw(false); 1.56 + } 1.57 + 1.58 + void onAddPagerView(String title) { 1.59 + final TextView button = (TextView) LayoutInflater.from(getContext()).inflate(R.layout.tab_menu_strip, this, false); 1.60 + button.setText(title.toUpperCase()); 1.61 + 1.62 + addView(button); 1.63 + button.setOnClickListener(new ViewClickListener(getChildCount() - 1)); 1.64 + button.setOnFocusChangeListener(this); 1.65 + } 1.66 + 1.67 + void onPageSelected(final int position) { 1.68 + selectedView = getChildAt(position); 1.69 + 1.70 + // Callback to measure and draw the strip after the view is visible. 1.71 + ViewTreeObserver vto = selectedView.getViewTreeObserver(); 1.72 + if (vto.isAlive()) { 1.73 + vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 1.74 + @Override 1.75 + public void onGlobalLayout() { 1.76 + selectedView.getViewTreeObserver().removeGlobalOnLayoutListener(this); 1.77 + 1.78 + if (strip != null) { 1.79 + strip.setBounds(selectedView.getLeft(), 1.80 + selectedView.getTop(), 1.81 + selectedView.getRight(), 1.82 + selectedView.getBottom()); 1.83 + } 1.84 + 1.85 + prevProgress = position; 1.86 + } 1.87 + }); 1.88 + } 1.89 + } 1.90 + 1.91 + // Page scroll animates the drawable and its bounds from the previous to next child view. 1.92 + void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 1.93 + if (strip == null) { 1.94 + return; 1.95 + } 1.96 + 1.97 + setScrollingData(position, positionOffset); 1.98 + 1.99 + if (fromTab == null || toTab == null) { 1.100 + return; 1.101 + } 1.102 + 1.103 + final int fromTabLeft = fromTab.getLeft(); 1.104 + final int fromTabRight = fromTab.getRight(); 1.105 + 1.106 + final int toTabLeft = toTab.getLeft(); 1.107 + final int toTabRight = toTab.getRight(); 1.108 + 1.109 + strip.setBounds((int) (fromTabLeft + ((toTabLeft - fromTabLeft) * progress)), 1.110 + 0, 1.111 + (int) (fromTabRight + ((toTabRight - fromTabRight) * progress)), 1.112 + getHeight()); 1.113 + invalidate(); 1.114 + } 1.115 + 1.116 + /* 1.117 + * position + positionOffset goes from 0 to 2 as we scroll from page 1 to 3. 1.118 + * Normalized progress is relative to the the direction the page is being scrolled towards. 1.119 + * For this, we maintain direction of scroll with a state, and the child view we are moving towards and away from. 1.120 + */ 1.121 + void setScrollingData(int position, float positionOffset) { 1.122 + if (position >= getChildCount() - 1) { 1.123 + return; 1.124 + } 1.125 + 1.126 + final float currProgress = position + positionOffset; 1.127 + 1.128 + if (prevProgress > currProgress) { 1.129 + toTab = getChildAt(position); 1.130 + fromTab = getChildAt(position + 1); 1.131 + progress = 1 - positionOffset; 1.132 + } else { 1.133 + toTab = getChildAt(position + 1); 1.134 + fromTab = getChildAt(position); 1.135 + progress = positionOffset; 1.136 + } 1.137 + 1.138 + prevProgress = currProgress; 1.139 + } 1.140 + 1.141 + @Override 1.142 + public void onDraw(Canvas canvas) { 1.143 + super.onDraw(canvas); 1.144 + 1.145 + if (strip != null) { 1.146 + strip.draw(canvas); 1.147 + } 1.148 + } 1.149 + 1.150 + @Override 1.151 + public void onFocusChange(View v, boolean hasFocus) { 1.152 + if (v == this && hasFocus && getChildCount() > 0) { 1.153 + selectedView.requestFocus(); 1.154 + return; 1.155 + } 1.156 + 1.157 + if (!hasFocus) { 1.158 + return; 1.159 + } 1.160 + 1.161 + int i = 0; 1.162 + final int numTabs = getChildCount(); 1.163 + 1.164 + while (i < numTabs) { 1.165 + View view = getChildAt(i); 1.166 + if (view == v) { 1.167 + view.requestFocus(); 1.168 + if (isShown()) { 1.169 + // A view is focused so send an event to announce the menu strip state. 1.170 + sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); 1.171 + } 1.172 + break; 1.173 + } 1.174 + 1.175 + i++; 1.176 + } 1.177 + } 1.178 + 1.179 + void setOnTitleClickListener(HomePager.OnTitleClickListener onTitleClickListener) { 1.180 + this.onTitleClickListener = onTitleClickListener; 1.181 + } 1.182 + 1.183 + private class ViewClickListener implements OnClickListener { 1.184 + private final int mIndex; 1.185 + 1.186 + public ViewClickListener(int index) { 1.187 + mIndex = index; 1.188 + } 1.189 + 1.190 + @Override 1.191 + public void onClick(View view) { 1.192 + if (onTitleClickListener != null) { 1.193 + onTitleClickListener.onTitleClicked(mIndex); 1.194 + } 1.195 + } 1.196 + } 1.197 +}