Когда элемент добавляется в верхнюю часть RecyclerView
и элемент может помещаться на экране, элемент прикрепляется к держателю вида, и RecyclerView
проходит фазу анимации для перемещения элементов вниз, чтобы отобразить новый элемент.вверху.
Если новый элемент нельзя отобразить без прокрутки, держатель вида не создается, поэтому анимировать нечего.Единственный способ вывести новый элемент на экран, когда это происходит, - это прокрутка, которая приводит к созданию держателя вида, так что вид может быть размещен на экране.(По-видимому, существует крайний случай, когда представление частично отображается и создается держатель представления, но я игнорирую этот конкретный экземпляр, поскольку он не уместен.)
Итак, проблема в том, что два разныхдействия, анимация добавленного вида и прокрутка добавленного вида должны быть сделаны так, чтобы они выглядели одинаково для пользователя.Мы могли бы погрузиться в базовый код и точно выяснить, что происходит с точки зрения создания держателя представления, синхронизации анимации и т. Д. Но даже если мы сможем дублировать действия, он может сломаться, если базовый код изменится.Это то, чему вы сопротивляетесь.
Альтернативой является добавление заголовка в нулевую позицию RecyclerView
.Вы всегда будете видеть анимацию при отображении этого заголовка и добавлении новых элементов в позицию 1. Если вам не нужен заголовок, вы можете сделать его нулевой высотой, и он не будет отображаться.Следующее видео демонстрирует эту технику:
![[video]](https://i.stack.imgur.com/1o6uO.gif)
Это код для демонстрации.Он просто добавляет фиктивную запись в позиции 0 предметов.Если фиктивная запись вам не по вкусу, есть другие способы подойти к этому.Вы можете искать способы добавления заголовков к RecyclerView
.
(Если вы используете полосу прокрутки, она будет работать неправильно, как вы, вероятно, можете сказать из демо. Чтобы исправить это на 100%, вы будетеприходится принимать на себя большую часть вычислений высоты и размещения полосы прокрутки. Пользовательский computeVerticalScrollOffset()
для LinearLayoutManager
заботится о размещении полосы прокрутки вверху, когда это необходимо. (Код был введен после снятия видео.) Однако полоса прокруткискачки при прокрутке вниз. Эту проблему можно решить с помощью лучшего вычисления размещения. См. этот вопрос переполнения стека для получения дополнительной информации о полосах прокрутки в контексте элементов различной высоты.)
MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private TheAdapter mAdapter;
private final ArrayList<String> mItems = new ArrayList<>();
private int mItemCount = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
LinearLayoutManager layoutManager =
new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) {
@Override
public int computeVerticalScrollOffset(RecyclerView.State state) {
if (findFirstCompletelyVisibleItemPosition() == 0) {
// Force scrollbar to top of range. When scrolling down, the scrollbar
// will jump since RecyclerView seems to assume the same height for
// all items.
return 0;
} else {
return super.computeVerticalScrollOffset(state);
}
}
};
recyclerView.setLayoutManager(layoutManager);
for (mItemCount = 0; mItemCount < 6; mItemCount++) {
mItems.add(0, "Item # " + mItemCount);
}
// Create a dummy entry that is just a placeholder.
mItems.add(0, "Dummy item that won't display");
mAdapter = new TheAdapter(mItems);
recyclerView.setAdapter(mAdapter);
}
@Override
public void onClick(View view) {
// Always at to position #1 to let animation occur.
mItems.add(1, "Item # " + mItemCount++);
mAdapter.notifyItemInserted(1);
}
}
TheAdapter.java
class TheAdapter extends RecyclerView.Adapter<TheAdapter.ItemHolder> {
private ArrayList<String> mData;
public TheAdapter(ArrayList<String> data) {
mData = data;
}
@Override
public ItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
if (viewType == 0) {
// Create a zero-height view that will sit at the top of the RecyclerView to force
// animations when items are added below it.
view = new Space(parent.getContext());
view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0));
} else {
view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_item, parent, false);
}
return new ItemHolder(view);
}
@Override
public void onBindViewHolder(final ItemHolder holder, int position) {
if (position == 0) {
return;
}
holder.mTextView.setText(mData.get(position));
}
@Override
public int getItemViewType(int position) {
return (position == 0) ? 0 : 1;
}
@Override
public int getItemCount() {
return mData.size();
}
public static class ItemHolder extends RecyclerView.ViewHolder {
private TextView mTextView;
public ItemHolder(View itemView) {
super(itemView);
mTextView = (TextView) itemView.findViewById(R.id.textView);
}
}
}
activity_main.xml
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
android:scrollbars="vertical"
app:layout_constraintBottom_toTopOf="@+id/button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:text="Button"
android:onClick="onClick"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</android.support.constraint.ConstraintLayout>
list_item.xml
<LinearLayout
android:id="@+id/list_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:orientation="horizontal">
<View
android:id="@+id/box"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginStart="16dp"
android:background="@android:color/holo_green_light"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:textSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/box"
app:layout_constraintTop_toTopOf="parent"
tools:text="TextView" />
</LinearLayout>