0

I would like to inset a RecyclerView ItemDecorator divider to left align with a TextView (the item title) inside the list element that is constrained to be 72dp from the parent left side. Something like we see here in the Google Material Design Docs. I assume that I need to somehow reference the layout params on the titleTextView, but am not sure how to do that from my ItemDecorator code, as I seem to be able to only get the params which is the ConstraintLayout, and not the textview itself. Any help or pointers in the right direction would be greatly appreciated! The relevant ItemDecorator code, where I try to get the textView param looks something like this:

        for (int i = 0; i < recyclerView.getChildCount() - 1; ++i) {
            View child = recyclerView.getChildAt(i);
            RecyclerView.LayoutParams params =(RecyclerView.LayoutParams) child.getLayoutParams();
//this just gives me the constraint layout, not the TextView params, how to reference the textViewParams!?

The RecyclerView looks like this:

        <android.support.v7.widget.RecyclerView
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/myList"
        android:name="com.example.Something"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layoutManager="LinearLayoutManager"
        tools:context=".controllers.fragments.ExampleFragment"
        tools:listitem="@layout/fragment_something"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent" />

The list item xml is a constraint layout with some TextViews, and the relevant portion looks like this:

    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="72dp">

    <ImageView
        android:id="@+id/myImageView"
        android:layout_width="32dp"
        android:layout_height="32dp"
        android:layout_marginLeft="16dp"
        app:layout_constraintLeft_toLeftOf="parent"
        tools:src="@drawable/ic_menu_manage"
        android:src="@drawable/ic_menu_manage"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_marginTop="16dp"
        app:layout_constraintBottom_toBottomOf="parent"
        android:layout_marginBottom="16dp" />

    <TextView
        android:id="@+id/titleTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="4dp"
        android:layout_marginLeft="72dp"
        android:layout_marginTop="20dp"
        android:text="MyTitle"
        android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
        android:textStyle="bold"
        app:layout_constraintBottom_toTopOf="@+id/textView5"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="SampleTitle" />

    ...

    </android.support.constraint.ConstraintLayout>
bryant
  • 2,041
  • 3
  • 18
  • 26

3 Answers3

2

use a custom item decorator.

class CustomItemDecoration(context: Context) : RecyclerView.ItemDecoration()
{

    private val mDivider: Drawable = context.resources.getDrawable(R.drawable.custom_item_divider) // this is a shape   that i want to use

    override fun onDrawOver(c: Canvas,
                            parent: RecyclerView,
                            state: RecyclerView.State)
    {
        val left = parent.paddingLeft // change to how much padding u want to add here .  
        val right = parent.width - parent.paddingRight

        val childCount = parent.childCount
        for (i in 0 until childCount)
        {
            val child = parent.getChildAt(i)

            val params = child.layoutParams as RecyclerView.LayoutParams

            val top = child.bottom + params.bottomMargin
            val bottom = top + mDivider.intrinsicHeight

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

you set it like that

recyclerView.addItemDecoration(CustomItemDecoration(context))
Arutha
  • 21
  • 1
  • Hi Arutha, thanks for your reply. What i have is basically what you wrote, except that where you wrote “change to how much padding you want here”, that is where I would like to reference the padding on the textview. Not sure how to access that? – bryant Sep 26 '17 at 13:49
  • 2
    parent is the recyclerview so you can get the view from there, val item = parent.getchildat(0) val textview = item.getchild(x) as textview then u can use the textviews data/layout parameters to get what you need isn't padding on textview is static? that you wrote before in the xml which is 72 dp? just change the paddingleft part with a 72 * getDpi() (some function to convert 72 to dpi) – Arutha Sep 26 '17 at 18:22
0

Since the ConstraintLayout is a ViewGroup, it is possible to access the child of the ConstraintLayout from inside of the ItemDecorator by simply doing something like:

public class MyItemDivider extends RecyclerView.ItemDecoration {

....some code here


@Override
public void onDrawOver(Canvas canvas, RecyclerView recyclerView,
                       RecyclerView.State state) {
    super.onDrawOver(canvas, recyclerView, state);

    for (int i = 0; i < recyclerView.getChildCount() - 1; ++i) {
        ViewGroup item = (ViewGroup) recyclerView.getChildAt(i);

        //for now the titleText is at index 1 of constraint layout
        TextView titleChild =  (TextView) item.getChildAt(1);
        int titleTextLeftMargin = titleChild.getLeft();

        //this would probably be less brittle 
        //but I would need to somehow change the context for this to be doable?
        //View titleChild = item.findViewById(R.id.contractListProductNameTextView);
        ...some more code here

            divider.setBounds(titleTextLeftMargin, top, right, bottom);
            divider.draw(canvas);
        }
    }
}
bryant
  • 2,041
  • 3
  • 18
  • 26
0

Since you already know by how much you have to inset the divider from the left side (calculate it from your layout if you don't know), all you need to do is convert that dp value to px as shown here and set it as the left bound in your custom ItemDecoration.

public class InsetDividerDecoration extends RecyclerView.ItemDecoration {

    private final int insetValueInDp = 72; // in your case

    @Override
    public void onDraw(Canvas canvas, RecyclerView parent, RecyclerView.State state) {
        int dividerLeft = (int) (insetValueInDp * Resources.getSystem().getDisplayMetrics().density);

        ...
    }
}

And use it in your recycler view -

recyclerView.addItemDecoration(new InsetDividerDecoration());
jL4
  • 1,238
  • 10
  • 15