diff options
author | Mario Bertschler <bmario@google.com> | 2017-12-06 11:45:49 -0800 |
---|---|---|
committer | Mario Bertschler <bmario@google.com> | 2017-12-06 11:45:49 -0800 |
commit | 3d87ed17b36a382cbff447ed103b0771e0692ffb (patch) | |
tree | 61b1b529e9be0fd9827254e749cc7265ce7d0c0d /src/com/android/launcher3/allapps/FloatingHeaderView.java | |
parent | adaeeee690b8ecd5e77ee59f1591ab4f926c85b9 (diff) | |
download | android_packages_apps_Trebuchet-3d87ed17b36a382cbff447ed103b0771e0692ffb.tar.gz android_packages_apps_Trebuchet-3d87ed17b36a382cbff447ed103b0771e0692ffb.tar.bz2 android_packages_apps_Trebuchet-3d87ed17b36a382cbff447ed103b0771e0692ffb.zip |
FloatingHeaderHandler refactored into custom view FloatingHeaderView.
No logical change.
Change-Id: I9a6dadb151872abe9915e96833af530ca08ddb8e
Diffstat (limited to 'src/com/android/launcher3/allapps/FloatingHeaderView.java')
-rw-r--r-- | src/com/android/launcher3/allapps/FloatingHeaderView.java | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java new file mode 100644 index 000000000..39e681886 --- /dev/null +++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.allapps; + + +import android.animation.ValueAnimator; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Rect; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.widget.RecyclerView; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.RelativeLayout; + +import com.android.launcher3.R; + +public class FloatingHeaderView extends RelativeLayout implements + ValueAnimator.AnimatorUpdateListener { + + private static final boolean SHOW_PREDICTIONS_ONLY_ON_TOP = true; + + private final Rect mClip = new Rect(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE); + private final ValueAnimator mAnimator = ValueAnimator.ofInt(0, 0); + private final RecyclerView.OnScrollListener mOnScrollListener = new RecyclerView.OnScrollListener() { + @Override + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + if (SHOW_PREDICTIONS_ONLY_ON_TOP) { + return; + } + if (!mTopOnlyMode && newState == RecyclerView.SCROLL_STATE_IDLE + && mTranslationY != -mMaxTranslation && mTranslationY != 0) { + float scroll = Math.abs(getCurrentScroll()); + boolean expand = scroll > mMaxTranslation + ? Math.abs(mTranslationY) < mMaxTranslation / 2 : true; + setExpanded(expand); + } + } + + @Override + public void onScrolled(RecyclerView rv, int dx, int dy) { + boolean isMainRV = rv == mMainRV; + if (isMainRV != mMainRVActive) { + return; + } + + if (mAnimator.isStarted()) { + mAnimator.cancel(); + } + + int current = - (isMainRV + ? mMainRV.getCurrentScrollY() + : mWorkRV.getCurrentScrollY()); + moved(current); + apply(); + } + }; + + private PredictionRowView mPredictionRow; + private ViewGroup mTabLayout; + private View mDivider; + private AllAppsRecyclerView mMainRV; + private AllAppsRecyclerView mWorkRV; + private boolean mTopOnlyMode; + private boolean mHeaderHidden; + private int mMaxTranslation; + private int mSnappedScrolledY; + private int mTranslationY; + private int mMainScrolledY; + private int mWorkScrolledY; + private boolean mMainRVActive; + + public FloatingHeaderView(@NonNull Context context) { + this(context, null); + } + + public FloatingHeaderView(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mTabLayout = findViewById(R.id.tabs); + mDivider = findViewById(R.id.divider); + mPredictionRow = findViewById(R.id.header_content); + } + + public void setup(@NonNull AllAppsRecyclerView personalRV, @Nullable AllAppsRecyclerView workRV, + int predictionRowHeight) { + mTopOnlyMode = workRV == null; + mTabLayout.setVisibility(mTopOnlyMode ? View.GONE : View.VISIBLE); + mPredictionRow.getLayoutParams().height = predictionRowHeight; + mMaxTranslation = predictionRowHeight; + mMainRV = setupRV(mMainRV, personalRV); + mWorkRV = setupRV(mWorkRV, workRV); + setMainActive(true); + setupDivider(); + } + + private AllAppsRecyclerView setupRV(AllAppsRecyclerView old, AllAppsRecyclerView updated) { + if (old != updated && updated != null ) { + updated.addOnScrollListener(mOnScrollListener); + } + return updated; + } + + private void setupDivider() { + Resources res = getResources(); + int verticalGap = res.getDimensionPixelSize(R.dimen.all_apps_divider_margin_vertical); + int sideGap = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin); + mDivider.setPadding(sideGap, verticalGap,sideGap, mTopOnlyMode ? verticalGap : 0); + RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) mDivider.getLayoutParams(); + lp.removeRule(RelativeLayout.ALIGN_BOTTOM); + lp.addRule(RelativeLayout.ALIGN_BOTTOM, mTopOnlyMode ? R.id.header_content : R.id.tabs); + mDivider.setLayoutParams(lp); + } + + public void setMainActive(boolean active) { + mMainRVActive = active; + mSnappedScrolledY = getCurrentScroll() - mMaxTranslation; + setExpanded(true); + } + + public PredictionRowView getPredictionRow() { + return mPredictionRow; + } + + public ViewGroup getTabLayout() { + return mTabLayout; + } + + public View getDivider() { + return mDivider; + } + + public void reset() { + mMainScrolledY = 0; + mWorkScrolledY = 0; + setExpanded(true); + } + + private boolean canSnapAt(int currentScrollY) { + boolean snapOnlyOnTop = SHOW_PREDICTIONS_ONLY_ON_TOP || mTopOnlyMode; + return !snapOnlyOnTop || Math.abs(currentScrollY) <= mPredictionRow.getHeight(); + } + + private void moved(final int currentScrollY) { + if (mHeaderHidden) { + if (currentScrollY <= mSnappedScrolledY) { + if (canSnapAt(currentScrollY)) { + mSnappedScrolledY = currentScrollY; + } + } else { + mHeaderHidden = false; + } + mTranslationY = currentScrollY; + } else if (!mHeaderHidden) { + mTranslationY = currentScrollY - mSnappedScrolledY - mMaxTranslation; + + // update state vars + if (mTranslationY >= 0) { // expanded: must not move down further + mTranslationY = 0; + mSnappedScrolledY = currentScrollY - mMaxTranslation; + } else if (mTranslationY <= -mMaxTranslation) { // hide or stay hidden + mHeaderHidden = true; + mSnappedScrolledY = currentScrollY; + } + } + } + + private void apply() { + int uncappedTranslationY = mTranslationY; + mTranslationY = Math.max(mTranslationY, -mMaxTranslation); + if (mTranslationY != uncappedTranslationY) { + // we hide it completely if already capped (for opening search anim) + mPredictionRow.setVisibility(View.INVISIBLE); + } else { + mPredictionRow.setVisibility(View.VISIBLE); + mPredictionRow.setTranslationY(uncappedTranslationY); + } + mTabLayout.setTranslationY(mTranslationY); + mDivider.setTranslationY(mTopOnlyMode ? uncappedTranslationY : mTranslationY); + mClip.top = mMaxTranslation + mTranslationY; + // clipping on a draw might cause additional redraw + mMainRV.setClipBounds(mClip); + if (mWorkRV != null) { + mWorkRV.setClipBounds(mClip); + } + } + + private void setExpanded(boolean expand) { + int translateTo = expand ? 0 : -mMaxTranslation; + mAnimator.setIntValues(mTranslationY, translateTo); + mAnimator.addUpdateListener(this); + mAnimator.setDuration(150); + mAnimator.start(); + mHeaderHidden = !expand; + mSnappedScrolledY = expand ? getCurrentScroll() - mMaxTranslation : getCurrentScroll(); + } + + public boolean isExpanded() { + return !mHeaderHidden; + } + + private int getCurrentScroll() { + return mMainRVActive ? mMainScrolledY : mWorkScrolledY; + } + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + mTranslationY = (Integer) animation.getAnimatedValue(); + apply(); + } + +} + + |