1/* 2 * Copyright (C) 2015 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 17package com.android.setupwizardlib; 18 19import android.content.Context; 20import android.content.res.TypedArray; 21import android.graphics.Canvas; 22import android.graphics.Rect; 23import android.graphics.drawable.Drawable; 24import android.support.annotation.IntDef; 25import android.support.v4.view.ViewCompat; 26import android.support.v7.widget.RecyclerView; 27import android.view.View; 28 29import java.lang.annotation.Retention; 30import java.lang.annotation.RetentionPolicy; 31 32/** 33 * An {@link android.support.v7.widget.RecyclerView.ItemDecoration} for RecyclerView to draw 34 * dividers between items. This ItemDecoration will draw the drawable specified by 35 * {@link #setDivider(android.graphics.drawable.Drawable)} as the divider in between each item by 36 * default, and the behavior of whether the divider is shown can be customized by subclassing 37 * {@link com.android.setupwizardlib.DividerItemDecoration.DividedViewHolder}. 38 * 39 * <p>Modified from v14 PreferenceFragment.DividerDecoration, added with inset capabilities. 40 */ 41public class DividerItemDecoration extends RecyclerView.ItemDecoration { 42 43 /* static section */ 44 45 public interface DividedViewHolder { 46 47 /** 48 * Returns whether divider is allowed above this item. A divider will be shown only if both 49 * items immediately above and below it allows this divider. 50 */ 51 boolean isDividerAllowedAbove(); 52 53 /** 54 * Returns whether divider is allowed below this item. A divider will be shown only if both 55 * items immediately above and below it allows this divider. 56 */ 57 boolean isDividerAllowedBelow(); 58 } 59 60 @Retention(RetentionPolicy.SOURCE) 61 @IntDef({ 62 DIVIDER_CONDITION_EITHER, 63 DIVIDER_CONDITION_BOTH}) 64 public @interface DividerCondition {} 65 66 public static final int DIVIDER_CONDITION_EITHER = 0; 67 public static final int DIVIDER_CONDITION_BOTH = 1; 68 69 /** 70 * Creates a default instance of {@link DividerItemDecoration}, using 71 * {@code android:attr/listDivider} as the divider and {@code android:attr/dividerHeight} as the 72 * divider height. 73 */ 74 public static DividerItemDecoration getDefault(Context context) { 75 final TypedArray a = context.obtainStyledAttributes(R.styleable.SuwDividerItemDecoration); 76 final Drawable divider = a.getDrawable( 77 R.styleable.SuwDividerItemDecoration_android_listDivider); 78 final int dividerHeight = a.getDimensionPixelSize( 79 R.styleable.SuwDividerItemDecoration_android_dividerHeight, 0); 80 @DividerCondition final int dividerCondition = a.getInt( 81 R.styleable.SuwDividerItemDecoration_suwDividerCondition, 82 DIVIDER_CONDITION_EITHER); 83 a.recycle(); 84 85 final DividerItemDecoration decoration = new DividerItemDecoration(); 86 decoration.setDivider(divider); 87 decoration.setDividerHeight(dividerHeight); 88 decoration.setDividerCondition(dividerCondition); 89 return decoration; 90 } 91 92 /* non-static section */ 93 94 private Drawable mDivider; 95 private int mDividerHeight; 96 private int mDividerIntrinsicHeight; 97 @DividerCondition 98 private int mDividerCondition; 99 100 @Override 101 public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { 102 if (mDivider == null) { 103 return; 104 } 105 final int childCount = parent.getChildCount(); 106 final int width = parent.getWidth(); 107 final int dividerHeight = mDividerHeight != 0 ? mDividerHeight : mDividerIntrinsicHeight; 108 for (int childViewIndex = 0; childViewIndex < childCount; childViewIndex++) { 109 final View view = parent.getChildAt(childViewIndex); 110 if (shouldDrawDividerBelow(view, parent)) { 111 final int top = (int) ViewCompat.getY(view) + view.getHeight(); 112 mDivider.setBounds(0, top, width, top + dividerHeight); 113 mDivider.draw(c); 114 } 115 } 116 } 117 118 @Override 119 public void getItemOffsets(Rect outRect, View view, RecyclerView parent, 120 RecyclerView.State state) { 121 if (shouldDrawDividerBelow(view, parent)) { 122 outRect.bottom = mDividerHeight != 0 ? mDividerHeight : mDividerIntrinsicHeight; 123 } 124 } 125 126 private boolean shouldDrawDividerBelow(View view, RecyclerView parent) { 127 final RecyclerView.ViewHolder holder = parent.getChildViewHolder(view); 128 final int index = holder.getLayoutPosition(); 129 final int lastItemIndex = parent.getAdapter().getItemCount() - 1; 130 if ((holder instanceof DividedViewHolder)) { 131 if (((DividedViewHolder) holder).isDividerAllowedBelow()) { 132 if (mDividerCondition == DIVIDER_CONDITION_EITHER) { 133 // Draw the divider without consulting the next item if we only 134 // need permission for either above or below. 135 return true; 136 } 137 } else if (mDividerCondition == DIVIDER_CONDITION_BOTH || index == lastItemIndex) { 138 // Don't draw if the current view holder doesn't allow drawing below 139 // and the current theme requires permission for both the item below and above. 140 // Also, if this is the last item, there is no item below to ask permission 141 // for whether to draw a divider above, so don't draw it. 142 return false; 143 } 144 } 145 // Require permission from index below to draw the divider. 146 if (index < lastItemIndex) { 147 final RecyclerView.ViewHolder nextHolder = 148 parent.findViewHolderForLayoutPosition(index + 1); 149 if ((nextHolder instanceof DividedViewHolder) 150 && !((DividedViewHolder) nextHolder).isDividerAllowedAbove()) { 151 // Don't draw if the next view holder doesn't allow drawing above 152 return false; 153 } 154 } 155 return true; 156 } 157 158 /** 159 * Sets the drawable to be used as the divider. 160 */ 161 public void setDivider(Drawable divider) { 162 if (divider != null) { 163 mDividerIntrinsicHeight = divider.getIntrinsicHeight(); 164 } else { 165 mDividerIntrinsicHeight = 0; 166 } 167 mDivider = divider; 168 } 169 170 /** 171 * Gets the drawable currently used as the divider. 172 */ 173 public Drawable getDivider() { 174 return mDivider; 175 } 176 177 /** 178 * Sets the divider height, in pixels. 179 */ 180 public void setDividerHeight(int dividerHeight) { 181 mDividerHeight = dividerHeight; 182 } 183 184 /** 185 * Gets the divider height, in pixels. 186 */ 187 public int getDividerHeight() { 188 return mDividerHeight; 189 } 190 191 /** 192 * Sets whether the divider needs permission from both the item view holder below 193 * and above from where the divider would draw itself or just needs permission from 194 * one or the other before drawing itself. 195 */ 196 public void setDividerCondition(@DividerCondition int dividerCondition) { 197 mDividerCondition = dividerCondition; 198 } 199 200 /** 201 * Gets whether the divider needs permission from both the item view holder below 202 * and above from where the divider would draw itself or just needs permission from 203 * one or the other before drawing itself. 204 */ 205 @DividerCondition 206 public int getDividerCondition() { 207 return mDividerCondition; 208 } 209} 210