1a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb/* 2a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb * Copyright (C) 2011 The Android Open Source Project 3a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb * 4a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb * use this file except in compliance with the License. You may obtain a copy of 6a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb * the License at 7a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb * 8a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb * http://www.apache.org/licenses/LICENSE-2.0 9a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb * 10a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb * Unless required by applicable law or agreed to in writing, software 11a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb * License for the specific language governing permissions and limitations under 14a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb * the License. 15a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb */ 16a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 17a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolbpackage com.android.browser; 18a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 19a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 20a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolbimport android.animation.Animator; 21a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolbimport android.animation.AnimatorListenerAdapter; 22a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolbimport android.animation.AnimatorSet; 23a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolbimport android.animation.ObjectAnimator; 24a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolbimport android.content.Context; 25a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolbimport android.database.DataSetObserver; 26a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolbimport android.graphics.Canvas; 27a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolbimport android.util.AttributeSet; 28a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolbimport android.view.Gravity; 29a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolbimport android.view.View; 30a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolbimport android.view.ViewGroup; 31a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolbimport android.view.animation.DecelerateInterpolator; 32a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolbimport android.widget.BaseAdapter; 33a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolbimport android.widget.LinearLayout; 34a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 35a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolbimport com.android.browser.view.ScrollerView; 36a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 37a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb/** 38a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb * custom view for displaying tabs in the nav screen 39a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb */ 40a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolbpublic class NavTabScroller extends ScrollerView { 41a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 42a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb static final int INVALID_POSITION = -1; 43a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb static final float[] PULL_FACTOR = { 2.5f, 0.9f }; 44a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 45a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb interface OnRemoveListener { 46a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb public void onRemovePosition(int position); 47a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 48a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 49a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb interface OnLayoutListener { 50a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb public void onLayout(int l, int t, int r, int b); 51a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 52a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 53a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb private ContentLayout mContentView; 54a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb private BaseAdapter mAdapter; 55a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb private OnRemoveListener mRemoveListener; 56a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb private OnLayoutListener mLayoutListener; 57a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb private int mGap; 58a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb private int mGapPosition; 59a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb private ObjectAnimator mGapAnimator; 60a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 61a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb // after drag animation velocity in pixels/sec 62a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb private static final float MIN_VELOCITY = 1500; 631a8f3c45241eaa047116f33e08bcb77b4924daf3Michael Kolb private AnimatorSet mAnimator; 64a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 65a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb private float mFlingVelocity; 66a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb private boolean mNeedsScroll; 67a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb private int mScrollPosition; 68a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 69a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb DecelerateInterpolator mCubic; 70a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb int mPullValue; 71a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 72a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb public NavTabScroller(Context context, AttributeSet attrs, int defStyle) { 73a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb super(context, attrs, defStyle); 74a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb init(context); 75a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 76a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 77a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb public NavTabScroller(Context context, AttributeSet attrs) { 78a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb super(context, attrs); 79a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb init(context); 80a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 81a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 82a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb public NavTabScroller(Context context) { 83a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb super(context); 84a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb init(context); 85a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 86a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 87a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb private void init(Context ctx) { 88a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mCubic = new DecelerateInterpolator(1.5f); 89a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mGapPosition = INVALID_POSITION; 90a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb setHorizontalScrollBarEnabled(false); 91a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb setVerticalScrollBarEnabled(false); 92a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mContentView = new ContentLayout(ctx, this); 93a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mContentView.setOrientation(LinearLayout.HORIZONTAL); 94a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb addView(mContentView); 95a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mContentView.setLayoutParams( 96a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT)); 97a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb // ProGuard ! 98a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb setGap(getGap()); 99a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mFlingVelocity = getContext().getResources().getDisplayMetrics().density 100a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb * MIN_VELOCITY; 101a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 102a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 103a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb protected int getScrollValue() { 104a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb return mHorizontal ? mScrollX : mScrollY; 105a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 106a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 107a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb protected void setScrollValue(int value) { 108a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb scrollTo(mHorizontal ? value : 0, mHorizontal ? 0 : value); 109a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 110a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 111a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb protected NavTabView getTabView(int pos) { 112a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb return (NavTabView) mContentView.getChildAt(pos); 113a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 114a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 115a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb protected boolean isHorizontal() { 116a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb return mHorizontal; 117a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 118a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 119a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb public void setOrientation(int orientation) { 120a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mContentView.setOrientation(orientation); 121a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb if (orientation == LinearLayout.HORIZONTAL) { 122a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mContentView.setLayoutParams( 123a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT)); 124a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } else { 125a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mContentView.setLayoutParams( 126a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); 127a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 128a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb super.setOrientation(orientation); 129a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 130a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 131a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb @Override 132a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb protected void onMeasure(int wspec, int hspec) { 133a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb super.onMeasure(wspec, hspec); 134a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb calcPadding(); 135a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 136a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 137a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb private void calcPadding() { 138a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb if (mAdapter.getCount() > 0) { 139a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb View v = mContentView.getChildAt(0); 140a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb if (mHorizontal) { 141a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb int pad = (getMeasuredWidth() - v.getMeasuredWidth()) / 2 + 2; 142a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mContentView.setPadding(pad, 0, pad, 0); 143a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } else { 144a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb int pad = (getMeasuredHeight() - v.getMeasuredHeight()) / 2 + 2; 145a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mContentView.setPadding(0, pad, 0, pad); 146a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 147a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 148a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 149a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 150a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb public void setAdapter(BaseAdapter adapter) { 151a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb setAdapter(adapter, 0); 152a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 153a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 154a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 155a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb public void setOnRemoveListener(OnRemoveListener l) { 156a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mRemoveListener = l; 157a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 158a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 159a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb public void setOnLayoutListener(OnLayoutListener l) { 160a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mLayoutListener = l; 161a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 162a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 163a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb protected void setAdapter(BaseAdapter adapter, int selection) { 164a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mAdapter = adapter; 165a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mAdapter.registerDataSetObserver(new DataSetObserver() { 166a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 167a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb @Override 168a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb public void onChanged() { 169a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb super.onChanged(); 170a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb handleDataChanged(); 171a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 172a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 173a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb @Override 174a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb public void onInvalidated() { 175a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb super.onInvalidated(); 176a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 177a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb }); 178a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb handleDataChanged(selection); 179a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 180a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 181a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb protected ViewGroup getContentView() { 182a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb return mContentView; 183a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 184a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 185a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb protected int getRelativeChildTop(int ix) { 186a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb return mContentView.getChildAt(ix).getTop() - mScrollY; 187a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 188a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 189a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb protected void handleDataChanged() { 190a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb handleDataChanged(INVALID_POSITION); 191a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 192a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 1931adf0307932f3933be792e631612ea1a2fd4e6e2John Reck void handleDataChanged(int newscroll) { 194a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb int scroll = getScrollValue(); 195a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb if (mGapAnimator != null) { 196a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mGapAnimator.cancel(); 197a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 198a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mContentView.removeAllViews(); 199a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb for (int i = 0; i < mAdapter.getCount(); i++) { 200a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb View v = mAdapter.getView(i, null, mContentView); 201a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( 202a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 203a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb lp.gravity = (mHorizontal ? Gravity.CENTER_VERTICAL : Gravity.CENTER_HORIZONTAL); 204a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mContentView.addView(v, lp); 2050e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb if (mGapPosition > INVALID_POSITION){ 2060e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb adjustViewGap(v, i); 207a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 208a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 209a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb if (newscroll > INVALID_POSITION) { 210a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb newscroll = Math.min(mAdapter.getCount() - 1, newscroll); 211a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mNeedsScroll = true; 212a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mScrollPosition = newscroll; 213a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb requestLayout(); 214a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } else { 215a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb setScrollValue(scroll); 216a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 217a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 218a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 219a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb protected void finishScroller() { 220a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mScroller.forceFinished(true); 221a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 222a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 223a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb @Override 224a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb protected void onLayout(boolean changed, int l, int t, int r, int b) { 225a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb super.onLayout(changed, l, t, r, b); 226a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb if (mNeedsScroll) { 227a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mScroller.forceFinished(true); 228a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb snapToSelected(mScrollPosition, false); 229a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mNeedsScroll = false; 230a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 231a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb if (mLayoutListener != null) { 232a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mLayoutListener.onLayout(l, t, r, b); 233a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mLayoutListener = null; 234a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 235a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 236a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 237a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb void clearTabs() { 238a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mContentView.removeAllViews(); 239a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 240a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 241a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb void snapToSelected(int pos, boolean smooth) { 242a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb if (pos < 0) return; 243a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb View v = mContentView.getChildAt(pos); 2445685ce6b4ebd5fb8a1c70390c0719f33d0c8e44dMichael Kolb if (v == null) return; 245a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb int sx = 0; 246a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb int sy = 0; 247a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb if (mHorizontal) { 248a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb sx = (v.getLeft() + v.getRight() - getWidth()) / 2; 249a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } else { 250a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb sy = (v.getTop() + v.getBottom() - getHeight()) / 2; 251a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 252a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb if ((sx != mScrollX) || (sy != mScrollY)) { 253a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb if (smooth) { 254a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb smoothScrollTo(sx,sy); 255a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } else { 256a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb scrollTo(sx, sy); 257a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 258a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 259a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 260a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 261a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb protected void animateOut(View v) { 262a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb if (v == null) return; 263a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb animateOut(v, -mFlingVelocity); 264a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 265a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 266a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb private void animateOut(final View v, float velocity) { 267a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb float start = mHorizontal ? v.getTranslationY() : v.getTranslationX(); 268a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb animateOut(v, velocity, start); 269a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 270a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 271a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb private void animateOut(final View v, float velocity, float start) { 272a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb if ((v == null) || (mAnimator != null)) return; 273a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb final int position = mContentView.indexOfChild(v); 274a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb int target = 0; 275a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb if (velocity < 0) { 276a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb target = mHorizontal ? -getHeight() : -getWidth(); 277a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } else { 278a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb target = mHorizontal ? getHeight() : getWidth(); 279a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 280a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb int distance = target - (mHorizontal ? v.getTop() : v.getLeft()); 281a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb long duration = (long) (Math.abs(distance) * 1000 / Math.abs(velocity)); 2820e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb int scroll = 0; 2830e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb int translate = 0; 2840e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb int gap = mHorizontal ? v.getWidth() : v.getHeight(); 2850e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb int centerView = getViewCenter(v); 2860e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb int centerScreen = getScreenCenter(); 2870e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb int newpos = INVALID_POSITION; 2880e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb if (centerView < centerScreen - gap / 2) { 2890e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb // top view 2900e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb scroll = - (centerScreen - centerView - gap); 2910e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb translate = (position > 0) ? gap : 0; 2920e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb newpos = position; 2930e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb } else if (centerView > centerScreen + gap / 2) { 2940e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb // bottom view 2950e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb scroll = - (centerScreen + gap - centerView); 2960e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb if (position < mAdapter.getCount() - 1) { 2970e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb translate = -gap; 2980e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb } 2990e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb } else { 3000e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb // center view 3010e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb scroll = - (centerScreen - centerView); 3020e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb if (position < mAdapter.getCount() - 1) { 3030e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb translate = -gap; 3040e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb } else { 3050e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb scroll -= gap; 3060e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb } 3070e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb } 3080e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb mGapPosition = position; 3090e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb final int pos = newpos; 3100e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb ObjectAnimator trans = ObjectAnimator.ofFloat(v, 3110e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb (mHorizontal ? TRANSLATION_Y : TRANSLATION_X), start, target); 3121a8f3c45241eaa047116f33e08bcb77b4924daf3Michael Kolb ObjectAnimator alpha = ObjectAnimator.ofFloat(v, ALPHA, getAlpha(v,start), 3131a8f3c45241eaa047116f33e08bcb77b4924daf3Michael Kolb getAlpha(v,target)); 3140e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb AnimatorSet set1 = new AnimatorSet(); 3150e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb set1.playTogether(trans, alpha); 3160e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb set1.setDuration(duration); 3170e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb mAnimator = new AnimatorSet(); 3180e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb ObjectAnimator trans2 = null; 3190e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb ObjectAnimator scroll1 = null; 3200e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb if (scroll != 0) { 3210e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb if (mHorizontal) { 3220e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb scroll1 = ObjectAnimator.ofInt(this, "scrollX", getScrollX(), getScrollX() + scroll); 3230e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb } else { 3240e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb scroll1 = ObjectAnimator.ofInt(this, "scrollY", getScrollY(), getScrollY() + scroll); 3250e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb } 3260e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb } 3270e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb if (translate != 0) { 3280e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb trans2 = ObjectAnimator.ofInt(this, "gap", 0, translate); 3290e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb } 3300e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb final int duration2 = 200; 3310e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb if (scroll1 != null) { 3320e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb if (trans2 != null) { 3330e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb AnimatorSet set2 = new AnimatorSet(); 3340e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb set2.playTogether(scroll1, trans2); 3350e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb set2.setDuration(duration2); 3360e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb mAnimator.playSequentially(set1, set2); 3370e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb } else { 3380e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb scroll1.setDuration(duration2); 3390e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb mAnimator.playSequentially(set1, scroll1); 3400e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb } 341a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } else { 3420e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb if (trans2 != null) { 3430e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb trans2.setDuration(duration2); 3440e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb mAnimator.playSequentially(set1, trans2); 3450e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb } 346a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 347a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mAnimator.addListener(new AnimatorListenerAdapter() { 348a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb public void onAnimationEnd(Animator a) { 349a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb if (mRemoveListener != null) { 350a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mRemoveListener.onRemovePosition(position); 351a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mAnimator = null; 3520e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb mGapPosition = INVALID_POSITION; 3530e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb mGap = 0; 3540e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb handleDataChanged(pos); 355a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 356a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 357a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb }); 358a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mAnimator.start(); 359a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 360a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 3610e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb public void setGap(int gap) { 3620e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb if (mGapPosition != INVALID_POSITION) { 3630e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb mGap = gap; 3640e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb postInvalidate(); 3650e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb } 3660e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb } 3670e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb 3680e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb public int getGap() { 3690e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb return mGap; 3700e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb } 3710e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb 3720e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb void adjustGap() { 3730e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb for (int i = 0; i < mContentView.getChildCount(); i++) { 3740e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb final View child = mContentView.getChildAt(i); 3750e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb adjustViewGap(child, i); 3760e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb } 3770e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb } 3780e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb 3790e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb private void adjustViewGap(View view, int pos) { 3800e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb if ((mGap < 0 && pos > mGapPosition) 3810e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb || (mGap > 0 && pos < mGapPosition)) { 3820e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb if (mHorizontal) { 3830e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb view.setTranslationX(mGap); 3840e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb } else { 3850e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb view.setTranslationY(mGap); 3860e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb } 3870e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb } 3880e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb } 3890e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb 3900e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb private int getViewCenter(View v) { 3910e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb if (mHorizontal) { 3920e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb return v.getLeft() + v.getWidth() / 2; 3930e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb } else { 3940e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb return v.getTop() + v.getHeight() / 2; 3950e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb } 3960e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb } 3970e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb 3980e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb private int getScreenCenter() { 3990e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb if (mHorizontal) { 4000e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb return getScrollX() + getWidth() / 2; 4010e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb } else { 4020e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb return getScrollY() + getHeight() / 2; 4030e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb } 4040e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb } 4050e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb 406a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb @Override 407a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb public void draw(Canvas canvas) { 408a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb if (mGapPosition > INVALID_POSITION) { 409a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb adjustGap(); 410a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 4110e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb super.draw(canvas); 412a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 413a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 414a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb @Override 415a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb protected View findViewAt(int x, int y) { 416a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb x += mScrollX; 417a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb y += mScrollY; 418a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb final int count = mContentView.getChildCount(); 419a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb for (int i = count - 1; i >= 0; i--) { 420a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb View child = mContentView.getChildAt(i); 421a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb if (child.getVisibility() == View.VISIBLE) { 422a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb if ((x >= child.getLeft()) && (x < child.getRight()) 423a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb && (y >= child.getTop()) && (y < child.getBottom())) { 424a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb return child; 425a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 426a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 427a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 428a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb return null; 429a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 430a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 431a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb @Override 432a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb protected void onOrthoDrag(View v, float distance) { 433a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb if ((v != null) && (mAnimator == null)) { 434a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb offsetView(v, distance); 435a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 436a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 437a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 438a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb @Override 439a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb protected void onOrthoDragFinished(View downView) { 440a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb if (mAnimator != null) return; 441a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb if (mIsOrthoDragged && downView != null) { 442a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb // offset 443a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb float diff = mHorizontal ? downView.getTranslationY() : downView.getTranslationX(); 444a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb if (Math.abs(diff) > (mHorizontal ? downView.getHeight() : downView.getWidth()) / 2) { 445a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb // remove it 446a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb animateOut(downView, Math.signum(diff) * mFlingVelocity, diff); 447a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } else { 448a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb // snap back 449a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb offsetView(downView, 0); 450a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 451a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 452a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 453a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 454a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb @Override 455a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb protected void onOrthoFling(View v, float velocity) { 456a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb if (v == null) return; 457a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb if (mAnimator == null && Math.abs(velocity) > mFlingVelocity / 2) { 458a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb animateOut(v, velocity); 459a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } else { 460a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb offsetView(v, 0); 461a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 462a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 463a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 464a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb private void offsetView(View v, float distance) { 4651a8f3c45241eaa047116f33e08bcb77b4924daf3Michael Kolb v.setAlpha(getAlpha(v, distance)); 466a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb if (mHorizontal) { 467a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb v.setTranslationY(distance); 468a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } else { 469a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb v.setTranslationX(distance); 470a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 471a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 472a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 4731a8f3c45241eaa047116f33e08bcb77b4924daf3Michael Kolb private float getAlpha(View v, float distance) { 4741a8f3c45241eaa047116f33e08bcb77b4924daf3Michael Kolb return 1 - (float) Math.abs(distance) / (mHorizontal ? v.getHeight() : v.getWidth()); 4751a8f3c45241eaa047116f33e08bcb77b4924daf3Michael Kolb } 4761a8f3c45241eaa047116f33e08bcb77b4924daf3Michael Kolb 4771a8f3c45241eaa047116f33e08bcb77b4924daf3Michael Kolb private float ease(DecelerateInterpolator inter, float value, float start, 4781a8f3c45241eaa047116f33e08bcb77b4924daf3Michael Kolb float dist, float duration) { 479a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb return start + dist * inter.getInterpolation(value / duration); 480a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 481a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 482a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb @Override 483a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb protected void onPull(int delta) { 484a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb boolean layer = false; 485a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb int count = 2; 486a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb if (delta == 0 && mPullValue == 0) return; 487a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb if (delta == 0 && mPullValue != 0) { 488a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb // reset 489a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb for (int i = 0; i < count; i++) { 490a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb View child = mContentView.getChildAt((mPullValue < 0) 491a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb ? i 492a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb : mContentView.getChildCount() - 1 - i); 493a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb if (child == null) break; 494a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb ObjectAnimator trans = ObjectAnimator.ofFloat(child, 495a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mHorizontal ? "translationX" : "translationY", 496a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mHorizontal ? getTranslationX() : getTranslationY(), 497a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 0); 498a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb ObjectAnimator rot = ObjectAnimator.ofFloat(child, 499a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mHorizontal ? "rotationY" : "rotationX", 500a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mHorizontal ? getRotationY() : getRotationX(), 501a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 0); 502a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb AnimatorSet set = new AnimatorSet(); 503a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb set.playTogether(trans, rot); 504a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb set.setDuration(100); 505a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb set.start(); 506a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 507a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mPullValue = 0; 508a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } else { 509a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb if (mPullValue == 0) { 510a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb layer = true; 511a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 512a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mPullValue += delta; 513a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 514a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb final int height = mHorizontal ? getWidth() : getHeight(); 515a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb int oscroll = Math.abs(mPullValue); 516a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb int factor = (mPullValue <= 0) ? 1 : -1; 517a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb for (int i = 0; i < count; i++) { 518a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb View child = mContentView.getChildAt((mPullValue < 0) 519a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb ? i 520a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb : mContentView.getChildCount() - 1 - i); 521a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb if (child == null) break; 522a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb if (layer) { 523a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 524a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb float k = PULL_FACTOR[i]; 525a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb float rot = -factor * ease(mCubic, oscroll, 0, k * 2, height); 526a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb int y = factor * (int) ease(mCubic, oscroll, 0, k*20, height); 527a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb if (mHorizontal) { 528a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb child.setTranslationX(y); 529a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } else { 530a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb child.setTranslationY(y); 531a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 532a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb if (mHorizontal) { 533a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb child.setRotationY(-rot); 534a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } else { 535a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb child.setRotationX(rot); 536a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 537a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 538a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 539a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 540a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb static class ContentLayout extends LinearLayout { 541a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 542a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb NavTabScroller mScroller; 543a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 544a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb public ContentLayout(Context context, NavTabScroller scroller) { 545a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb super(context); 546a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb mScroller = scroller; 547a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 548a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 549a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb @Override 550a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 551a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb super.onMeasure(widthMeasureSpec, heightMeasureSpec); 5520e3bff8ba81811c766068807d47ee65565aa7731Michael Kolb if (mScroller.getGap() != 0) { 553a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb View v = getChildAt(0); 554a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb if (v != null) { 555a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb if (mScroller.isHorizontal()) { 556a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb int total = v.getMeasuredWidth() + getMeasuredWidth(); 557a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb setMeasuredDimension(total, getMeasuredHeight()); 558a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } else { 559a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb int total = v.getMeasuredHeight() + getMeasuredHeight(); 560a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb setMeasuredDimension(getMeasuredWidth(), total); 561a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 562a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 563a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 564a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 565a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 566a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 567a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb } 568a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb 569a3194d0b9c9c36be29598cac8faf8453cdaebe55Michael Kolb}