Hide or show search bar while recycler view scroll
This commit is contained in:
1 parent
894cf61d67
commit
00189740b2
5 files changed
+300
-15
No files matched your search
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
Reference in new issue
Block a user