Skip to content Skip to sidebar Skip to footer

How To Mimic The Listview Stickey-items Like On Lollipop's Contacts App?

Background Google has recently published a new version of Android, which has a contact app that look like this : The problem I need to mimic this kind of list, but I can't find ou

Solution 1:

ok, I've managed to solve all of the issues I've written about:

1.I changed the way that the third party library works (I don't remember where I got the library from, but this one is very similar) , by changing the layout of each row, so that the header would be on the left of the content itself. It's just a matter of a layout XML file and you're pretty much done. Maybe I will publish a nice library for both of those solutions.

2.This is the view I've made. It's not an official implementation (didn't find any), so I made something by myself. It can be more efficient, but at least it's quite easy to understand and also quite flexible:

publicclassCircularViewextendsViewSwitcher {
    private ImageView mImageView;
    private TextView mTextView;
    private Bitmap mBitmap;
    private CharSequence mText;
    privateintmBackgroundColor=0;
    privateintmImageResId=0;

    publicCircularView(final Context context) {
        this(context, null);
    }

    publicCircularView(final Context context, final AttributeSet attrs) {
        super(context, attrs);
        addView(mImageView = newImageView(context), newFrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
                LayoutParams.MATCH_PARENT, Gravity.CENTER));
        addView(mTextView = newTextView(context), newFrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
                LayoutParams.MATCH_PARENT, Gravity.CENTER));
        mTextView.setGravity(Gravity.CENTER);
        if (isInEditMode())
            setTextAndBackgroundColor("", 0xFFff0000);
    }

    @OverrideprotectedvoidonMeasure(finalint widthMeasureSpec, finalint heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        finalintmeasuredWidth= getMeasuredWidth();
        finalintmeasuredHeight= getMeasuredHeight();
        if (measuredWidth != 0 && measuredHeight != 0)
            drawContent(measuredWidth, measuredHeight);
    }

    @SuppressWarnings("deprecation")privatevoiddrawContent(finalint measuredWidth, finalint measuredHeight) {
        ShapeDrawableroundedBackgroundDrawable=null;
        if (mBackgroundColor != 0) {
            roundedBackgroundDrawable = newShapeDrawable(newOvalShape());
            roundedBackgroundDrawable.getPaint().setColor(mBackgroundColor);
            roundedBackgroundDrawable.setIntrinsicHeight(measuredHeight);
            roundedBackgroundDrawable.setIntrinsicWidth(measuredWidth);
            roundedBackgroundDrawable.setBounds(newRect(0, 0, measuredWidth, measuredHeight));
        }
        if (mImageResId != 0) {
            mImageView.setBackgroundDrawable(roundedBackgroundDrawable);
            mImageView.setImageResource(mImageResId);
            mImageView.setScaleType(ScaleType.CENTER_INSIDE);
        } elseif (mText != null) {
            mTextView.setText(mText);
            mTextView.setBackgroundDrawable(roundedBackgroundDrawable);
            // mTextView.setPadding(0, measuredHeight / 4, 0, measuredHeight / 4);
            mTextView.setTextSize(measuredHeight / 5);
        } elseif (mBitmap != null) {
            mImageView.setScaleType(ScaleType.FIT_CENTER);
            mImageView.setBackgroundDrawable(roundedBackgroundDrawable);
            mBitmap = ThumbnailUtils.extractThumbnail(mBitmap, measuredWidth, measuredHeight);
            finalRoundedBitmapDrawableroundedBitmapDrawable= RoundedBitmapDrawableFactory.create(getResources(),
                    mBitmap);
            roundedBitmapDrawable.setCornerRadius((measuredHeight + measuredWidth) / 4);
            mImageView.setImageDrawable(roundedBitmapDrawable);
        }
        resetValuesState(false);
    }

    publicvoidsetTextAndBackgroundColor(final CharSequence text, finalint backgroundColor) {
        resetValuesState(true);
        while (getCurrentView() != mTextView)
            showNext();
        this.mBackgroundColor = backgroundColor;
        mText = text;
        finalintheight= getHeight(), width = getWidth();
        if (height != 0 && width != 0)
            drawContent(width, height);
    }

    publicvoidsetImageResource(finalint imageResId, finalint backgroundColor) {
        resetValuesState(true);
        while (getCurrentView() != mImageView)
            showNext();
        mImageResId = imageResId;
        this.mBackgroundColor = backgroundColor;
        finalintheight= getHeight(), width = getWidth();
        if (height != 0 && width != 0)
            drawContent(width, height);
    }

    publicvoidsetImageBitmap(final Bitmap bitmap) {
        setImageBitmapAndBackgroundColor(bitmap, 0);
    }

    publicvoidsetImageBitmapAndBackgroundColor(final Bitmap bitmap, finalint backgroundColor) {
        resetValuesState(true);
        while (getCurrentView() != mImageView)
            showNext();
        this.mBackgroundColor = backgroundColor;
        mBitmap = bitmap;
        finalintheight= getHeight(), width = getWidth();
        if (height != 0 && width != 0)
            drawContent(width, height);
    }

    privatevoidresetValuesState(finalboolean alsoResetViews) {
        mBackgroundColor = mImageResId = 0;
        mBitmap = null;
        mText = null;
        if (alsoResetViews) {
            mTextView.setText(null);
            mTextView.setBackgroundDrawable(null);
            mImageView.setImageBitmap(null);
            mImageView.setBackgroundDrawable(null);
        }
    }

    public ImageView getImageView() {
        return mImageView;
    }

    public TextView getTextView() {
        return mTextView;
    }

}

3.I've found a nice library that does it, called PagerSlidingTabStrip . Didn't find an official way to style the native one, though.

Another way is to look at Google's sample which is available right within Android-Studio, and is called "SlidingTabLayout". It shows how it's done.

EDIT: a better library for #3 is here, called "PagerSlidingTabStrip" too.

Solution 2:

You can do the following:

  1. On the leftmost side of your RecyclerView, creates a TextView that will hold the letter index;
  2. On the top of the Recycler view (in the layout that wrappes it) place a TextView in order to cover the one you created in step 1, this will be the sticky one;
  3. Add a OnScrollListener in your RecyclerView. On method onScrolled (), set the TextView created in step 2 for the reference text taken from firstVisibleRow. Until here you shall have a stiky index, without the effects of transition;
  4. 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);
        intnext = actual + 1;
        int previous = actual - 1;
        intlast = 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

Solution 3:

I answering so late, but I hope my answer will help to someone. I also had such a task. I have been looking for answers and examples of sticky header, but for recyclerView. I found the best and simplest solution in the article "Sticky Header For RecyclerView" of Saber Solooki.

enter image description here

Based on this example, I made my contact module for my application, it is very simple.

enter image description here

Solution 4:

Solution 5:

To achieve something similar to the contact list of phone, This is the BEST solution I came across!

This will also work with the custom list as Images may need to be there with the name. Above solutions only have the list or array of String which may not everyone need!

Post a Comment for "How To Mimic The Listview Stickey-items Like On Lollipop's Contacts App?"