Скрытие панели действий в RecyclerView/ListView onScroll
В моем приложении я получил действие с какой-то панелью действий вверху и списком под ним. Что я хочу сделать - это прокрутить его вверх со списком, чтобы он скрывался, а затем, когда список прокручивается вниз, - он должен прокрутить список вниз, как если бы он находился чуть выше верхней границы экрана. Как я могу достичь этой функциональности?
7 ответов
Обновлено 03.06.2015:
Google теперь поддерживает это, используя CoordinatorLayout
,
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:layout_scrollFlags="scroll|enterAlways" />
<android.support.design.widget.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="@dimen/fab_margin"
android:src="@drawable/ic_done" />
</android.support.design.widget.CoordinatorLayout>
Документировано здесь: https://developer.android.com/reference/android/support/design/widget/AppBarLayout.html
Оригинальный ответ:
Пример, аналогичный приложениям Google Play Music и Umano:
https://github.com/umano/AndroidSlidingUpPanel
Посмотрите на код в этом хранилище. Когда вы двигаете панель вверх, панель действий также перемещается вверх.
Из демоверсии:
getWindow().requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
SlidingUpPanelLayout layout = (SlidingUpPanelLayout) findViewById(R.id.sliding_layout);
layout.setShadowDrawable(getResources().getDrawable(R.drawable.above_shadow));
layout.setAnchorPoint(0.3f);
layout.setPanelSlideListener(new PanelSlideListener() {
@Override
public void onPanelSlide(View panel, float slideOffset) {
Log.i(TAG, "onPanelSlide, offset " + slideOffset);
if (slideOffset < 0.2) {
if (getActionBar().isShowing()) {
getActionBar().hide();
}
} else {
if (!getActionBar().isShowing()) {
getActionBar().show();
}
}
}
@Override
public void onPanelExpanded(View panel) {
Log.i(TAG, "onPanelExpanded");
}
@Override
public void onPanelCollapsed(View panel) {
Log.i(TAG, "onPanelCollapsed");
}
@Override
public void onPanelAnchored(View panel) {
Log.i(TAG, "onPanelAnchored");
}
});
Скачать пример здесь:
https://play.google.com/store/apps/details?id=com.sothree.umano
ListView - без библиотек:
Я недавно хотел такую же функциональность, и это прекрасно работает для меня:
Когда пользователь прокручивает вверх, панель действий будет скрыта, чтобы предоставить пользователю весь экран для работы.
Когда пользователь прокручивает вниз и отпускает, ActionBar вернется.
getWindow().requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
listView.setOnScrollListener(new OnScrollListener() {
int mLastFirstVisibleItem = 0;
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) { }
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (view.getId() == listView.getId()) {
final int currentFirstVisibleItem = listView.getFirstVisiblePosition();
if (currentFirstVisibleItem > mLastFirstVisibleItem) {
// getSherlockActivity().getSupportActionBar().hide();
getSupportActionBar().hide();
} else if (currentFirstVisibleItem < mLastFirstVisibleItem) {
// getSherlockActivity().getSupportActionBar().show();
getSupportActionBar().show();
}
mLastFirstVisibleItem = currentFirstVisibleItem;
}
}
});
RecyclerView - без библиотек
this.mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
int mLastFirstVisibleItem = 0;
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
final int currentFirstVisibleItem = mLinearLayoutManager.findFirstVisibleItemPosition();
if (currentFirstVisibleItem > this.mLastFirstVisibleItem) {
MainActivity.this.getSupportActionBar().hide();
} else if (currentFirstVisibleItem < this.mLastFirstVisibleItem) {
MainActivity.this.getSupportActionBar().show();
}
this.mLastFirstVisibleItem = currentFirstVisibleItem;
}
});
Дайте мне знать, если вам нужна дополнительная помощь!
Вы испытываете мерцание, потому что, скрывая / показывая ActionBar, доступно пространство для изменений макета вашего контента, что вызывает ретрансляцию. При этом также изменяется индекс позиции первого видимого элемента (это можно проверить, выйдя из системы mLastFirstVisibleItem и currentFirstVisibleItem.
Вы можете справиться с мерцанием, позволяя ActionBar наложить ваш макет контента. Чтобы включить режим наложения для панели действий, вам нужно создать собственную тему, которая расширяет существующую тему панели действий и установить для свойства android: windowActionBarOverlay значение true.
При этом вы можете устранить мерцание, но панель действий будет перекрывать содержимое вашего списка. Простым решением этого является установка верхнего отступа списка (или корневого макета) на высоту панели действий.
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="?android:attr/actionBarSize" />
К сожалению, это приведет к постоянному заполнению сверху. Уточнение этого решения состоит в том, чтобы добавить представление заголовка к представлению списка, которое имеет высоту ? Android: attr / actionBarSize (и удалить ранее установленный верхний отступ)
То, что вы ищете, называется шаблоном быстрого возврата, применяемым к панели действий. Приложение Google IO 2014 использует именно это. Я использую его в одном из своих приложений, вы можете проверить исходный код этого приложения Google, чтобы увидеть, как они его получили. Класс BaseActivity - это то, где у вас есть то, что вам нужно, прочитайте код и извлеките только эту конкретную функциональность. Наслаждайтесь кодированием!:)
Я уверен, что это не лучшее решение. Но я еще не нашел лучшего. Надеюсь, это будет полезно.
static boolean scroll_down;
...
mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (scroll_down) {
actionBar.hide();
} else {
actionBar.show();
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (dy > 70) {
//scroll down
scroll_down = true;
} else if (dy < -5) {
//scroll up
scroll_down = false;
}
}
});
У меня есть мысль, что это хорошая идея.
listView.setOnScrollListener(new OnScrollListener() {
int mLastFirstVisibleItem = 0;
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
switch (scrollState) {
case OnScrollListener.SCROLL_STATE_FLING:
if (view.getId()==lv_searchresult_results.getId()){
final int currentFirstVisibleItem=lv_searchresult_results.getFirstVisiblePosition();
if(currentFirstVisibleItem>mLastFirstVisibleItem){
ObjectAnimator.ofFloat(toolbar, "translationY", -toolbar.getHeight()).start();
else if(currentFirstVisibleItem<(mLastFirstVisibleItem)){
ObjectAnimator.ofFloat(toolbar, "translationY", 0).start();
}
mLastFirstVisibleItem= currentFirstVisibleItem;
}
if(lv_searchresult_results.getLastVisiblePosition() == myAdapter.getListMap().size()){
if(myAdapter.getListMap().size() < allRows&&!upLoading){
}
}
break;
case OnScrollListener.SCROLL_STATE_IDLE:
if (view.getFirstVisiblePosition()<=1){
ObjectAnimator.ofFloat(toolbar, "translationY", 0).start();
}
if(lv_searchresult_results.getLastVisiblePosition() == myAdapter.getListMap().size()){
if(myAdapter.getListMap().size() < allRows&&!upLoading){
}
}
break;
Я думаю, что это будет работать нормально.....
listView.setOnScrollListener(new OnScrollListener() {
int mLastFirstVisibleItem = listView.getLastVisiblePosition();
boolean isScrolling = false;
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if(scrollState == 0)
isScrolling = false;
else if(scrollState == 1)
isScrolling = true;
else if(scrollState == 2)
isScrolling = true;
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (view.getId() == myTeamListView.getId()) {
if(isScrolling) {
final int currentFirstVisibleItem = myTeamListView.getLastVisiblePosition();
if (currentFirstVisibleItem > mLastFirstVisibleItem) {
((AppCompatActivity)getActivity()).getSupportActionBar().hide();
} else if (currentFirstVisibleItem < mLastFirstVisibleItem) {
((AppCompatActivity)getActivity()).getSupportActionBar().show();
}
mLastFirstVisibleItem = currentFirstVisibleItem;
}
}
}
});
Если вы используете CoordinatorLayout, вот хитрость.
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways" />
app:layout_scrollFlags="scroll|enterAlways"
Строка заставит нашу Панель инструментов прокручиваться за пределы экрана, когда пользователь прокручивает список вниз, и как только он начинает прокручивать вверх, Панель инструментов снова появится.