Home | History | Annotate | Download | only in setupwizardlib
      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          */
     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      * @deprecated Use {@link #DividerItemDecoration(android.content.Context)}
     71      */
     72     @Deprecated
     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 
     85     public DividerItemDecoration() {
     86     }
     87 
     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
    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
    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 
    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      */
    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      */
    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      */
    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      */
    202     public Drawable getDivider() {
    203         return mDivider;
    204     }
    205 
    206     /**
    207      * Sets the divider height, in pixels.
    208      */
    209     public void setDividerHeight(int dividerHeight) {
    210         mDividerHeight = dividerHeight;
    211     }
    212 
    213     /**
    214      * Gets the divider height, in pixels.
    215      */
    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      */
    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
    235     public int getDividerCondition() {
    236         return mDividerCondition;
    237     }
    238 }
    239