diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.java index b0fb232c2..7d79a5497 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.java @@ -15,25 +15,34 @@ package com.hippo.ehviewer.ui.scene; +import android.animation.Animator; +import android.animation.ValueAnimator; import android.content.res.Resources; import android.os.Bundle; import android.support.annotation.Nullable; +import android.support.v7.widget.RecyclerView; import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import com.hippo.animation.SimpleAnimatorListener; import com.hippo.effect.ViewTransition; import com.hippo.ehviewer.R; import com.hippo.ehviewer.ui.ContentActivity; import com.hippo.ehviewer.widget.ContentLayout; +import com.hippo.ehviewer.widget.OffsetLayout; import com.hippo.ehviewer.widget.SearchBar; import com.hippo.ehviewer.widget.SearchDatabase; import com.hippo.ehviewer.widget.SearchLayout; import com.hippo.scene.Scene; import com.hippo.util.Log; +import com.hippo.util.MathUtils; import com.hippo.util.ViewUtils; public class GalleryListScene extends Scene implements SearchBar.Helper { + private static final long ANIMATE_TIME = 300l; + private final static int STATE_NORMAL = 0; private final static int STATE_SIMPLE_SEARCH = 1; private final static int STATE_SEARCH = 2; @@ -45,10 +54,16 @@ class GalleryListScene extends Scene implements SearchBar.Helper { private SearchBar mSearchBar; private ContentLayout mContentLayout; + private RecyclerView mContentRecyclerView; private SearchLayout mSearchLayout; + private RecyclerView mSearchRecyclerView; private ViewTransition mViewTransition; + private int mSearchBarOriginalTop; + private int mSearchBarOriginalBottom; + private ValueAnimator mSearchBarMoveAnimator; + private int mState = STATE_NORMAL; @Override @@ -62,18 +77,28 @@ class GalleryListScene extends Scene implements SearchBar.Helper { mSearchBar = (SearchBar) findViewById(R.id.search_bar); mContentLayout = (ContentLayout) findViewById(R.id.content_layout); + mContentRecyclerView = mContentLayout.getRecyclerView(); mSearchLayout = (SearchLayout) findViewById(R.id.search_layout); + mSearchRecyclerView = mSearchLayout.getRecyclerView(); mViewTransition = new ViewTransition(mContentLayout, mSearchLayout); // Search Bar mSearchBar.setHelper(this); - ViewUtils.measureView(mSearchBar, ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT); - - // Search Layout - mSearchLayout.setFitPaddingTop(mSearchBar.getMeasuredHeight() + - mResources.getDimensionPixelOffset(R.dimen.search_bar_padding_vertical)); + ViewUtils.measureView(mSearchBar, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + SearchBarMoveHelper sbmHelper = new SearchBarMoveHelper(); + mContentRecyclerView.addOnScrollListener(sbmHelper); + mSearchRecyclerView.addOnScrollListener(sbmHelper); + mSearchBar.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + ViewUtils.removeOnGlobalLayoutListener(mSearchBar.getViewTreeObserver(), this); + mSearchBarOriginalTop = mSearchBar.getTop(); + mSearchBarOriginalBottom = mSearchBar.getBottom(); + mSearchLayout.setFitPaddingTop(mSearchBar.getHeight() + + mResources.getDimensionPixelOffset(R.dimen.search_bar_padding_vertical)); + } + }); // TEST mContentLayout.showText("四姑拉斯基"); @@ -91,17 +116,20 @@ class GalleryListScene extends Scene implements SearchBar.Helper { super.onBackPressed(); break; case STATE_SIMPLE_SEARCH: - mSearchBar.setInNormalMode(); mState = STATE_NORMAL; + mSearchBar.setInNormalMode(); + returnSearchBarPosition(); break; case STATE_SEARCH: + mState = STATE_NORMAL; mViewTransition.showFirstView(); mSearchBar.setInNormalMode(); - mState = STATE_NORMAL; + returnSearchBarPosition(); break; case STATE_SEARCH_SHOW_LIST: - mSearchBar.hideImeAndSuggestionsList(); mState = STATE_SEARCH; + mSearchBar.hideImeAndSuggestionsList(); + returnSearchBarPosition(); break; } } @@ -111,6 +139,7 @@ class GalleryListScene extends Scene implements SearchBar.Helper { if (mState == STATE_NORMAL) { mState = STATE_SIMPLE_SEARCH; mSearchBar.setInEditMode(true); + returnSearchBarPosition(); } } @@ -127,18 +156,20 @@ class GalleryListScene extends Scene implements SearchBar.Helper { @Override public void onClickAdvanceSearch() { if (mState == STATE_NORMAL) { + mState = STATE_SEARCH; mViewTransition.showSecondView(); mSearchLayout.scrollSearchContainerToTop(); mSearchBar.setInEditMode(false); - mState = STATE_SEARCH; + returnSearchBarPosition(); } } @Override public void onSearchEditTextClick() { if (mState == STATE_SEARCH) { - mSearchBar.showImeAndSuggestionsList(); mState = STATE_SEARCH_SHOW_LIST; + mSearchBar.showImeAndSuggestionsList(); + returnSearchBarPosition(); } } @@ -147,8 +178,95 @@ class GalleryListScene extends Scene implements SearchBar.Helper { Log.d("onApplySearch " + query); mSearchDatabase.addQuery(query); + mState = STATE_NORMAL; mViewTransition.showFirstView(); mSearchBar.setInNormalMode(); - mState = STATE_NORMAL; + returnSearchBarPosition(); + } + + private RecyclerView getVaildRecyclerView() { + if (mState == STATE_NORMAL || mState == STATE_SIMPLE_SEARCH) { + return mContentRecyclerView; + } else { + return mSearchRecyclerView; + } + } + + private void returnSearchBarPosition() { + boolean show; + if (mState == STATE_SIMPLE_SEARCH || mState == STATE_SEARCH_SHOW_LIST) { + show = true; + } else { + RecyclerView recyclerView = getVaildRecyclerView(); + if (!recyclerView.isShown()) { + show = true; + } else if (recyclerView.computeVerticalScrollOffset() < mSearchBarOriginalBottom){ + show = true; + } else if (mSearchBar.getBottom() > (mSearchBarOriginalBottom - mSearchBarOriginalTop) / 2) { + show = true; + } else { + show = false; + } + } + + int offset; + if (show) { + offset = mSearchBarOriginalTop - mSearchBar.getTop(); + } else { + offset = -mSearchBar.getBottom(); + } + + if (offset != 0) { + final ValueAnimator va = ValueAnimator.ofInt(0, offset); + va.setDuration(ANIMATE_TIME); + va.addListener(new SimpleAnimatorListener() { + @Override + public void onAnimationEnd(Animator animation) { + mSearchBarMoveAnimator = null; + } + }); + va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + int lastValue; + @Override + public void onAnimationUpdate(ValueAnimator animation) { + int value = (Integer) animation.getAnimatedValue(); + int offsetStep = value - lastValue; + lastValue = value; + mSearchBar.offsetTopAndBottom(offsetStep); + ((OffsetLayout.LayoutParams) mSearchBar.getLayoutParams()).offsetY += offsetStep; + } + }); + if (mSearchBarMoveAnimator != null) { + mSearchBarMoveAnimator.cancel(); + } + mSearchBarMoveAnimator = va; + va.start(); + } + } + + private class SearchBarMoveHelper extends RecyclerView.OnScrollListener { + + @Override + public void onScrollStateChanged(RecyclerView recyclerView, int newState){ + if (newState == RecyclerView.SCROLL_STATE_IDLE && isVaildView(recyclerView)) { + returnSearchBarPosition(); + } + } + + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + if (mState != STATE_SIMPLE_SEARCH && mState != STATE_SEARCH_SHOW_LIST && + isVaildView(recyclerView)) { + int offsetYStep = MathUtils.clamp(-dy, + -mSearchBar.getBottom(), mSearchBarOriginalTop - mSearchBar.getTop()); + mSearchBar.offsetTopAndBottom(offsetYStep); + ((OffsetLayout.LayoutParams) mSearchBar.getLayoutParams()).offsetY += offsetYStep; + } + } + + private boolean isVaildView(RecyclerView view) { + return (mState == STATE_NORMAL && view == mContentRecyclerView) || + (mState == STATE_SEARCH && view == mSearchRecyclerView); + } } } diff --git a/app/src/main/java/com/hippo/ehviewer/widget/ContentLayout.java b/app/src/main/java/com/hippo/ehviewer/widget/ContentLayout.java index a478d35f7..84beabea5 100644 --- a/app/src/main/java/com/hippo/ehviewer/widget/ContentLayout.java +++ b/app/src/main/java/com/hippo/ehviewer/widget/ContentLayout.java @@ -40,6 +40,7 @@ public class ContentLayout extends FrameLayout { private ProgressBar mProgressBar; private ViewGroup mItView; private RefreshLayout mRefreshLayout; + private EasyRecyclerView mRecyclerView; private View mImageView; private TextView mTextView; @@ -64,10 +65,15 @@ public class ContentLayout extends FrameLayout { mProgressBar = (ProgressBar) getChildAt(0); mItView = (ViewGroup) getChildAt(1); mRefreshLayout = (RefreshLayout) getChildAt(2); + mRecyclerView = (EasyRecyclerView) mRefreshLayout.getChildAt(1); mImageView = mItView.getChildAt(0); mTextView = (TextView) mItView.getChildAt(1); } + public EasyRecyclerView getRecyclerView() { + return mRecyclerView; + } + public void showProgressBar() { ViewUtils.setVisibility(mProgressBar, View.VISIBLE); ViewUtils.setVisibility(mItView, View.GONE); diff --git a/app/src/main/java/com/hippo/ehviewer/widget/OffsetLayout.java b/app/src/main/java/com/hippo/ehviewer/widget/OffsetLayout.java new file mode 100644 index 000000000..2f0e568e8 --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/widget/OffsetLayout.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2015 Hippo Seven + * + * 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 + * 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.hippo.ehviewer.widget; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +public class OffsetLayout extends FrameLayout { + + public OffsetLayout(Context context) { + super(context); + } + + public OffsetLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public OffsetLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + layoutChildrenEx(left, top, right, bottom, false /* no force left gravity */); + } + + // TODO foreground not work fine + @SuppressLint("RtlHardcoded") + void layoutChildrenEx(int left, int top, int right, int bottom, + boolean forceLeftGravity) { + final int count = getChildCount(); + + final int parentLeft = getPaddingLeft(); + final int parentRight = right - left - getPaddingRight(); + + final int parentTop = getPaddingTop(); + final int parentBottom = bottom - top - getPaddingBottom(); + + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + if (child.getVisibility() != GONE) { + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + + final int width = child.getMeasuredWidth(); + final int height = child.getMeasuredHeight(); + + int childLeft; + int childTop; + + int gravity = lp.gravity; + if (gravity == -1) { + gravity = Gravity.TOP | Gravity.LEFT; + } + + final int absoluteGravity = gravity; + final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK; + + switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { + case Gravity.CENTER_HORIZONTAL: + childLeft = parentLeft + (parentRight - parentLeft - width) / 2 + + lp.leftMargin - lp.rightMargin; + break; + case Gravity.RIGHT: + if (!forceLeftGravity) { + childLeft = parentRight - width - lp.rightMargin; + break; + } + case Gravity.LEFT: + default: + childLeft = parentLeft + lp.leftMargin; + } + + switch (verticalGravity) { + case Gravity.TOP: + childTop = parentTop + lp.topMargin; + break; + case Gravity.CENTER_VERTICAL: + childTop = parentTop + (parentBottom - parentTop - height) / 2 + + lp.topMargin - lp.bottomMargin; + break; + case Gravity.BOTTOM: + childTop = parentBottom - height - lp.bottomMargin; + break; + default: + childTop = parentTop + lp.topMargin; + } + + child.layout(childLeft + lp.offsetX, childTop + lp.offsetY, + childLeft + width + lp.offsetX, childTop + height + lp.offsetY); + } + } + } + + @Override + public LayoutParams generateLayoutParams(AttributeSet attrs) { + return new LayoutParams(getContext(), attrs); + } + + @Override + protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { + return p instanceof LayoutParams; + } + + @Override + protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { + return new LayoutParams(p); + } + + public static class LayoutParams extends FrameLayout.LayoutParams { + + public int offsetX; + public int offsetY; + + public LayoutParams(Context c, AttributeSet attrs) { + super(c, attrs); + } + + public LayoutParams(ViewGroup.LayoutParams source) { + super(source); + } + + public LayoutParams(MarginLayoutParams source) { + super(source); + } + + public LayoutParams(FrameLayout.LayoutParams source) { + super((ViewGroup.MarginLayoutParams) source); + this.gravity = source.gravity; + } + + public LayoutParams(int width, int height) { + super(width, height); + } + } +} diff --git a/app/src/main/java/com/hippo/ehviewer/widget/SearchLayout.java b/app/src/main/java/com/hippo/ehviewer/widget/SearchLayout.java index 16571b787..9489dbe17 100644 --- a/app/src/main/java/com/hippo/ehviewer/widget/SearchLayout.java +++ b/app/src/main/java/com/hippo/ehviewer/widget/SearchLayout.java @@ -159,8 +159,16 @@ public class SearchLayout extends FrameLayout implements CompoundButton.OnChecke mSearchPaddingBottomOrigin); } + public EasyRecyclerView getRecyclerView() { + return mSearchContainer; + } + public void setFitPaddingTop(int fitPaddingTop) { - mSearchContainer.setPadding(mSearchContainer.getPaddingLeft(), mSearchPaddingTopOrigin + fitPaddingTop, mSearchContainer.getPaddingRight(), mSearchContainer.getPaddingBottom()); + mSearchContainer.setPadding( + mSearchContainer.getPaddingLeft(), + mSearchPaddingTopOrigin + fitPaddingTop, + mSearchContainer.getPaddingRight(), + mSearchContainer.getPaddingBottom()); } public void setFitPaddingBottom(int fitPaddingBottom) { diff --git a/app/src/main/res/layout/scene_gallery_list.xml b/app/src/main/res/layout/scene_gallery_list.xml index d7adb9a59..579a6d608 100644 --- a/app/src/main/res/layout/scene_gallery_list.xml +++ b/app/src/main/res/layout/scene_gallery_list.xml @@ -13,7 +13,7 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - @@ -39,5 +39,5 @@ android:layout_marginTop="@dimen/search_bar_padding_vertical" auto:forceNoElevation="true"/> - +