1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 18 package android.support.v7.widget; 19 20 import android.content.Context; 21 import android.content.res.TypedArray; 22 import android.graphics.Canvas; 23 import android.graphics.Rect; 24 import android.graphics.drawable.Drawable; 25 import android.support.annotation.NonNull; 26 import android.util.Log; 27 import android.view.View; 28 import android.widget.LinearLayout; 29 30 /** 31 * DividerItemDecoration is a {@link RecyclerView.ItemDecoration} that can be used as a divider 32 * between items of a {@link LinearLayoutManager}. It supports both {@link #HORIZONTAL} and 33 * {@link #VERTICAL} orientations. 34 * 35 * <pre> 36 * mDividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(), 37 * mLayoutManager.getOrientation()); 38 * recyclerView.addItemDecoration(mDividerItemDecoration); 39 * </pre> 40 */ 41 public class DividerItemDecoration extends RecyclerView.ItemDecoration { 42 public static final int HORIZONTAL = LinearLayout.HORIZONTAL; 43 public static final int VERTICAL = LinearLayout.VERTICAL; 44 45 private static final String TAG = "DividerItem"; 46 private static final int[] ATTRS = new int[]{ android.R.attr.listDivider }; 47 48 private Drawable mDivider; 49 50 /** 51 * Current orientation. Either {@link #HORIZONTAL} or {@link #VERTICAL}. 52 */ 53 private int mOrientation; 54 55 private final Rect mBounds = new Rect(); 56 57 /** 58 * Creates a divider {@link RecyclerView.ItemDecoration} that can be used with a 59 * {@link LinearLayoutManager}. 60 * 61 * @param context Current context, it will be used to access resources. 62 * @param orientation Divider orientation. Should be {@link #HORIZONTAL} or {@link #VERTICAL}. 63 */ DividerItemDecoration(Context context, int orientation)64 public DividerItemDecoration(Context context, int orientation) { 65 final TypedArray a = context.obtainStyledAttributes(ATTRS); 66 mDivider = a.getDrawable(0); 67 if (mDivider == null) { 68 Log.w(TAG, "@android:attr/listDivider was not set in the theme used for this " 69 + "DividerItemDecoration. Please set that attribute all call setDrawable()"); 70 } 71 a.recycle(); 72 setOrientation(orientation); 73 } 74 75 /** 76 * Sets the orientation for this divider. This should be called if 77 * {@link RecyclerView.LayoutManager} changes orientation. 78 * 79 * @param orientation {@link #HORIZONTAL} or {@link #VERTICAL} 80 */ setOrientation(int orientation)81 public void setOrientation(int orientation) { 82 if (orientation != HORIZONTAL && orientation != VERTICAL) { 83 throw new IllegalArgumentException( 84 "Invalid orientation. It should be either HORIZONTAL or VERTICAL"); 85 } 86 mOrientation = orientation; 87 } 88 89 /** 90 * Sets the {@link Drawable} for this divider. 91 * 92 * @param drawable Drawable that should be used as a divider. 93 */ setDrawable(@onNull Drawable drawable)94 public void setDrawable(@NonNull Drawable drawable) { 95 if (drawable == null) { 96 throw new IllegalArgumentException("Drawable cannot be null."); 97 } 98 mDivider = drawable; 99 } 100 101 @Override onDraw(Canvas c, RecyclerView parent, RecyclerView.State state)102 public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { 103 if (parent.getLayoutManager() == null || mDivider == null) { 104 return; 105 } 106 if (mOrientation == VERTICAL) { 107 drawVertical(c, parent); 108 } else { 109 drawHorizontal(c, parent); 110 } 111 } 112 drawVertical(Canvas canvas, RecyclerView parent)113 private void drawVertical(Canvas canvas, RecyclerView parent) { 114 canvas.save(); 115 final int left; 116 final int right; 117 //noinspection AndroidLintNewApi - NewApi lint fails to handle overrides. 118 if (parent.getClipToPadding()) { 119 left = parent.getPaddingLeft(); 120 right = parent.getWidth() - parent.getPaddingRight(); 121 canvas.clipRect(left, parent.getPaddingTop(), right, 122 parent.getHeight() - parent.getPaddingBottom()); 123 } else { 124 left = 0; 125 right = parent.getWidth(); 126 } 127 128 final int childCount = parent.getChildCount(); 129 for (int i = 0; i < childCount; i++) { 130 final View child = parent.getChildAt(i); 131 parent.getDecoratedBoundsWithMargins(child, mBounds); 132 final int bottom = mBounds.bottom + Math.round(child.getTranslationY()); 133 final int top = bottom - mDivider.getIntrinsicHeight(); 134 mDivider.setBounds(left, top, right, bottom); 135 mDivider.draw(canvas); 136 } 137 canvas.restore(); 138 } 139 drawHorizontal(Canvas canvas, RecyclerView parent)140 private void drawHorizontal(Canvas canvas, RecyclerView parent) { 141 canvas.save(); 142 final int top; 143 final int bottom; 144 //noinspection AndroidLintNewApi - NewApi lint fails to handle overrides. 145 if (parent.getClipToPadding()) { 146 top = parent.getPaddingTop(); 147 bottom = parent.getHeight() - parent.getPaddingBottom(); 148 canvas.clipRect(parent.getPaddingLeft(), top, 149 parent.getWidth() - parent.getPaddingRight(), bottom); 150 } else { 151 top = 0; 152 bottom = parent.getHeight(); 153 } 154 155 final int childCount = parent.getChildCount(); 156 for (int i = 0; i < childCount; i++) { 157 final View child = parent.getChildAt(i); 158 parent.getLayoutManager().getDecoratedBoundsWithMargins(child, mBounds); 159 final int right = mBounds.right + Math.round(child.getTranslationX()); 160 final int left = right - mDivider.getIntrinsicWidth(); 161 mDivider.setBounds(left, top, right, bottom); 162 mDivider.draw(canvas); 163 } 164 canvas.restore(); 165 } 166 167 @Override getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state)168 public void getItemOffsets(Rect outRect, View view, RecyclerView parent, 169 RecyclerView.State state) { 170 if (mDivider == null) { 171 outRect.set(0, 0, 0, 0); 172 return; 173 } 174 if (mOrientation == VERTICAL) { 175 outRect.set(0, 0, 0, mDivider.getIntrinsicHeight()); 176 } else { 177 outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0); 178 } 179 } 180 } 181