2017-02-22 3 views
6

Я сделал RecyclerView внутри NavigationView с пользовательским первым элементом в качестве эквивалента ListView's addHeaderView(). В нижней части этого макета заголовка я разместил EditText, чтобы я мог отфильтровать список ниже.Как заставить RecyclerView прокручивать, когда не хватает элементов, чтобы заполнить высоту sceen

Что я хотел сделать, так это прокрутить RecyclerView до тех пор, пока EditText не станет самым верхним элементом всякий раз, когда EditText получает фокус. Однако, если мой счетчик элементов достаточно мал, чтобы он не смог заполнить всю высоту навигационного экрана, RecyclerView отказывается прокручивать весь путь вверх и, таким образом, показывая немного больше заголовка, отличного от EditText. В качестве дополнительной проблемы, даже если список достаточно велик, он по-прежнему прокручивается вниз, как только я начинаю фильтровать элементы списка.

Как я могу заставить свой RecyclerView по-прежнему прокручивать весь путь, даже если у меня недостаточно элементов, чтобы заполнить высоту моего представления, чтобы остальная часть моего заголовка не заглядывала?

Вот визуализация того, что я пытаюсь достичь:

_______________ 
| HEADER LINE 1 | 
| HEADER LINE 2 | 
| HEADER LINE 3 | 
| ------------- | 
| Search box | 
| ------------- | 
| List item 1 | 
| List item 2 | 
| List item 3 | 
--------------- 

Ожидаемое поведение, когда я сосредотачиваюсь на поле поиска:

_______________ 
| ------------- | 
| Search box | <- becomes the topmost item. I can already achieve this 
| ------------- | with RecyclerView.smoothScrollBy(dx, dy) 
| List item 1 | except when the list is small enough (see below) 
| List item 2 | 
| List item 3 | 
| List item 4 | 
|    | <- empty space is okay and is desired if dataset 
|    | is small enough 
--------------- 

Что на самом деле происходит:

_______________ 
| HEADER LINE 2 | <- the header is 'bleeding' on top 
| HEADER LINE 3 | 
| ------------- | 
| Search box | <- search box cannot take the topmost spot 
| ------------- |  as the recycler tries to position itself to fill in 
| List item 1 |  the bottom part with items 
| List item 2 | 
| List item 3 | 
| List item 4 | <- items are pushed down to the bottom to fill 
---------------  in the space 

Вот фрагмент моего навигационного xml (просто ваша типичная настройка):

<android.support.design.widget.NavigationView 
    android:id="@+id/navigation_view" 
    android:layout_width="wrap_content" 
    android:layout_height="match_parent" 
    android:layout_gravity="start" 
    android:fitsSystemWindows="true"> 
    <android.support.v7.widget.RecyclerView 
     android:id="@+id/drawer_content" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" /> 
</android.support.design.widget.NavigationView> 

ViewHolder с пользовательским заголовком здесь (RecyclerView адаптер):

@Override 
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 
    if(viewType == HEADER) { 
     return new HeaderHolder(mHeaderView); 
    } else { 
     View v = LayoutInflater.from(parent.getContext()) 
       .inflate(R.layout.list_item, parent, false); 

     return new ItemViewHolder(v); 
    } 
} 

Заголовок ящик просто XML макет я завышены в основной деятельности (я назначил слушателей и анимации там) и передается на мой RecyclerView через setHeaderView (заголовок View) Я сделал.

+0

попробовал? Это тот же результат. https://stackoverflow.com/questions/26568087/parallax-header-effect-with-recyclerview –

+0

Пила уже. Мой заголовок имеет элементы управления (включая расширяемый макет), поэтому эффект параллакса разрушит его. Не говоря уже о том, что использование библиотеки для нее было бы излишним, поскольку я уже успешно реализовал собственный заголовок в моем RecyclerView. Это была только позиция прокрутки, это проблема. – dmonloz

+0

Попробуйте изменить высоту представления ресайлера на 'wrap_content'. –

ответ

1

Что вам нужно, это нижний колонтитул, который имеет ту же высоту, что и ваш заголовок.

Есть два сценария, чтобы рассмотреть здесь:

  1. Вашего заголовок имеет фиксированную высоту
  2. Ваш заголовок имеет динамическую высоту

В первом случае решение очень простое ,Просто определить высоту вашего заголовка в некотором значении как <dimen name="header_height">300dp</dimen>, а затем определить свой верхние и нижние колонтитулы, как в следующем:

<FrameLayout 
    android:layout_height="@dimen/header_height"> 

    <!-- Content goes here --> 

</FrameLayout> 

Во втором случае, мы будем иметь, чтобы сделать несколько вещей:

  • Получить высоту заголовке всякий раз, когда он меняет
  • Обновить высоту нижнего колонтитула на то, что значение высоты заголовке является

Там ар e много способов сделать это. Одним из таких способов, который я использовал ранее, является то, что вы можете использовать ViewTreeObserver.OnPreDrawListener в RecyclerView, чтобы определить, когда заголовок имеет высоту (а затем, если он был изменен), а затем передать эту высоту вашему RecyclerView.Adapter, который будет использовать его для установки запрашиваемого высоту для вашего нижнего колонтитула, когда он раздувает его, а затем вам просто нужно позвонить notifyItemChanged(int) на адаптер с индексом нижнего колонтитула, чтобы сообщить ему повторно надуть нижний колонтитул. Вот большинство из того, что я только что описал в коде:

final RecyclerView recyclerView = (RecyclerView) findViewById(R.id.drawer_content); 
recyclerView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { 
    @Override 
    public boolean onPreDraw() { 
     if(recyclerView.getChildCount() < 1 || recyclerView.getChildAt(0) == null || recyclerView.getChildAt(0).getHeight() == 0) { 
      return true; 
     } 

     int headerHeight = recyclerView.getChildAt(0).getHeight(); 
     adapter.setFooterHeight(headerHeight); 

     return true; 
    } 
}); 

И в адаптере:

int footerHeight = 0; 

public void setFooterHeight(int footerHeight){ 
    if(this.footerHeight == footerHeight){ 
     return; 
    } 

    this.footerHeight = footerHeight; 
    notifyItemChanged(getItemCount() - 1); 
} 

И тогда единственное, что вам нужно сделать, это раздувать футер как вы свой заголовок уже и установить его высоту до footerHeight.

Надеюсь, это поможет.

+0

Это было почти пятно. У меня динамический размер заголовка. Установка высоты нижнего колонтитула, а затем notifyItemChanged запускает анимацию списка, когда он сдвигается вниз. Я сделал некоторые хитрости. Имел пустой вид для нижнего колонтитула, который я назвал спейсером. Я установил его размер 'recyclerVIew.getHeight() - (mItemHeight * (getItemCount() - 2))' Чтобы он всегда заполнял экран, если элементы списка недостаточны для заполнения экрана (высота заголовка не имеет значения). Я устанавливаю высоту распорки каждый раз, когда список обновляется. Спасибо, что указал мне в правильном направлении. :) – dmonloz

Смежные вопросы