• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.setupwizardlib;
18 
19 import android.content.Context;
20 import android.content.res.TypedArray;
21 import android.graphics.Canvas;
22 import android.graphics.Rect;
23 import android.graphics.drawable.Drawable;
24 import android.support.annotation.IntDef;
25 import android.support.v4.view.ViewCompat;
26 import android.support.v7.widget.RecyclerView;
27 import android.view.View;
28 
29 import java.lang.annotation.Retention;
30 import 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  */
41 public 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          */
isDividerAllowedAbove()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          */
isDividerAllowedBelow()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      * @deprecated Use {@link #DividerItemDecoration(android.content.Context)}
71      */
72     @Deprecated
getDefault(Context context)73     public static DividerItemDecoration getDefault(Context context) {
74         return new DividerItemDecoration(context);
75     }
76 
77     /* non-static section */
78 
79     private Drawable mDivider;
80     private int mDividerHeight;
81     private int mDividerIntrinsicHeight;
82     @DividerCondition
83     private int mDividerCondition;
84 
DividerItemDecoration()85     public DividerItemDecoration() {
86     }
87 
DividerItemDecoration(Context context)88     public DividerItemDecoration(Context context) {
89         final TypedArray a = context.obtainStyledAttributes(R.styleable.SuwDividerItemDecoration);
90         final Drawable divider = a.getDrawable(
91                 R.styleable.SuwDividerItemDecoration_android_listDivider);
92         final int dividerHeight = a.getDimensionPixelSize(
93                 R.styleable.SuwDividerItemDecoration_android_dividerHeight, 0);
94         @DividerCondition final int dividerCondition = a.getInt(
95                 R.styleable.SuwDividerItemDecoration_suwDividerCondition,
96                 DIVIDER_CONDITION_EITHER);
97         a.recycle();
98 
99         setDivider(divider);
100         setDividerHeight(dividerHeight);
101         setDividerCondition(dividerCondition);
102     }
103 
104     @Override
onDraw(Canvas c, RecyclerView parent, RecyclerView.State state)105     public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
106         if (mDivider == null) {
107             return;
108         }
109         final int childCount = parent.getChildCount();
110         final int width = parent.getWidth();
111         final int dividerHeight = mDividerHeight != 0 ? mDividerHeight : mDividerIntrinsicHeight;
112         for (int childViewIndex = 0; childViewIndex < childCount; childViewIndex++) {
113             final View view = parent.getChildAt(childViewIndex);
114             if (shouldDrawDividerBelow(view, parent)) {
115                 final int top = (int) ViewCompat.getY(view) + view.getHeight();
116                 mDivider.setBounds(0, top, width, top + dividerHeight);
117                 mDivider.draw(c);
118             }
119         }
120     }
121 
122     @Override
getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state)123     public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
124             RecyclerView.State state) {
125         if (shouldDrawDividerBelow(view, parent)) {
126             outRect.bottom = mDividerHeight != 0 ? mDividerHeight : mDividerIntrinsicHeight;
127         }
128     }
129 
shouldDrawDividerBelow(View view, RecyclerView parent)130     private boolean shouldDrawDividerBelow(View view, RecyclerView parent) {
131         final RecyclerView.ViewHolder holder = parent.getChildViewHolder(view);
132         final int index = holder.getLayoutPosition();
133         final int lastItemIndex = parent.getAdapter().getItemCount() - 1;
134         if (isDividerAllowedBelow(holder)) {
135             if (mDividerCondition == DIVIDER_CONDITION_EITHER) {
136                 // Draw the divider without consulting the next item if we only
137                 // need permission for either above or below.
138                 return true;
139             }
140         } else if (mDividerCondition == DIVIDER_CONDITION_BOTH || index == lastItemIndex) {
141             // Don't draw if the current view holder doesn't allow drawing below
142             // and the current theme requires permission for both the item below and above.
143             // Also, if this is the last item, there is no item below to ask permission
144             // for whether to draw a divider above, so don't draw it.
145             return false;
146         }
147         // Require permission from index below to draw the divider.
148         if (index < lastItemIndex) {
149             final RecyclerView.ViewHolder nextHolder =
150                     parent.findViewHolderForLayoutPosition(index + 1);
151             if (!isDividerAllowedAbove(nextHolder)) {
152                 // Don't draw if the next view holder doesn't allow drawing above
153                 return false;
154             }
155         }
156         return true;
157     }
158 
159     /**
160      * Whether a divider is allowed above the view holder. The allowed values will be combined
161      * according to {@link #getDividerCondition()}. The default implementation delegates to
162      * {@link com.android.setupwizardlib.DividerItemDecoration.DividedViewHolder}, or simply allows
163      * the divider if the view holder doesn't implement {@code DividedViewHolder}. Subclasses can
164      * override this to give more information to decide whether a divider should be drawn.
165      *
166      * @return True if divider is allowed above this view holder.
167      */
isDividerAllowedAbove(RecyclerView.ViewHolder viewHolder)168     protected boolean isDividerAllowedAbove(RecyclerView.ViewHolder viewHolder) {
169         return !(viewHolder instanceof DividedViewHolder)
170                 || ((DividedViewHolder) viewHolder).isDividerAllowedAbove();
171     }
172 
173     /**
174      * Whether a divider is allowed below the view holder. The allowed values will be combined
175      * according to {@link #getDividerCondition()}. The default implementation delegates to
176      * {@link com.android.setupwizardlib.DividerItemDecoration.DividedViewHolder}, or simply allows
177      * the divider if the view holder doesn't implement {@code DividedViewHolder}. Subclasses can
178      * override this to give more information to decide whether a divider should be drawn.
179      *
180      * @return True if divider is allowed below this view holder.
181      */
isDividerAllowedBelow(RecyclerView.ViewHolder viewHolder)182     protected boolean isDividerAllowedBelow(RecyclerView.ViewHolder viewHolder) {
183         return !(viewHolder instanceof DividedViewHolder)
184                 || ((DividedViewHolder) viewHolder).isDividerAllowedBelow();
185     }
186 
187     /**
188      * Sets the drawable to be used as the divider.
189      */
setDivider(Drawable divider)190     public void setDivider(Drawable divider) {
191         if (divider != null) {
192             mDividerIntrinsicHeight = divider.getIntrinsicHeight();
193         } else {
194             mDividerIntrinsicHeight = 0;
195         }
196         mDivider = divider;
197     }
198 
199     /**
200      * Gets the drawable currently used as the divider.
201      */
getDivider()202     public Drawable getDivider() {
203         return mDivider;
204     }
205 
206     /**
207      * Sets the divider height, in pixels.
208      */
setDividerHeight(int dividerHeight)209     public void setDividerHeight(int dividerHeight) {
210         mDividerHeight = dividerHeight;
211     }
212 
213     /**
214      * Gets the divider height, in pixels.
215      */
getDividerHeight()216     public int getDividerHeight() {
217         return mDividerHeight;
218     }
219 
220     /**
221      * Sets whether the divider needs permission from both the item view holder below
222      * and above from where the divider would draw itself or just needs permission from
223      * one or the other before drawing itself.
224      */
setDividerCondition(@ividerCondition int dividerCondition)225     public void setDividerCondition(@DividerCondition int dividerCondition) {
226         mDividerCondition = dividerCondition;
227     }
228 
229     /**
230      * Gets whether the divider needs permission from both the item view holder below
231      * and above from where the divider would draw itself or just needs permission from
232      * one or the other before drawing itself.
233      */
234     @DividerCondition
getDividerCondition()235     public int getDividerCondition() {
236         return mDividerCondition;
237     }
238 }
239