content: /**
   * Copyright 2013 Joan Zapata
   * <p/>
   * 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
   * <p/>
   * http://www.apache.org/licenses/LICENSE-2.0
   * <p/>
   * 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.chad.library.adapter.base;
  
  import android.animation.Animator;
  import android.content.Context;
  import android.support.annotation.IntDef;
  import android.support.annotation.IntRange;
  import android.support.annotation.NonNull;
  import android.support.v7.widget.GridLayoutManager;
  import android.support.v7.widget.RecyclerView;
  import android.support.v7.widget.RecyclerView.LayoutParams;
  import android.support.v7.widget.StaggeredGridLayoutManager;
  import android.view.LayoutInflater;
  import android.view.View;
  import android.view.ViewGroup;
  import android.view.animation.Interpolator;
  import android.view.animation.LinearInterpolator;
  import android.widget.FrameLayout;
  import android.widget.LinearLayout;
  
  import com.chad.library.adapter.base.animation.AlphaInAnimation;
  import com.chad.library.adapter.base.animation.BaseAnimation;
  import com.chad.library.adapter.base.animation.ScaleInAnimation;
  import com.chad.library.adapter.base.animation.SlideInBottomAnimation;
  import com.chad.library.adapter.base.animation.SlideInLeftAnimation;
  import com.chad.library.adapter.base.animation.SlideInRightAnimation;
  import com.chad.library.adapter.base.entity.IExpandable;
  import com.chad.library.adapter.base.loadmore.LoadMoreView;
  import com.chad.library.adapter.base.loadmore.SimpleLoadMoreView;
  
  import java.lang.annotation.Retention;
  import java.lang.annotation.RetentionPolicy;
  import java.util.ArrayList;
  import java.util.List;
  
  import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
  import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
  
  
  /**
   * https://github.com/CymChad/BaseRecyclerViewAdapterHelper
   */
  public abstract class BaseQuickAdapter<T, K extends BaseViewHolder> extends RecyclerView.Adapter<K> {
  
      //load more
      private boolean mNextLoadEnable = false;
      private boolean mLoadMoreEnable = false;
      private boolean mLoading = false;
      private LoadMoreView mLoadMoreView = new SimpleLoadMoreView();
      private RequestLoadMoreListener mRequestLoadMoreListener;
  
      //Animation
      /**
       * Use with {@link #openLoadAnimation}
       */
      public static final int ALPHAIN = 0x00000001;
      /**
       * Use with {@link #openLoadAnimation}
       */
      public static final int SCALEIN = 0x00000002;
      /**
       * Use with {@link #openLoadAnimation}
       */
      public static final int SLIDEIN_BOTTOM = 0x00000003;
      /**
       * Use with {@link #openLoadAnimation}
       */
      public static final int SLIDEIN_LEFT = 0x00000004;
      /**
       * Use with {@link #openLoadAnimation}
       */
      public static final int SLIDEIN_RIGHT = 0x00000005;
  
      @IntDef({ALPHAIN, SCALEIN, SLIDEIN_BOTTOM, SLIDEIN_LEFT, SLIDEIN_RIGHT})
      @Retention(RetentionPolicy.SOURCE)
      public @interface AnimationType {
      }
  
      private boolean mFirstOnlyEnable = true;
      private boolean mOpenAnimationEnable = false;
      private Interpolator mInterpolator = new LinearInterpolator();
      private int mDuration = 300;
      private int mLastPosition = -1;
  
      private BaseAnimation mCustomAnimation;
      private BaseAnimation mSelectAnimation = new AlphaInAnimation();
      //header footer
      private LinearLayout mHeaderLayout;
      private LinearLayout mFooterLayout;
      //empty
      private FrameLayout mEmptyLayout;
      private boolean mIsUseEmpty = true;
      private boolean mHeadAndEmptyEnable;
      private boolean mFootAndEmptyEnable;
  
      protected static final String TAG = BaseQuickAdapter.class.getSimpleName();
      protected Context mContext;
      protected int mLayoutResId;
      protected LayoutInflater mLayoutInflater;
      protected List<T> mData;
      public static final int HEADER_VIEW = 0x00000111;
      public static final int LOADING_VIEW = 0x00000222;
      public static final int FOOTER_VIEW = 0x00000333;
      public static final int EMPTY_VIEW = 0x00000555;
  
      public void setOnLoadMoreListener(RequestLoadMoreListener requestLoadMoreListener) {
          this.mRequestLoadMoreListener = requestLoadMoreListener;
          mNextLoadEnable = true;
          mLoadMoreEnable = true;
          mLoading = false;
      }
  
      /**
       * Set custom load more
       *
       * @param loadingView
       */
      public void setLoadMoreView(LoadMoreView loadingView) {
          this.mLoadMoreView = loadingView;
      }
  
      /**
       * Load more view count
       *
       * @return 0 or 1
       */
      public int getLoadMoreViewCount() {
          if (mRequestLoadMoreListener == null || !mLoadMoreEnable) {
              return 0;
          }
          if (!mNextLoadEnable && mLoadMoreView.isLoadEndMoreGone()) {
              return 0;
          }
          if (mData.size() == 0) {
              return 0;
          }
          return 1;
      }
  
      /**
       * @return Whether the Adapter is actively showing load
       * progress.
       */
      public boolean isLoading() {
          return mLoading;
      }
  
  
      /**
       * Refresh end, no more data
       */
      public void loadMoreEnd() {
          loadMoreEnd(false);
      }
  
      /**
       * Refresh end, no more data
       *
       * @param gone if true gone the load more view
       */
      public void loadMoreEnd(boolean gone) {
          if (getLoadMoreViewCount() == 0) {
              return;
          }
          mLoading = false;
          mNextLoadEnable = false;
          mLoadMoreView.setLoadMoreEndGone(gone);
          if (gone) {
              notifyItemRemoved(getHeaderLayoutCount() + mData.size() + getFooterLayoutCount());
          } else {
              mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_END);
              notifyItemChanged(getHeaderLayoutCount() + mData.size() + getFooterLayoutCount());
          }
      }
  
      /**
       * Refresh complete
       */
      public void loadMoreComplete() {
          if (getLoadMoreViewCount() == 0) {
              return;
          }
          mLoading = false;
          mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_DEFAULT);
          notifyItemChanged(getHeaderLayoutCount() + mData.size() + getFooterLayoutCount());
      }
  
      /**
       * Refresh failed
       */
      public void loadMoreFail() {
          if (getLoadMoreViewCount() == 0) {
              return;
          }
          mLoading = false;
          mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_FAIL);
          notifyItemChanged(getHeaderLayoutCount() + mData.size() + getFooterLayoutCount());
      }
  
      /**
       * Set the enabled state of load more.
       *
       * @param enable True if load more is enabled, false otherwise.
       */
      public void setEnableLoadMore(boolean enable) {
          int oldLoadMoreCount = getLoadMoreViewCount();
          mLoadMoreEnable = enable;
          int newLoadMoreCount = getLoadMoreViewCount();
  
          if (oldLoadMoreCount == 1) {
              if (newLoadMoreCount == 0) {
                  notifyItemRemoved(getHeaderLayoutCount() + mData.size() + getFooterLayoutCount());
              }
          } else {
              if (newLoadMoreCount == 1) {
                  mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_DEFAULT);
                  notifyItemInserted(getHeaderLayoutCount() + mData.size() + getFooterLayoutCount());
              }
          }
      }
  
      /**
       * Returns the enabled status for load more.
       *
       * @return True if load more is enabled, false otherwise.
       */
      public boolean isLoadMoreEnable() {
          return mLoadMoreEnable;
      }
  
      /**
       * Sets the duration of the animation.
       *
       * @param duration The length of the animation, in milliseconds.
       */
      public void setDuration(int duration) {
          mDuration = duration;
      }
  
  
      /**
       * Same as QuickAdapter#QuickAdapter(Context,int) but with
       * some initialization data.
       *
       * @param layoutResId The layout resource id of each item.
       * @param data        A new list is created out of this one to avoid mutable list
       */
      public BaseQuickAdapter(int layoutResId, List<T> data) {
          this.mData = data == null ? new ArrayList<T>() : data;
          if (layoutResId != 0) {
              this.mLayoutResId = layoutResId;
          }
      }
  
      public BaseQuickAdapter(List<T> data) {
          this(0, data);
      }
  
      /**
       * setting up a new instance to data;
       *
       * @param data
       */
      public void setNewData(List<T> data) {
          this.mData = data == null ? new ArrayList<T>() : data;
          if (mRequestLoadMoreListener != null) {
              mNextLoadEnable = true;
              mLoadMoreEnable = true;
              mLoading = false;
              mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_DEFAULT);
          }
          mLastPosition = -1;
          notifyDataSetChanged();
      }
  
  
      /**
       * insert  a item associated with the specified position of adapter
       * @deprecated use {@link #addData(int, Object)} instead
       * @param position
       * @param item
       */
      @Deprecated
      public void add(int position, T item) {
          addData(position,item);
      }
  
      /**
       * add one new data in to certain location
       *
       * @param position
       */
      public void addData(int position, T data) {
          mData.add(position, data);
          notifyItemInserted(position + getHeaderLayoutCount());
          compatibilityDataSizeChanged(1);
      }
  
      /**
       * add one new data
       */
      public void addData(T data) {
          mData.add(data);
          notifyItemInserted(mData.size() + getHeaderLayoutCount());
          compatibilityDataSizeChanged(1);
      }
  
      /**
       * remove the item associated with the specified position of adapter
       *
       * @param position
       */
      public void remove(int position) {
          mData.remove(position);
          notifyItemRemoved(position + getHeaderLayoutCount());
          compatibilityDataSizeChanged(0);
      }
  
      /**
       * change data
       */
      public void setData(int index, T data) {
          mData.set(index, data);
          notifyItemChanged(index + getHeaderLayoutCount());
      }
  
      /**
       * add new data in to certain location
       *
       * @param position
       */
      public void addData(int position, List<T> data) {
          mData.addAll(position, data);
          notifyItemRangeInserted(position + getHeaderLayoutCount(), data.size());
          compatibilityDataSizeChanged(data.size());
      }
  
      /**
       * additional data;
       *
       * @param newData
       */
      public void addData(List<T> newData) {
          this.mData.addAll(newData);
          notifyItemRangeInserted(mData.size() - newData.size() + getHeaderLayoutCount(), newData.size());
          compatibilityDataSizeChanged(newData.size());
      }
  
      /**
       * compatible getLoadMoreViewCount and getEmptyViewCount may change
       *
       * @param size Need compatible data size
       */
      private void compatibilityDataSizeChanged(int size) {
          final int dataSize = mData == null ? 0 : mData.size();
          if (dataSize == size) {
              notifyDataSetChanged();
          }
      }
  
      /**
       * Get the data of list
       *
       * @return
       */
      public List<T> getData() {
          return mData;
      }
  
      /**
       * Get the data item associated with the specified position in the data set.
       *
       * @param position Position of the item whose data we want within the adapter's
       *                 data set.
       * @return The data at the specified position.
       */
      public T getItem(int position) {
          return mData.get(position);
      }
  
      /**
       * if setHeadView will be return 1 if not will be return 0.
       * notice: Deprecated! Use {@link ViewGroup#getChildCount()} of {@link #getHeaderLayout()} to replace.
       *
       * @return
       */
      @Deprecated
      public int getHeaderViewsCount() {
          return getHeaderLayoutCount();
      }
  
      /**
       * if mFooterLayout will be return 1 or not will be return 0.
       * notice: Deprecated! Use {@link ViewGroup#getChildCount()} of {@link #getFooterLayout()} to replace.
       *
       * @return
       */
      @Deprecated
      public int getFooterViewsCount() {
          return getFooterLayoutCount();
      }
  
      /**
       * if addHeaderView will be return 1, if not will be return 0
       */
      public int getHeaderLayoutCount() {
          if (mHeaderLayout == null || mHeaderLayout.getChildCount() == 0) {
              return 0;
          }
          return 1;
      }
  
      /**
       * if addFooterView will be return 1, if not will be return 0
       */
      public int getFooterLayoutCount() {
          if (mFooterLayout == null || mFooterLayout.getChildCount() == 0) {
              return 0;
          }
          return 1;
      }
  
      /**
       * if show empty view will be return 1 or not will be return 0
       *
       * @return
       */
      public int getEmptyViewCount() {
          if (mEmptyLayout == null || mEmptyLayout.getChildCount() == 0) {
              return 0;
          }
          if (!mIsUseEmpty) {
              return 0;
          }
          if (mData.size() != 0) {
              return 0;
          }
          return 1;
      }
  
      @Override
      public int getItemCount() {
          int count;
          if (getEmptyViewCount() == 1) {
              count = 1;
              if (mHeadAndEmptyEnable && getHeaderLayoutCount() != 0) {
                  count++;
              }
              if (mFootAndEmptyEnable && getFooterLayoutCount() != 0) {
                  count++;
              }
          } else {
              count = getHeaderLayoutCount() + mData.size() + getFooterLayoutCount() + getLoadMoreViewCount();
          }
          return count;
      }
  
      @Override
      public int getItemViewType(int position) {
          if (getEmptyViewCount() == 1) {
              boolean header = mHeadAndEmptyEnable && getHeaderLayoutCount() != 0;
              switch (position) {
                  case 0:
                      if (header) {
                          return HEADER_VIEW;
                      } else {
                          return EMPTY_VIEW;
                      }
                  case 1:
                      if (header) {
                          return EMPTY_VIEW;
                      } else {
                          return FOOTER_VIEW;
                      }
                  case 2:
                      return FOOTER_VIEW;
                  default:
                      return EMPTY_VIEW;
              }
          }
          autoLoadMore(position);
          int numHeaders = getHeaderLayoutCount();
          if (position < numHeaders) {
              return HEADER_VIEW;
          } else {
              int adjPosition = position - numHeaders;
              int adapterCount = mData.size();
              if (adjPosition < adapterCount) {
                  return getDefItemViewType(adjPosition);
              } else {
                  adjPosition = adjPosition - adapterCount;
                  int numFooters = getFooterLayoutCount();
                  if (adjPosition < numFooters) {
                      return FOOTER_VIEW;
                  } else {
                      return LOADING_VIEW;
                  }
              }
          }
      }
  
      protected int getDefItemViewType(int position) {
          return super.getItemViewType(position);
      }
  
      @Override
      public K onCreateViewHolder(ViewGroup parent, int viewType) {
          K baseViewHolder = null;
          this.mContext = parent.getContext();
          this.mLayoutInflater = LayoutInflater.from(mContext);
          switch (viewType) {
              case LOADING_VIEW:
                  baseViewHolder = getLoadingView(parent);
                  break;
              case HEADER_VIEW:
                  baseViewHolder = createBaseViewHolder(mHeaderLayout);
                  break;
              case EMPTY_VIEW:
                  baseViewHolder = createBaseViewHolder(mEmptyLayout);
                  break;
              case FOOTER_VIEW:
                  baseViewHolder = createBaseViewHolder(mFooterLayout);
                  break;
              default:
                  baseViewHolder = onCreateDefViewHolder(parent, viewType);
          }
          return baseViewHolder;
  
      }
  
  
      private K getLoadingView(ViewGroup parent) {
          View view = getItemView(mLoadMoreView.getLayoutId(), parent);
          K holder = createBaseViewHolder(view);
          holder.itemView.setOnClickListener(new View.OnClickListener() {
              @Override
              public void onClick(View v) {
                  if (mLoadMoreView.getLoadMoreStatus() == LoadMoreView.STATUS_FAIL) {
                      mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_DEFAULT);
                      notifyItemChanged(getHeaderLayoutCount() + mData.size() + getFooterLayoutCount());
                  }
              }
          });
          return holder;
      }
  
      /**
       * Called when a view created by this adapter has been attached to a window.
       * simple to solve item will layout using all
       * {@link #setFullSpan(RecyclerView.ViewHolder)}
       *
       * @param holder
       */
      @Override
      public void onViewAttachedToWindow(K holder) {
          super.onViewAttachedToWindow(holder);
          int type = holder.getItemViewType();
          if (type == EMPTY_VIEW || type == HEADER_VIEW || type == FOOTER_VIEW || type == LOADING_VIEW) {
              setFullSpan(holder);
          } else {
              addAnimation(holder);
          }
      }
  
      /**
       * When set to true, the item will layout using all span area. That means, if orientation
       * is vertical, the view will have full width; if orientation is horizontal, the view will
       * have full height.
       * if the hold view use StaggeredGridLayoutManager they should using all span area
       *
       * @param holder True if this item should traverse all spans.
       */
      protected void setFullSpan(RecyclerView.ViewHolder holder) {
          if (holder.itemView.getLayoutParams() instanceof StaggeredGridLayoutManager.LayoutParams) {
              StaggeredGridLayoutManager.LayoutParams params = (StaggeredGridLayoutManager.LayoutParams) holder.itemView.getLayoutParams();
              params.setFullSpan(true);
          }
      }
  
      @Override
      public void onAttachedToRecyclerView(final RecyclerView recyclerView) {
          super.onAttachedToRecyclerView(recyclerView);
          RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
          if (manager instanceof GridLayoutManager) {
              final GridLayoutManager gridManager = ((GridLayoutManager) manager);
              gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
                  @Override
                  public int getSpanSize(int position) {
                      int type = getItemViewType(position);
                      if (mSpanSizeLookup == null)
                          return (type == EMPTY_VIEW || type == HEADER_VIEW || type == FOOTER_VIEW || type == LOADING_VIEW) ? gridManager.getSpanCount() : 1;
                      else
                          return (type == EMPTY_VIEW || type == HEADER_VIEW || type == FOOTER_VIEW || type == LOADING_VIEW) ? gridManager.getSpanCount() : mSpanSizeLookup.getSpanSize(gridManager, position - getHeaderLayoutCount());
                  }
              });
          }
      }
  
      private boolean flag = true;
      private SpanSizeLookup mSpanSizeLookup;
  
      public interface SpanSizeLookup {
          int getSpanSize(GridLayoutManager gridLayoutManager, int position);
      }
  
      /**
       * @param spanSizeLookup instance to be used to query number of spans occupied by each item
       */
      public void setSpanSizeLookup(SpanSizeLookup spanSizeLookup) {
          this.mSpanSizeLookup = spanSizeLookup;
      }
  
      @Override
      public void onBindViewHolder(K holder, int position, List<Object> payloads) {
          int viewType = holder.getItemViewType();
  
          switch (viewType) {
              case 0:
                  convert(holder, mData.get(holder.getLayoutPosition() - getHeaderLayoutCount()));
                  break;
              case LOADING_VIEW:
                  mLoadMoreView.convert(holder);
                  break;
              case HEADER_VIEW:
                  break;
              case EMPTY_VIEW:
                  break;
              case FOOTER_VIEW:
                  break;
              default:
                  convert(holder, mData.get(holder.getLayoutPosition() - getHeaderLayoutCount()));
                  break;
          }
      }
  
      /**
       * To bind different types of holder and solve different the bind events
       *
       * @param holder
       * @param positions
       * @see #getDefItemViewType(int)
       */
      @Override
      public void onBindViewHolder(K holder, int positions) {
          int viewType = holder.getItemViewType();
  
          switch (viewType) {
              case 0:
                  convert(holder, mData.get(holder.getLayoutPosition() - getHeaderLayoutCount()));
                  break;
              case LOADING_VIEW:
                  mLoadMoreView.convert(holder);
                  break;
              case HEADER_VIEW:
                  break;
              case EMPTY_VIEW:
                  break;
              case FOOTER_VIEW:
                  break;
              default:
                  convert(holder, mData.get(holder.getLayoutPosition() - getHeaderLayoutCount()));
                  break;
          }
      }
  
  
  
      protected K onCreateDefViewHolder(ViewGroup parent, int viewType) {
          return createBaseViewHolder(parent, mLayoutResId);
      }
  
      protected K createBaseViewHolder(ViewGroup parent, int layoutResId) {
          return createBaseViewHolder(getItemView(layoutResId, parent));
      }
  
      /**
       * if you want to use subclass of BaseViewHolder in the adapter,
       * you must override the method to create new ViewHolder.
       *
       * @param view view
       * @return new ViewHolder
       */
      protected K createBaseViewHolder(View view) {
          return (K) new BaseViewHolder(view);
      }
  
      /**
       * Return root layout of header
       */
      public LinearLayout getHeaderLayout() {
          return mHeaderLayout;
      }
  
      /**
       * Return root layout of footer
       */
      public LinearLayout getFooterLayout() {
          return mFooterLayout;
      }
  
      /**
       * Append header to the rear of the mHeaderLayout.
       *
       * @param header
       */
      public void addHeaderView(View header) {
          addHeaderView(header, -1);
      }
  
      /**
       * Add header view to mHeaderLayout and set header view position in mHeaderLayout.
       * When index = -1 or index >= child count in mHeaderLayout,
       * the effect of this method is the same as that of {@link #addHeaderView(View)}.
       *
       * @param header
       * @param index  the position in mHeaderLayout of this header.
       *               When index = -1 or index >= child count in mHeaderLayout,
       *               the effect of this method is the same as that of {@link #addHeaderView(View)}.
       */
      public void addHeaderView(View header, int index) {
          addHeaderView(header, index, LinearLayout.VERTICAL);
      }
  
      /**
       * @param header
       * @param index
       * @param orientation
       */
      public void addHeaderView(View header, int index, int orientation) {
          if (mHeaderLayout == null) {
              mHeaderLayout = new LinearLayout(header.getContext());
              if (orientation == LinearLayout.VERTICAL) {
                  mHeaderLayout.setOrientation(LinearLayout.VERTICAL);
                  mHeaderLayout.setLayoutParams(new LayoutParams(MATCH_PARENT, WRAP_CONTENT));
              } else {
                  mHeaderLayout.setOrientation(LinearLayout.HORIZONTAL);
                  mHeaderLayout.setLayoutParams(new LayoutParams(WRAP_CONTENT, MATCH_PARENT));
              }
          }
          index = index >= mHeaderLayout.getChildCount() ? -1 : index;
          mHeaderLayout.addView(header, index);
          if (mHeaderLayout.getChildCount() == 1) {
              int position = getHeaderViewPosition();
              if (position != -1) {
                  notifyItemInserted(position);
              }
          }
      }
  
      public void setHeaderView(View header) {
          setHeaderView(header, 0, LinearLayout.VERTICAL);
      }
  
      public void setHeaderView(View header, int index) {
          setHeaderView(header, index, LinearLayout.VERTICAL);
      }
  
      public void setHeaderView(View header, int index, int orientation) {
          if (mHeaderLayout == null || mHeaderLayout.getChildCount() <= index) {
              addHeaderView(header, index, orientation);
          } else {
              mHeaderLayout.removeViewAt(index);
              mHeaderLayout.addView(header, index);
          }
      }
  
      /**
       * Append footer to the rear of the mFooterLayout.
       *
       * @param footer
       */
      public void addFooterView(View footer) {
          addFooterView(footer, -1, LinearLayout.VERTICAL);
      }
  
      public void addFooterView(View footer, int index) {
          addFooterView(footer, index, LinearLayout.VERTICAL);
      }
  
      /**
       * Add footer view to mFooterLayout and set footer view position in mFooterLayout.
       * When index = -1 or index >= child count in mFooterLayout,
       * the effect of this method is the same as that of {@link #addFooterView(View)}.
       *
       * @param footer
       * @param index  the position in mFooterLayout of this footer.
       *               When index = -1 or index >= child count in mFooterLayout,
       *               the effect of this method is the same as that of {@link #addFooterView(View)}.
       */
      public void addFooterView(View footer, int index, int orientation) {
          if (mFooterLayout == null) {
              mFooterLayout = new LinearLayout(footer.getContext());
              if (orientation == LinearLayout.VERTICAL) {
                  mFooterLayout.setOrientation(LinearLayout.VERTICAL);
                  mFooterLayout.setLayoutParams(new LayoutParams(MATCH_PARENT, WRAP_CONTENT));
              } else {
                  mFooterLayout.setOrientation(LinearLayout.HORIZONTAL);
                  mFooterLayout.setLayoutParams(new LayoutParams(WRAP_CONTENT, MATCH_PARENT));
              }
          }
          index = index >= mFooterLayout.getChildCount() ? -1 : index;
          mFooterLayout.addView(footer, index);
          if (mFooterLayout.getChildCount() == 1) {
              int position = getFooterViewPosition();
              if (position != -1) {
                  notifyItemInserted(position);
              }
          }
      }
  
      /**
       * remove header view from mHeaderLayout.
       * When the child count of mHeaderLayout is 0, mHeaderLayout will be set to null.
       *
       * @param header
       */
      public void removeHeaderView(View header) {
          if (getHeaderLayoutCount() == 0) return;
  
          mHeaderLayout.removeView(header);
          if (mHeaderLayout.getChildCount() == 0) {
              int position = getHeaderViewPosition();
              if (position != -1) {
                  notifyItemRemoved(position);
              }
          }
      }
  
      /**
       * remove footer view from mFooterLayout,
       * When the child count of mFooterLayout is 0, mFooterLayout will be set to null.
       *
       * @param footer
       */
      public void removeFooterView(View footer) {
          if (getFooterLayoutCount() == 0) return;
  
          mFooterLayout.removeView(footer);
          if (mFooterLayout.getChildCount() == 0) {
              int position = getFooterViewPosition();
              if (position != -1) {
                  notifyItemRemoved(position);
              }
          }
      }
  
      /**
       * remove all header view from mHeaderLayout and set null to mHeaderLayout
       */
      public void removeAllHeaderView() {
          if (getHeaderLayoutCount() == 0) return;
  
          mHeaderLayout.removeAllViews();
          int position = getHeaderViewPosition();
          if (position != -1) {
              notifyItemRemoved(position);
          }
      }
  
      /**
       * remove all footer view from mFooterLayout and set null to mFooterLayout
       */
      public void removeAllFooterView() {
          if (getFooterLayoutCount() == 0) return;
  
          mFooterLayout.removeAllViews();
          int position = getFooterViewPosition();
          if (position != -1) {
              notifyItemRemoved(position);
          }
      }
  
      private int getHeaderViewPosition() {
          //Return to header view notify position
          if (getEmptyViewCount() == 1) {
              if (mHeadAndEmptyEnable) {
                  return 0;
              }
          } else {
              return 0;
          }
          return -1;
      }
  
      private int getFooterViewPosition() {
          //Return to footer view notify position
          if (getEmptyViewCount() == 1) {
              int position = 1;
              if (mHeadAndEmptyEnable && getHeaderLayoutCount() != 0) {
                  position++;
              }
              if (mFootAndEmptyEnable) {
                  return position;
              }
          } else {
              return getHeaderLayoutCount() + mData.size();
          }
          return -1;
      }
  
      public void setEmptyView(View emptyView) {
          boolean insert = false;
          if (mEmptyLayout == null) {
              mEmptyLayout = new FrameLayout(emptyView.getContext());
              final LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
              final ViewGroup.LayoutParams lp = emptyView.getLayoutParams();
              if (lp != null) {
                  layoutParams.width = lp.width;
                  layoutParams.height = lp.height;
              }
              mEmptyLayout.setLayoutParams(layoutParams);
              insert = true;
          }
          mEmptyLayout.removeAllViews();
          mEmptyLayout.addView(emptyView);
          mIsUseEmpty = true;
          if (insert) {
              if (getEmptyViewCount() == 1) {
                  int position = 0;
                  if (mHeadAndEmptyEnable && getHeaderLayoutCount() != 0) {
                      position++;
                  }
                  notifyItemInserted(position);
              }
          }
      }
  
      /**
       * Call before {@link RecyclerView#setAdapter(RecyclerView.Adapter)}
       *
       * @param isHeadAndEmpty false will not show headView if the data is empty true will show emptyView and headView
       */
      public void setHeaderAndEmpty(boolean isHeadAndEmpty) {
          setHeaderFooterEmpty(isHeadAndEmpty, false);
      }
  
      /**
       * set emptyView show if adapter is empty and want to show headview and footview
       * Call before {@link RecyclerView#setAdapter(RecyclerView.Adapter)}
       *
       * @param isHeadAndEmpty
       * @param isFootAndEmpty
       */
      public void setHeaderFooterEmpty(boolean isHeadAndEmpty, boolean isFootAndEmpty) {
          mHeadAndEmptyEnable = isHeadAndEmpty;
          mFootAndEmptyEnable = isFootAndEmpty;
      }
  
      /**
       * Set whether to use empty view
       *
       * @param isUseEmpty
       */
      public void isUseEmpty(boolean isUseEmpty) {
          mIsUseEmpty = isUseEmpty;
      }
  
      /**
       * When the current adapter is empty, the BaseQuickAdapter can display a special view
       * called the empty view. The empty view is used to provide feedback to the user
       * that no data is available in this AdapterView.
       *
       * @return The view to show if the adapter is empty.
       */
      public View getEmptyView() {
          return mEmptyLayout;
      }
  
      private int mAutoLoadMoreSize = 1;
  
      public void setAutoLoadMoreSize(int autoLoadMoreSize) {
          if (autoLoadMoreSize > 1) {
              mAutoLoadMoreSize = autoLoadMoreSize;
          }
      }
  
      private void autoLoadMore(int position) {
          if (getLoadMoreViewCount() == 0) {
              return;
          }
          if (position < getItemCount() - mAutoLoadMoreSize) {
              return;
          }
          if (mLoadMoreView.getLoadMoreStatus() != LoadMoreView.STATUS_DEFAULT) {
              return;
          }
          mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_LOADING);
          if (!mLoading) {
              mLoading = true;
              mRequestLoadMoreListener.onLoadMoreRequested();
          }
      }
  
  
      /**
       * add animation when you want to show time
       *
       * @param holder
       */
      private void addAnimation(RecyclerView.ViewHolder holder) {
          if (mOpenAnimationEnable) {
              if (!mFirstOnlyEnable || holder.getLayoutPosition() > mLastPosition) {
                  BaseAnimation animation = null;
                  if (mCustomAnimation != null) {
                      animation = mCustomAnimation;
                  } else {
                      animation = mSelectAnimation;
                  }
                  for (Animator anim : animation.getAnimators(holder.itemView)) {
                      startAnim(anim, holder.getLayoutPosition());
                  }
                  mLastPosition = holder.getLayoutPosition();
              }
          }
      }
  
      /**
       * set anim to start when loading
       *
       * @param anim
       * @param index
       */
      protected void startAnim(Animator anim, int index) {
          anim.setDuration(mDuration).start();
          anim.setInterpolator(mInterpolator);
      }
  
      /**
       * @param layoutResId ID for an XML layout resource to load
       * @param parent      Optional view to be the parent of the generated hierarchy or else simply an object that
       *                    provides a set of LayoutParams values for root of the returned
       *                    hierarchy
       * @return view will be return
       */
      protected View getItemView(int layoutResId, ViewGroup parent) {
          return mLayoutInflater.inflate(layoutResId, parent, false);
      }
  
  
      public interface RequestLoadMoreListener {
  
          void onLoadMoreRequested();
  
      }
  
  
      /**
       * Set the view animation type.
       *
       * @param animationType One of {@link #ALPHAIN}, {@link #SCALEIN}, {@link #SLIDEIN_BOTTOM}, {@link #SLIDEIN_LEFT}, {@link #SLIDEIN_RIGHT}.
       */
      public void openLoadAnimation(@AnimationType int animationType) {
          this.mOpenAnimationEnable = true;
          mCustomAnimation = null;
          switch (animationType) {
              case ALPHAIN:
                  mSelectAnimation = new AlphaInAnimation();
                  break;
              case SCALEIN:
                  mSelectAnimation = new ScaleInAnimation();
                  break;
              case SLIDEIN_BOTTOM:
                  mSelectAnimation = new SlideInBottomAnimation();
                  break;
              case SLIDEIN_LEFT:
                  mSelectAnimation = new SlideInLeftAnimation();
                  break;
              case SLIDEIN_RIGHT:
                  mSelectAnimation = new SlideInRightAnimation();
                  break;
              default:
                  break;
          }
      }
  
      /**
       * Set Custom ObjectAnimator
       *
       * @param animation ObjectAnimator
       */
      public void openLoadAnimation(BaseAnimation animation) {
          this.mOpenAnimationEnable = true;
          this.mCustomAnimation = animation;
      }
  
      /**
       * To open the animation when loading
       */
      public void openLoadAnimation() {
          this.mOpenAnimationEnable = true;
      }
  
      /**
       * {@link #addAnimation(RecyclerView.ViewHolder)}
       *
       * @param firstOnly true just show anim when first loading false show anim when load the data every time
       */
      public void isFirstOnly(boolean firstOnly) {
          this.mFirstOnlyEnable = firstOnly;
      }
  
      /**
       * Implement this method and use the helper to adapt the view to the given item.
       *
       * @param helper A fully initialized helper.
       * @param item   The item that needs to be displayed.
       */
      protected abstract void convert(K helper, T item);
  
  
  
      /**
       * Get the row id associated with the specified position in the list.
       *
       * @param position The position of the item within the adapter's data set whose row id we want.
       * @return The id of the item at the specified position.
       */
      @Override
      public long getItemId(int position) {
          return position;
      }
  
      private int recursiveExpand(int position, @NonNull List list) {
          int count = 0;
          int pos = position + list.size() - 1;
          for (int i = list.size() - 1; i >= 0; i--, pos--) {
              if (list.get(i) instanceof IExpandable) {
                  IExpandable item = (IExpandable) list.get(i);
                  if (item.isExpanded() && hasSubItems(item)) {
                      List subList = item.getSubItems();
                      mData.addAll(pos + 1, subList);
                      int subItemCount = recursiveExpand(pos + 1, subList);
                      count += subItemCount;
                  }
              }
          }
          return count;
  
      }
  
      /**
       * Expand an expandable item
       *
       * @param position     position of the item
       * @param animate      expand items with animation
       * @param shouldNotify notify the RecyclerView to rebind items, <strong>false</strong> if you want to do it yourself.
       * @return the number of items that have been added.
       */
      public int expand(@IntRange(from = 0) int position, boolean animate, boolean shouldNotify) {
          position -= getHeaderLayoutCount();
  
          IExpandable expandable = getExpandableItem(position);
          if (expandable == null) {
              return 0;
          }
          if (!hasSubItems(expandable)) {
              expandable.setExpanded(false);
              return 0;
          }
          int subItemCount = 0;
          if (!expandable.isExpanded()) {
              List list = expandable.getSubItems();
              mData.addAll(position + 1, list);
              subItemCount += recursiveExpand(position + 1, list);
  
              expandable.setExpanded(true);
              subItemCount += list.size();
          }
          int parentPos = position + getHeaderLayoutCount();
          if (shouldNotify) {
              if (animate) {
                  notifyItemChanged(parentPos);
                  notifyItemRangeInserted(parentPos + 1, subItemCount);
              } else {
                  notifyDataSetChanged();
              }
          }
          return subItemCount;
      }
  
      /**
       * Expand an expandable item
       *
       * @param position position of the item, which includes the header layout count.
       * @param animate  expand items with animation
       * @return the number of items that have been added.
       */
      public int expand(@IntRange(from = 0) int position, boolean animate) {
          return expand(position, animate, true);
      }
  
      /**
       * Expand an expandable item with animation.
       *
       * @param position position of the item, which includes the header layout count.
       * @return the number of items that have been added.
       */
      public int expand(@IntRange(from = 0) int position) {
          return expand(position, true, true);
      }
  
      public int expandAll(int position, boolean animate, boolean notify) {
          position -= getHeaderLayoutCount();
  
          T endItem = null;
          if (position + 1 < this.mData.size()) {
              endItem = getItem(position + 1);
          }
  
          IExpandable expandable = getExpandableItem(position);
          if (!hasSubItems(expandable)) {
              return 0;
          }
  
          int count = expand(position + getHeaderLayoutCount(), false, false);
          for (int i = position + 1; i < this.mData.size(); i++) {
              T item = getItem(i);
  
              if (item == endItem) {
                  break;
              }
              if (isExpandable(item)) {
                  count += expand(i + getHeaderLayoutCount(), false, false);
              }
          }
  
          if (notify) {
              if (animate) {
                  notifyItemRangeInserted(position + getHeaderLayoutCount() + 1, count);
              } else {
                  notifyDataSetChanged();
              }
          }
          return count;
      }
  
      /**
       * expand the item and all its subItems
       *
       * @param position position of the item, which includes the header layout count.
       * @param init     whether you are initializing the recyclerView or not.
       *                 if <strong>true</strong>, it won't notify recyclerView to redraw UI.
       * @return the number of items that have been added to the adapter.
       */
      public int expandAll(int position, boolean init) {
          return expandAll(position, true, !init);
      }
  
      private int recursiveCollapse(@IntRange(from = 0) int position) {
          T item = getItem(position);
          if (!isExpandable(item)) {
              return 0;
          }
          IExpandable expandable = (IExpandable) item;
          int subItemCount = 0;
          if (expandable.isExpanded()) {
              List<T> subItems = expandable.getSubItems();
              for (int i = subItems.size() - 1; i >= 0; i--) {
                  T subItem = subItems.get(i);
                  int pos = getItemPosition(subItem);
                  if (pos < 0) {
                      continue;
                  }
                  if (subItem instanceof IExpandable) {
                      subItemCount += recursiveCollapse(pos);
                  }
                  mData.remove(pos);
                  subItemCount++;
              }
          }
          return subItemCount;
      }
  
      /**
       * Collapse an expandable item that has been expanded..
       *
       * @param position the position of the item, which includes the header layout count.
       * @param animate  collapse with animation or not.
       * @param notify   notify the recyclerView refresh UI or not.
       * @return the number of subItems collapsed.
       */
      public int collapse(@IntRange(from = 0) int position, boolean animate, boolean notify) {
          position -= getHeaderLayoutCount();
  
          IExpandable expandable = getExpandableItem(position);
          if (expandable == null) {
              return 0;
          }
          int subItemCount = recursiveCollapse(position);
          expandable.setExpanded(false);
          int parentPos = position + getHeaderLayoutCount();
          if (notify) {
              if (animate) {
                  notifyItemChanged(parentPos);
                  notifyItemRangeRemoved(parentPos + 1, subItemCount);
              } else {
                  notifyDataSetChanged();
              }
          }
          return subItemCount;
      }
  
      /**
       * Collapse an expandable item that has been expanded..
       *
       * @param position the position of the item, which includes the header layout count.
       * @return the number of subItems collapsed.
       */
      public int collapse(@IntRange(from = 0) int position) {
          return collapse(position, true, true);
      }
  
      /**
       * Collapse an expandable item that has been expanded..
       *
       * @param position the position of the item, which includes the header layout count.
       * @return the number of subItems collapsed.
       */
      public int collapse(@IntRange(from = 0) int position, boolean animate) {
          return collapse(position, animate, true);
      }
  
      private int getItemPosition(T item) {
          return item != null && mData != null && !mData.isEmpty() ? mData.indexOf(item) : -1;
      }
  
      private boolean hasSubItems(IExpandable item) {
          List list = item.getSubItems();
          return list != null && list.size() > 0;
      }
  
      private boolean isExpandable(T item) {
          return item != null && item instanceof IExpandable;
      }
  
      private IExpandable getExpandableItem(int position) {
          T item = getItem(position);
          if (isExpandable(item)) {
              return (IExpandable) item;
          } else {
              return null;
          }
      }
  
      /**
       * Get the parent item position of the IExpandable item
       *
       * @return return the closest parent item position of the IExpandable.
       * if the IExpandable item's level is 0, return itself position.
       * if the item's level is negative which mean do not implement this, return a negative
       * if the item is not exist in the data list, return a negative.
       */
      public int getParentPosition(@NonNull T item) {
          int position = getItemPosition(item);
          if (position == -1) {
              return -1;
          }
  
          // if the item is IExpandable, return a closest IExpandable item position whose level smaller than this.
          // if it is not, return the closest IExpandable item position whose level is not negative
          int level;
          if (item instanceof IExpandable) {
              level = ((IExpandable) item).getLevel();
          } else {
              level = Integer.MAX_VALUE;
          }
          if (level == 0) {
              return position;
          } else if (level == -1) {
              return -1;
          }
  
          for (int i = position; i >= 0; i--) {
              T temp = mData.get(i);
              if (temp instanceof IExpandable) {
                  IExpandable expandable = (IExpandable) temp;
                  if (expandable.getLevel() >= 0 && expandable.getLevel() < level) {
                      return i;
                  }
              }
          }
          return -1;
      }
  }
  