| /* |
| * Copyright (C) 2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.setupwizardlib; |
| |
| import android.content.Context; |
| import android.content.res.TypedArray; |
| import android.graphics.Canvas; |
| import android.graphics.Rect; |
| import android.graphics.drawable.Drawable; |
| import androidx.annotation.IntDef; |
| import androidx.core.view.ViewCompat; |
| import androidx.recyclerview.widget.RecyclerView; |
| import android.view.View; |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| |
| /** |
| * An {@link androidx.recyclerview.widget.RecyclerView.ItemDecoration} for RecyclerView to draw |
| * dividers between items. This ItemDecoration will draw the drawable specified by {@link |
| * #setDivider(android.graphics.drawable.Drawable)} as the divider in between each item by default, |
| * and the behavior of whether the divider is shown can be customized by subclassing {@link |
| * com.android.setupwizardlib.DividerItemDecoration.DividedViewHolder}. |
| * |
| * <p>Modified from v14 PreferenceFragment.DividerDecoration. |
| */ |
| public class DividerItemDecoration extends RecyclerView.ItemDecoration { |
| |
| /* static section */ |
| |
| /** |
| * An interface to be implemented by a {@link RecyclerView.ViewHolder} which controls whether |
| * dividers should be shown above and below that item. |
| */ |
| public interface DividedViewHolder { |
| |
| /** |
| * Returns whether divider is allowed above this item. A divider will be shown only if both |
| * items immediately above and below it allows this divider. |
| */ |
| boolean isDividerAllowedAbove(); |
| |
| /** |
| * Returns whether divider is allowed below this item. A divider will be shown only if both |
| * items immediately above and below it allows this divider. |
| */ |
| boolean isDividerAllowedBelow(); |
| } |
| |
| @Retention(RetentionPolicy.SOURCE) |
| @IntDef({DIVIDER_CONDITION_EITHER, DIVIDER_CONDITION_BOTH}) |
| public @interface DividerCondition {} |
| |
| public static final int DIVIDER_CONDITION_EITHER = 0; |
| public static final int DIVIDER_CONDITION_BOTH = 1; |
| |
| /** @deprecated Use {@link #DividerItemDecoration(android.content.Context)} */ |
| @Deprecated |
| public static DividerItemDecoration getDefault(Context context) { |
| return new DividerItemDecoration(context); |
| } |
| |
| /* non-static section */ |
| |
| private Drawable divider; |
| private int dividerHeight; |
| private int dividerIntrinsicHeight; |
| @DividerCondition private int dividerCondition; |
| |
| public DividerItemDecoration() {} |
| |
| public DividerItemDecoration(Context context) { |
| final TypedArray a = context.obtainStyledAttributes(R.styleable.SuwDividerItemDecoration); |
| final Drawable divider = |
| a.getDrawable(R.styleable.SuwDividerItemDecoration_android_listDivider); |
| final int dividerHeight = |
| a.getDimensionPixelSize(R.styleable.SuwDividerItemDecoration_android_dividerHeight, 0); |
| @DividerCondition |
| final int dividerCondition = |
| a.getInt( |
| R.styleable.SuwDividerItemDecoration_suwDividerCondition, DIVIDER_CONDITION_EITHER); |
| a.recycle(); |
| |
| setDivider(divider); |
| setDividerHeight(dividerHeight); |
| setDividerCondition(dividerCondition); |
| } |
| |
| @Override |
| public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { |
| if (divider == null) { |
| return; |
| } |
| final int childCount = parent.getChildCount(); |
| final int width = parent.getWidth(); |
| final int dividerHeight = this.dividerHeight != 0 ? this.dividerHeight : dividerIntrinsicHeight; |
| for (int childViewIndex = 0; childViewIndex < childCount; childViewIndex++) { |
| final View view = parent.getChildAt(childViewIndex); |
| if (shouldDrawDividerBelow(view, parent)) { |
| final int top = (int) ViewCompat.getY(view) + view.getHeight(); |
| divider.setBounds(0, top, width, top + dividerHeight); |
| divider.draw(c); |
| } |
| } |
| } |
| |
| @Override |
| public void getItemOffsets( |
| Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { |
| if (shouldDrawDividerBelow(view, parent)) { |
| outRect.bottom = dividerHeight != 0 ? dividerHeight : dividerIntrinsicHeight; |
| } |
| } |
| |
| private boolean shouldDrawDividerBelow(View view, RecyclerView parent) { |
| final RecyclerView.ViewHolder holder = parent.getChildViewHolder(view); |
| final int index = holder.getLayoutPosition(); |
| final int lastItemIndex = parent.getAdapter().getItemCount() - 1; |
| if (isDividerAllowedBelow(holder)) { |
| if (dividerCondition == DIVIDER_CONDITION_EITHER) { |
| // Draw the divider without consulting the next item if we only |
| // need permission for either above or below. |
| return true; |
| } |
| } else if (dividerCondition == DIVIDER_CONDITION_BOTH || index == lastItemIndex) { |
| // Don't draw if the current view holder doesn't allow drawing below |
| // and the current theme requires permission for both the item below and above. |
| // Also, if this is the last item, there is no item below to ask permission |
| // for whether to draw a divider above, so don't draw it. |
| return false; |
| } |
| // Require permission from index below to draw the divider. |
| if (index < lastItemIndex) { |
| final RecyclerView.ViewHolder nextHolder = parent.findViewHolderForLayoutPosition(index + 1); |
| if (!isDividerAllowedAbove(nextHolder)) { |
| // Don't draw if the next view holder doesn't allow drawing above |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Whether a divider is allowed above the view holder. The allowed values will be combined |
| * according to {@link #getDividerCondition()}. The default implementation delegates to {@link |
| * com.android.setupwizardlib.DividerItemDecoration.DividedViewHolder}, or simply allows the |
| * divider if the view holder doesn't implement {@code DividedViewHolder}. Subclasses can override |
| * this to give more information to decide whether a divider should be drawn. |
| * |
| * @return True if divider is allowed above this view holder. |
| */ |
| protected boolean isDividerAllowedAbove(RecyclerView.ViewHolder viewHolder) { |
| return !(viewHolder instanceof DividedViewHolder) |
| || ((DividedViewHolder) viewHolder).isDividerAllowedAbove(); |
| } |
| |
| /** |
| * Whether a divider is allowed below the view holder. The allowed values will be combined |
| * according to {@link #getDividerCondition()}. The default implementation delegates to {@link |
| * com.android.setupwizardlib.DividerItemDecoration.DividedViewHolder}, or simply allows the |
| * divider if the view holder doesn't implement {@code DividedViewHolder}. Subclasses can override |
| * this to give more information to decide whether a divider should be drawn. |
| * |
| * @return True if divider is allowed below this view holder. |
| */ |
| protected boolean isDividerAllowedBelow(RecyclerView.ViewHolder viewHolder) { |
| return !(viewHolder instanceof DividedViewHolder) |
| || ((DividedViewHolder) viewHolder).isDividerAllowedBelow(); |
| } |
| |
| /** Sets the drawable to be used as the divider. */ |
| public void setDivider(Drawable divider) { |
| if (divider != null) { |
| dividerIntrinsicHeight = divider.getIntrinsicHeight(); |
| } else { |
| dividerIntrinsicHeight = 0; |
| } |
| this.divider = divider; |
| } |
| |
| /** Gets the drawable currently used as the divider. */ |
| public Drawable getDivider() { |
| return divider; |
| } |
| |
| /** Sets the divider height, in pixels. */ |
| public void setDividerHeight(int dividerHeight) { |
| this.dividerHeight = dividerHeight; |
| } |
| |
| /** Gets the divider height, in pixels. */ |
| public int getDividerHeight() { |
| return dividerHeight; |
| } |
| |
| /** |
| * Sets whether the divider needs permission from both the item view holder below and above from |
| * where the divider would draw itself or just needs permission from one or the other before |
| * drawing itself. |
| */ |
| public void setDividerCondition(@DividerCondition int dividerCondition) { |
| this.dividerCondition = dividerCondition; |
| } |
| |
| /** |
| * Gets whether the divider needs permission from both the item view holder below and above from |
| * where the divider would draw itself or just needs permission from one or the other before |
| * drawing itself. |
| */ |
| @DividerCondition |
| public int getDividerCondition() { |
| return dividerCondition; |
| } |
| } |