Home | History | Annotate | Download | only in browser
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
      5  * use this file except in compliance with the License. You may obtain a copy of
      6  * the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
     12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
     13  * License for the specific language governing permissions and limitations under
     14  * the License.
     15  */
     16 
     17 package com.android.browser;
     18 
     19 import com.android.browser.TabBar.TabView;
     20 
     21 import android.animation.ObjectAnimator;
     22 import android.content.Context;
     23 import android.util.AttributeSet;
     24 import android.view.View;
     25 import android.widget.HorizontalScrollView;
     26 import android.widget.LinearLayout;
     27 
     28 /**
     29  * custom view for displaying tabs in the tabbed title bar
     30  */
     31 public class TabScrollView extends HorizontalScrollView {
     32 
     33     private LinearLayout mContentView;
     34     private int mSelected;
     35     private int mAnimationDuration;
     36     private int mTabOverlap;
     37 
     38     /**
     39      * @param context
     40      * @param attrs
     41      * @param defStyle
     42      */
     43     public TabScrollView(Context context, AttributeSet attrs, int defStyle) {
     44         super(context, attrs, defStyle);
     45         init(context);
     46     }
     47 
     48     /**
     49      * @param context
     50      * @param attrs
     51      */
     52     public TabScrollView(Context context, AttributeSet attrs) {
     53         super(context, attrs);
     54         init(context);
     55     }
     56 
     57     /**
     58      * @param context
     59      */
     60     public TabScrollView(Context context) {
     61         super(context);
     62         init(context);
     63     }
     64 
     65     private void init(Context ctx) {
     66         mAnimationDuration = ctx.getResources().getInteger(
     67                 R.integer.tab_animation_duration);
     68         mTabOverlap = (int) ctx.getResources().getDimension(R.dimen.tab_overlap);
     69         setHorizontalScrollBarEnabled(false);
     70         setOverScrollMode(OVER_SCROLL_NEVER);
     71         mContentView = new TabLayout(ctx);
     72         mContentView.setOrientation(LinearLayout.HORIZONTAL);
     73         mContentView.setLayoutParams(
     74                 new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT));
     75         mContentView.setPadding(
     76                 (int) ctx.getResources().getDimension(R.dimen.tab_first_padding_left),
     77                 0, 0, 0);
     78         addView(mContentView);
     79         mSelected = -1;
     80         // prevent ProGuard from removing the property methods
     81         setScroll(getScroll());
     82     }
     83 
     84     @Override
     85     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
     86         super.onLayout(changed, left, top, right, bottom);
     87         ensureChildVisible(getSelectedTab());
     88     }
     89 
     90     // in case of a configuration change, adjust tab width
     91     protected void updateLayout() {
     92         final int count = mContentView.getChildCount();
     93         for (int i = 0; i < count; i++) {
     94             final TabView tv = (TabView) mContentView.getChildAt(i);
     95             tv.updateLayoutParams();
     96         }
     97         ensureChildVisible(getSelectedTab());
     98     }
     99 
    100     void setSelectedTab(int position) {
    101         View v = getSelectedTab();
    102         if (v != null) {
    103             v.setActivated(false);
    104         }
    105         mSelected = position;
    106         v = getSelectedTab();
    107         if (v != null) {
    108             v.setActivated(true);
    109         }
    110         requestLayout();
    111     }
    112 
    113     int getChildIndex(View v) {
    114         return mContentView.indexOfChild(v);
    115     }
    116 
    117     View getSelectedTab() {
    118         if ((mSelected >= 0) && (mSelected < mContentView.getChildCount())) {
    119             return mContentView.getChildAt(mSelected);
    120         } else {
    121             return null;
    122         }
    123     }
    124 
    125     void clearTabs() {
    126         mContentView.removeAllViews();
    127     }
    128 
    129     void addTab(View tab) {
    130         mContentView.addView(tab);
    131         tab.setActivated(false);
    132     }
    133 
    134     void removeTab(View tab) {
    135         int ix = mContentView.indexOfChild(tab);
    136         if (ix == mSelected) {
    137             mSelected = -1;
    138         } else if (ix < mSelected) {
    139             mSelected--;
    140         }
    141         mContentView.removeView(tab);
    142     }
    143 
    144     private void ensureChildVisible(View child) {
    145         if (child != null) {
    146             int childl = child.getLeft();
    147             int childr = childl + child.getWidth();
    148             int viewl = getScrollX();
    149             int viewr = viewl + getWidth();
    150             if (childl < viewl) {
    151                 // need scrolling to left
    152                 animateScroll(childl);
    153             } else if (childr > viewr) {
    154                 // need scrolling to right
    155                 animateScroll(childr - viewr + viewl);
    156             }
    157         }
    158     }
    159 
    160 // TODO: These animations are broken and don't work correctly, removing for now
    161 //       as animateOut is actually causing issues
    162 //    private void animateIn(View tab) {
    163 //        ObjectAnimator animator = ObjectAnimator.ofInt(tab, "TranslationX", 500, 0);
    164 //        animator.setDuration(mAnimationDuration);
    165 //        animator.start();
    166 //    }
    167 //
    168 //    private void animateOut(final View tab) {
    169 //        ObjectAnimator animator = ObjectAnimator.ofInt(
    170 //                tab, "TranslationX", 0, getScrollX() - tab.getRight());
    171 //        animator.setDuration(mAnimationDuration);
    172 //        animator.addListener(new AnimatorListenerAdapter() {
    173 //            @Override
    174 //            public void onAnimationEnd(Animator animation) {
    175 //                mContentView.removeView(tab);
    176 //            }
    177 //        });
    178 //        animator.setInterpolator(new AccelerateInterpolator());
    179 //        animator.start();
    180 //    }
    181 
    182     private void animateScroll(int newscroll) {
    183         ObjectAnimator animator = ObjectAnimator.ofInt(this, "scroll", getScrollX(), newscroll);
    184         animator.setDuration(mAnimationDuration);
    185         animator.start();
    186     }
    187 
    188     /**
    189      * required for animation
    190      */
    191     public void setScroll(int newscroll) {
    192         scrollTo(newscroll, getScrollY());
    193     }
    194 
    195     /**
    196      * required for animation
    197      */
    198     public int getScroll() {
    199         return getScrollX();
    200     }
    201 
    202     @Override
    203     protected void onScrollChanged(int l, int t, int oldl, int oldt) {
    204         super.onScrollChanged(l, t, oldl, oldt);
    205 
    206         // TabViews base their drawing based on their absolute position within the
    207         // window. When hardware accelerated, we need to recreate their display list
    208         // when they scroll
    209         if (isHardwareAccelerated()) {
    210             int count = mContentView.getChildCount();
    211             for (int i = 0; i < count; i++) {
    212                 mContentView.getChildAt(i).invalidate();
    213             }
    214         }
    215     }
    216 
    217     class TabLayout extends LinearLayout {
    218 
    219         public TabLayout(Context context) {
    220             super(context);
    221             setChildrenDrawingOrderEnabled(true);
    222         }
    223 
    224         @Override
    225         protected void onMeasure(int hspec, int vspec) {
    226             super.onMeasure(hspec, vspec);
    227             int w = getMeasuredWidth();
    228             w -= Math.max(0, mContentView.getChildCount() - 1) * mTabOverlap;
    229             setMeasuredDimension(w, getMeasuredHeight());
    230         }
    231 
    232         @Override
    233         protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    234             super.onLayout(changed, left, top, right, bottom);
    235             if (getChildCount() > 1) {
    236                 int nextLeft = getChildAt(0).getRight() - mTabOverlap;
    237                 for (int i = 1; i < getChildCount(); i++) {
    238                     View tab = getChildAt(i);
    239                     int w = tab.getRight() - tab.getLeft();
    240                     tab.layout(nextLeft, tab.getTop(), nextLeft + w, tab.getBottom());
    241                     nextLeft += w - mTabOverlap;
    242                 }
    243             }
    244         }
    245 
    246         @Override
    247         protected int getChildDrawingOrder(int count, int i) {
    248             int next = -1;
    249             if ((i == (count - 1)) && (mSelected >= 0) && (mSelected < count)) {
    250                 next = mSelected;
    251             } else {
    252                 next = count - i - 1;
    253                 if (next <= mSelected && next > 0) {
    254                     next--;
    255                 }
    256             }
    257             return next;
    258         }
    259 
    260     }
    261 
    262 }
    263