Google has recently published a new version of Android, which has a contact app that look like this :
You can do the following:
To add the fade in/out transition effect, develop a logic that checks if the item previous of the currentFirstVisibleItem is the last of the previous letter list, or if the secondVisibleItem is the first one of the new letter. Based on these information make the sticky index visible/invisible and the row index the opposed, adding in this last the alpha effect.
if (recyclerView != null) {
View firstVisibleView = recyclerView.getChildAt(0);
View secondVisibleView = recyclerView.getChildAt(1);
TextView firstRowIndex = (TextView) firstVisibleView.findViewById(R.id.sticky_row_index);
TextView secondRowIndex = (TextView) secondVisibleView.findViewById(R.id.sticky_row_index);
int visibleRange = recyclerView.getChildCount();
int actual = recyclerView.getChildPosition(firstVisibleView);
int next = actual + 1;
int previous = actual - 1;
int last = actual + visibleRange;
// RESET STICKY LETTER INDEX
stickyIndex.setText(String.valueOf(getIndexContext(firstRowIndex)).toUpperCase());
stickyIndex.setVisibility(TextView.VISIBLE);
if (dy > 0) {
// USER SCROLLING DOWN THE RecyclerView
if (next <= last) {
if (isHeader(firstRowIndex, secondRowIndex)) {
stickyIndex.setVisibility(TextView.INVISIBLE);
firstRowIndex.setVisibility(TextView.VISIBLE);
firstRowIndex.setAlpha(1 - (Math.abs(firstVisibleView.getY()) / firstRowIndex.getHeight()));
secondRowIndex.setVisibility(TextView.VISIBLE);
} else {
firstRowIndex.setVisibility(TextView.INVISIBLE);
stickyIndex.setVisibility(TextView.VISIBLE);
}
}
} else {
// USER IS SCROLLING UP THE RecyclerVIew
if (next <= last) {
// RESET FIRST ROW STATE
firstRowIndex.setVisibility(TextView.INVISIBLE);
if ((isHeader(firstRowIndex, secondRowIndex) || (getIndexContext(firstRowIndex) != getIndexContext(secondRowIndex))) && isHeader(firstRowIndex, secondRowIndex)) {
stickyIndex.setVisibility(TextView.INVISIBLE);
firstRowIndex.setVisibility(TextView.VISIBLE);
firstRowIndex.setAlpha(1 - (Math.abs(firstVisibleView.getY()) / firstRowIndex.getHeight()));
secondRowIndex.setVisibility(TextView.VISIBLE);
} else {
secondRowIndex.setVisibility(TextView.INVISIBLE);
}
}
}
if (stickyIndex.getVisibility() == TextView.VISIBLE) {
firstRowIndex.setVisibility(TextView.INVISIBLE);
}
}
I have developed a component that does the above logic, it can be found here: https://github.com/edsilfer/sticky-index