Dividers in RecyclerView

My question on this topic got a considerable amount of attention, which only proves how insufficient the official documentation can sometimes be, even with regard to the basic issues a developer will encounter sooner or later.

A few very useful answers were posted by the community, but none of them covered both dividers and spaces, and some were based on deprecated code, so I’m posting a short wrap-up.

Unlike ListView, the RecyclerView class has no divider-related parameters. Instead, you need to extend ItemDecoration, a RecyclerView‘s inner class:

An ItemDecoration allows the application to add a special drawing and layout offset to specific item views from the adapter’s data set. This can be useful for drawing dividers between items, highlights, visual grouping boundaries and more.

All ItemDecorations are drawn in the order they were added, before the item views (in onDraw()) and after the items (in onDrawOver(Canvas, RecyclerView, RecyclerView.State).

Vertical spacing ItemDecoration

Extend ItemDecoration, add custom constructor which takes space height as a parameter and override getItemOffsets() method:

public class VerticalSpaceItemDecoration extends RecyclerView.ItemDecoration {

    private final int mVerticalSpaceHeight;

    public VerticalSpaceItemDecoration(int mVerticalSpaceHeight) {
        this.mVerticalSpaceHeight = mVerticalSpaceHeight;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
            RecyclerView.State state) {
        outRect.bottom = mVerticalSpaceHeight;
    }
}

If you don’t want to insert space below the last item, add the following condition:

if (parent.getChildAdapterPosition(view) != parent.getAdapter().getItemCount() - 1) {
            outRect.bottom = mVerticalSpaceHeight;
}

Note: you can also modify outRect.top, outRect.left and outRect.right properties for desired effect.

Divider ItemDecoration

Extend ItemDecoration and override onDraw() method:

public class DividerItemDecoration extends RecyclerView.ItemDecoration {

    private static final int[] ATTRS = new int[]{android.R.attr.listDivider};

    private Drawable mDivider;

    /**
     * Default divider will be used
     */
    public DividerItemDecoration(Context context) {
        final TypedArray styledAttributes = context.obtainStyledAttributes(ATTRS);
        mDivider = styledAttributes.getDrawable(0);
        styledAttributes.recycle();
    }

    /**
     * Custom divider will be used
     */
    public DividerItemDecoration(Context context, int resId) {
        mDivider = ContextCompat.getDrawable(context, resId);
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        int left = parent.getPaddingLeft();
        int right = parent.getWidth() - parent.getPaddingRight();

        int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = parent.getChildAt(i);

            RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();

            int top = child.getBottom() + params.bottomMargin;
            int bottom = top + mDivider.getIntrinsicHeight();

            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }
}

You can either call the first constructor that uses the default Android divider attributes, or the second one that uses your own drawable, for example drawable/divider.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="rectangle">
    <size android:height="1dp" />
    <solid android:color="#ff992900" />
</shape>

Note: if you want the divider to be drawn over your items, override onDrawOver() method instead.

Usage

To use your new class add VerticalSpaceItemDecoration or DividerSpaceItemDecoration to RecyclerView, for example in your fragment’s onCreateView() method:

private static final int VERTICAL_ITEM_SPACE = 48;
private RecyclerView mUiRecyclerView;
private LinearLayoutManager mLinearLayoutManager;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    View rootView = inflater.inflate(R.layout.fragment_feed, container, false);

    mUiRecyclerView = (RecyclerView) rootView.findViewById(R.id.fragment_home_recycler_view);
    mLinearLayoutManager = new LinearLayoutManager(getActivity());
    mUiRecyclerView.setLayoutManager(mLinearLayoutManager);

    //add ItemDecoration
    mUiRecyclerView.addItemDecoration(new VerticalSpaceItemDecoration(VERTICAL_ITEM_SPACE));
    //or
    mUiRecyclerView.addItemDecoration(
            new DividerItemDecoration(getActivity());
    //or
    mUiRecyclerView.addItemDecoration(
            new DividerItemDecoration(getActivity(), R.drawable.divider));

    mUiRecyclerView.setAdapter(...);

    return rootView;
}

There’s also Lucas Rocha’s library which is supposed to simplify the item decoration process. Haven’t tried it though.

Among its features are:

  • A collection of stock item decorations including:
  • Item spacing Horizontal/vertical dividers.
  • List item

How to get user’s e-mail without explicitly asking for permission?

Sorry to disappoint you – it’s not a hack.

Before Android 4.0, the official way was to use the AccountManager class which required the android.permission.GET_ACCOUNTS permission.

Later in API 14, a new method was introduced, which relies on ContactsContract.Profile. There’s been some controversy around it, as it requires two extremely sensitive permissions: android.permission.READ_PROFILE and android.permission.READ_CONTACTS.

Luckily, in most cases, you can bypass all these permissions and use the AccountPicker:

Intent emailIntent = AccountPicker.newChooseAccountIntent(null, null,
                new String[] { GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE }, false, null, null, null, null);
startActivityForResult(emailIntent, REQUEST_CODE_EMAIL);

This will prompt user to choose his account and retrieve the e-mail based on his choice. No scary permission dialog, no suspicions.

The second and the last step is to get the e-mail in onActivityResult:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_CODE_EMAIL && resultCode == RESULT_OK) {
        String email = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
    }
}

No, you can’t get heart beat data from Android Wear smartwatch using Google Fit API

At least for now. This is what Ian Lake from Google explained to me when I was struggling to read the real-time pulse from Fitness.SENSORS_API. Apparently, we have to wait for the next generation of Android Wear devices with BLE heart rate monitor on board. Until then, developers have to manually hook into raw BPM monitors and consume the data somehow. Ironically, they can later put all the pulse values into Google Fit (as DataSource.TYPE_RAW).

Together with my friend Piotr, I’ve started to work on HeartWatch – an app that’s supposed to help monitor heart rate of elder people. We ordered a Samsung Gear Live watch from Korea, hoping to use it with recently released Google Fit, but in that case we’ll have to implement our own mechanism.

Update: the app is now available on Google Play.