How to prevent a child of a RecyclerView cell from moving while the user scrolls

Although it may be possible to leave the title view within the RecyclerView and make it static, I suggest an alternate approach.

The title can continue to be represented internally within the RecyclerView, but the display will be taken outside to the top of the RecyclerView as follows:

- Title (C) <-- We want that to be still
- Recycler View (A)
-   -   Cell (parent) (B)
-   -   -   Content

A RecyclerView.OnScrollListener will listen for the appearance of new items and change the title accordingly. In this way, as new items appear, the title which is a TextView will display the new title. The following demonstrates this.

enter image description here

(This is a bare-bones implementation for demonstration purposes. A full app would display the dog breed images and some sort of meaningful description.)

Here is the code that accomplishes this effect:

MainActivity.java

public class MainActivity extends AppCompatActivity {
    private LinearLayoutManager mLayoutManager;
    private RecyclerViewAdapter mAdapter;
    private TextView mBreedNameTitle;
    private int mLastBreedTitlePosition = RecyclerView.NO_POSITION;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        List<String> breedList = createBreedList();

        // This is where the breed title is displayed.
        mBreedNameTitle = findViewById(R.id.breedNameTitle);

        // Set up the RecyclerView.
        mLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
        RecyclerView recyclerView = findViewById(R.id.recyclerView);
        mAdapter = new RecyclerViewAdapter(breedList);
        recyclerView.setLayoutManager(mLayoutManager);
        recyclerView.setAdapter(mAdapter);

        // Add the OnScrollListener so we know when to change the breed title.
        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);

                int lastVisible = mLayoutManager.findLastVisibleItemPosition();
                if (lastVisible == RecyclerView.NO_POSITION) {
                    return;
                }
                if (lastVisible != mLastBreedTitlePosition) {
                    mBreedNameTitle.setText(mAdapter.getItems().get(lastVisible));
                    mLastBreedTitlePosition = lastVisible;
                }
            }
        });
    }

    private List<String> createBreedList() {
        List<String> breedList = new ArrayList<>();
        breedList.add("Affenpinscher");
        breedList.add("Afghan Hound");
        breedList.add("Airedale Terrier");
        breedList.add("Akita");
        breedList.add("Alaskan Malamute");
        breedList.add("American Cocker Spaniel");
        breedList.add("American Eskimo Dog (Miniature)");
        breedList.add("American Eskimo Dog (Standard)");
        breedList.add("American Eskimo Dog (Toy)");
        breedList.add("American Foxhound");
        breedList.add("American Staffordshire Terrier");
        breedList.add("American Eskimo Dog (Standard)");
        return breedList;
    }

    @SuppressWarnings("unused")
    private final static String TAG = "MainActivity";
}

class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private final List<String> mItems;

    RecyclerViewAdapter(List<String> items) {
        mItems = items;
    }

    @Override
    @NonNull
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view;

        view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_layout, parent, false);
        return new RecyclerViewAdapter.ItemViewHolder(view);
    }


    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        RecyclerViewAdapter.ItemViewHolder vh = (RecyclerViewAdapter.ItemViewHolder) holder;

        vh.mBreedImage.setImageDrawable(holder.itemView.getResources().getDrawable(R.drawable.no_image));
        vh.mBreedName = mItems.get(position);
    }

    @Override
    public int getItemCount() {
        return mItems.size();
    }

    public List<String> getItems() {
        return mItems;
    }

    static class ItemViewHolder extends RecyclerView.ViewHolder {
        private ImageView mBreedImage;
        private String mBreedName;

        ItemViewHolder(View itemView) {
            super(itemView);
            mBreedImage = itemView.findViewById(R.id.breedImage);
        }
    }
}

activity_main.xml

<LinearLayout 
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="@dimen/activity_horizontal_margin"
    android:orientation="vertical">

    <TextView
        android:id="@+id/breedNameTitle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:fontFamily="sans-serif"
        android:textColor="@android:color/black"
        android:textSize="16sp"
        tools:text="Breed name" />

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal" />
</LinearLayout>

item_layout.xml

<android.support.constraint.ConstraintLayout 
    android:id="@+id/cont_item_root"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@android:color/white">

    <ImageView
        android:id="@+id/breedImage"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:layout_marginBottom="8dp"
        android:layout_marginEnd="8dp"
        android:contentDescription="Dog breed image"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="1.0"
        tools:ignore="HardcodedText" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginEnd="16dp"
        android:layout_marginStart="16dp"
        android:text="@string/large_text"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/breedImage"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

Update: Here is another approach that sets the left padding of a TextView to make the header sticky. The negative x-offset of the TextView is taken as padding for the header to make it slide to the right within the TextView and stick to the left side of the screen.

Here is the result:

enter image description here

MainActivity.java

public class MainActivity extends AppCompatActivity {
    private LinearLayoutManager mLayoutManager;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        List<String> breedList = createBreedList();

        // Set up the RecyclerView.
        mLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
        RecyclerView recyclerView = findViewById(R.id.recyclerView);
        RecyclerViewAdapter adapter = new RecyclerViewAdapter(breedList);
        recyclerView.setLayoutManager(mLayoutManager);
        recyclerView.setAdapter(adapter);

        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);

                // Pad the left of the breed name so it stays aligned with the left side of the display.
                int firstVisible = mLayoutManager.findFirstVisibleItemPosition();
                View firstView = mLayoutManager.findViewByPosition(firstVisible);
                firstView.findViewById(R.id.itemBreedName).setPadding((int) -firstView.getX(), 0, 0, 0);

                // Make sure the other breed name has zero padding because we may have changed it.
                int lastVisible = mLayoutManager.findLastVisibleItemPosition();
                View lastView = mLayoutManager.findViewByPosition(lastVisible);
                lastView.findViewById(R.id.itemBreedName).setPadding(0, 0, 0, 0);
            }
        });
    }

    private List<String> createBreedList() {
        List<String> breedList = new ArrayList<>();
        breedList.add("Affenpinscher");
        breedList.add("Afghan Hound");
        breedList.add("Airedale Terrier");
        breedList.add("Akita");
        breedList.add("Alaskan Malamute");
        breedList.add("American Cocker Spaniel");
        breedList.add("American Eskimo Dog (Miniature)");
        breedList.add("American Eskimo Dog (Standard)");
        breedList.add("American Eskimo Dog (Toy)");
        breedList.add("American Foxhound");
        breedList.add("American Staffordshire Terrier");
        breedList.add("American Eskimo Dog (Standard)");
        return breedList;
    }

    @SuppressWarnings("unused")
    private final static String TAG = "MainActivity";

}

class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private final List<String> mItems;

    RecyclerViewAdapter(List<String> items) {
        mItems = items;
    }

    @Override
    @NonNull
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view;

        view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_layout, parent, false);
        return new RecyclerViewAdapter.ItemViewHolder(view);
    }


    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        RecyclerViewAdapter.ItemViewHolder vh = (RecyclerViewAdapter.ItemViewHolder) holder;

        vh.mBreedImage.setImageDrawable(holder.itemView.getResources().getDrawable(R.drawable.no_image));
        vh.mBreedName.setPadding(0, 0, 0, 0);
        vh.mBreedName.setText(mItems.get(position));
    }

    @Override
    public int getItemCount() {
        return mItems.size();
    }

    static class ItemViewHolder extends RecyclerView.ViewHolder {
        private ImageView mBreedImage;
        private TextView mBreedName;

        ItemViewHolder(View itemView) {
            super(itemView);
            mBreedImage = itemView.findViewById(R.id.breedImage);
            mBreedName = itemView.findViewById(R.id.itemBreedName);
        }
    }
}

activity_main.xml

<LinearLayout 
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="@dimen/activity_horizontal_margin"
    android:orientation="vertical">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal" />
</LinearLayout>

item_layout.xml

<android.support.constraint.ConstraintLayout 
    android:id="@+id/cont_item_root"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@android:color/white">

    <TextView
        android:id="@+id/itemBreedName"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:ellipsize="none"
        android:fontFamily="sans-serif"
        android:singleLine="true"
        android:textColor="@android:color/black"
        android:textSize="16sp"
        tools:text="Breed name" />

    <ImageView
        android:id="@+id/breedImage"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:layout_marginBottom="8dp"
        android:layout_marginEnd="8dp"
        android:contentDescription="Dog breed image"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/itemBreedName"
        app:layout_constraintVertical_bias="1.0"
        tools:ignore="HardcodedText" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginEnd="16dp"
        android:layout_marginStart="16dp"
        android:text="@string/large_text"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/breedImage"
        app:layout_constraintTop_toBottomOf="@+id/itemBreedName" />

</android.support.constraint.ConstraintLayout>