UltimateRecyclerView.java

/* 
 * Copyright(c) 2015 Marshal Chen 
 * 
 * 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.marshalchen.ultimaterecyclerview; 
 
import android.animation.ValueAnimator; 
import android.annotation.TargetApi; 
import android.content.Context; 
import android.content.res.TypedArray; 
import android.graphics.Canvas; 
import android.graphics.Rect; 
import android.os.Build; 
import android.os.Parcelable; 
import android.support.annotation.ColorInt; 
import android.support.annotation.LayoutRes; 
import android.support.v4.view.ViewCompat; 
import android.support.v4.widget.SwipeRefreshLayout; 
import android.support.v7.widget.GridLayoutManager; 
import android.support.v7.widget.LinearLayoutManager; 
import android.support.v7.widget.RecyclerView; 
import android.support.v7.widget.StaggeredGridLayoutManager; 
import android.support.v7.widget.Toolbar; 
import android.util.AttributeSet; 
import android.util.Log; 
import android.util.SparseIntArray; 
import android.view.LayoutInflater; 
import android.view.MotionEvent; 
import android.view.View; 
import android.view.ViewGroup; 
import android.view.ViewStub; 
import android.view.animation.TranslateAnimation; 
import android.widget.FrameLayout; 
import android.widget.RelativeLayout; 
 
import com.marshalchen.ultimaterecyclerview.ui.DividerItemDecoration; 
import com.marshalchen.ultimaterecyclerview.ui.VerticalSwipeRefreshLayout; 
import com.marshalchen.ultimaterecyclerview.ui.emptyview.emptyViewOnShownListener; 
import com.marshalchen.ultimaterecyclerview.ui.floatingactionbutton.FloatingActionButton; 
import com.marshalchen.ultimaterecyclerview.ui.floatingactionbutton.FloatingActionsMenu; 
import com.marshalchen.ultimaterecyclerview.uiUtils.RecyclerViewPositionHelper; 
import com.marshalchen.ultimaterecyclerview.uiUtils.SavedStateScrolling; 
 
 
/** 
 * UltimateRecyclerView is a recyclerview which contains many features like  swipe to dismiss,animations,drag drop etc. 
 */ 
public class UltimateRecyclerView extends FrameLayout implements Scrollable { 
    /** 
     * TRIGGERED ON NOTIFIY ITEMS 
     */ 
    public static int EMPTY_CLEAR_ALL = 0; 
    public static int EMPTY_SHOW_LOADMORE_ONLY = 1; 
    public static int EMPTY_KEEP_HEADER = 2; 
    public static int EMPTY_KEEP_HEADER_AND_LOARMORE = 3; 
 
    /** 
     * TRIGGERED ON SETTING ADAPTER TO THE URV 
     */ 
    public static int STARTWITH_OFFLINE_ITEMS = 0; 
    public static int STARTWITH_ONLINE_ITEMS = 1; 
 
 
    public RecyclerView mRecyclerView; 
    protected FloatingActionButton defaultFloatingActionButton; 
    private OnLoadMoreListener onLoadMoreListener; 
    private int lastVisibleItemPosition; 
    protected RecyclerView.OnScrollListener mOnScrollListener; 
    protected LAYOUT_MANAGER_TYPE layoutManagerType; 
    private boolean isLoadingMore = false; 
    protected int mPadding; 
    protected int mPaddingTop; 
    protected int mPaddingBottom; 
    protected int mPaddingLeft; 
    protected int mPaddingRight; 
    //protected int mEmptyViewPolicy; 
    protected boolean mClipToPadding; 
    private UltimateViewAdapter mAdapter; 
    // Fields that should be saved onSaveInstanceState 
    private int mPrevFirstVisiblePosition; 
    private int mPrevFirstVisibleChildHeight = -1; 
    private int mPrevScrolledChildrenHeight; 
    private int mPrevScrollY; 
    private int mScrollY; 
    private SparseIntArray mChildrenHeights = new SparseIntArray(); 
 
    // Fields that don't need to be saved onSaveInstanceState 
    private ObservableScrollState mObservableScrollState; 
    private ObservableScrollViewCallbacks mCallbacks; 
    //private ScrollState mScrollState; 
    private boolean mFirstScroll; 
    private boolean mDragging; 
    private boolean mIntercepted; 
    private boolean mIsLoadMoreWidgetEnabled; 
    private MotionEvent mPrevMoveEvent; 
    private ViewGroup mTouchInterceptionViewGroup; 
    /** 
     * custom load more progress bar 
     */ 
    private View mLoadMoreView; 
    /** 
     * empty view group 
     */ 
    protected ViewStub mEmpty; 
    protected View mEmptyView; 
    protected int mEmptyId; 
    protected emptyViewOnShownListener mEmptyViewListener; 
 
 
    /** 
     * the floating button group 
     */ 
    protected ViewStub mFloatingButtonViewStub; 
    protected View mFloatingButtonView; 
    protected int mFloatingButtonId; 
 
    protected int[] defaultSwipeToDismissColors = null; 
    public int showLoadMoreItemNum = 3; 
 
    public VerticalSwipeRefreshLayout mSwipeRefreshLayout; 
 
    private RecyclerViewPositionHelper mRecyclerViewHelper; 
    private CustomRelativeWrapper mHeader; 
    private int mTotalYScrolled; 
 
    private final float SCROLL_MULTIPLIER = 0.5f; 
    private OnParallaxScroll mParallaxScroll; 
    private static boolean isParallaxHeader = false; 
 
 
    /** 
     * control to show the loading view first when list is initiated at the beginning 
     * true - assume there is a buffer to load things before and the adapter suppose zero data at the beignning 
     * false - assume there is data to show at the beginning level 
     */ 
    private boolean isFirstLoadingOnlineAdapter = false; 
    // added by Sevan Joe to support scrollbars 
    private static final int SCROLLBARS_NONE = 0; 
    private static final int SCROLLBARS_VERTICAL = 1; 
    private static final int SCROLLBARS_HORIZONTAL = 2; 
    private int mScrollbarsStyle; 
    private int mVisibleItemCount = 0; 
    private int mTotalItemCount = 0; 
    private int previousTotal = 0; 
    private int mFirstVisibleItem; 
 
    public UltimateRecyclerView(Context context) { 
        super(context); 
        initViews(); 
    } 
 
    public UltimateRecyclerView(Context context, AttributeSet attrs) { 
        super(context, attrs); 
        initAttrs(attrs); 
        initViews(); 
    } 
 
    public UltimateRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) { 
        super(context, attrs, defStyleAttr); 
        initAttrs(attrs); 
        initViews(); 
    } 
 
    public void setRecylerViewBackgroundColor(@ColorInt int color) { 
        mRecyclerView.setBackgroundColor(color); 
    } 
 
    protected void initViews() { 
        LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
        View view = inflater.inflate(R.layout.ultimate_recycler_view_layout, this); 
        mRecyclerView = (RecyclerView) view.findViewById(R.id.ultimate_list); 
        mSwipeRefreshLayout = (VerticalSwipeRefreshLayout) view.findViewById(R.id.swipe_refresh_layout); 
        setScrollbars(); 
        mSwipeRefreshLayout.setEnabled(false); 
 
        if (mRecyclerView != null) { 
            mRecyclerView.setClipToPadding(mClipToPadding); 
            if (mPadding != -1.1f) { 
                mRecyclerView.setPadding(mPadding, mPadding, mPadding, mPadding); 
            } else { 
                mRecyclerView.setPadding(mPaddingLeft, mPaddingTop, mPaddingRight, mPaddingBottom); 
            } 
        } 
 
        defaultFloatingActionButton = (FloatingActionButton) view.findViewById(R.id.defaultFloatingActionButton); 
        setDefaultScrollListener(); 
 
        /** 
         * empty view setup 
         */ 
        mEmpty = (ViewStub) view.findViewById(R.id.emptyview); 
        if (mEmptyId != 0) { 
            mEmpty.setLayoutResource(mEmptyId); 
            mEmptyView = mEmpty.inflate(); 
            mEmpty.setVisibility(View.GONE); 
        } 
 
        /** 
         * floating button setup 
         */ 
        mFloatingButtonViewStub = (ViewStub) view.findViewById(R.id.floatingActionViewStub); 
        mFloatingButtonViewStub.setLayoutResource(mFloatingButtonId); 
    } 
 
    /** 
     * retrieve the empty view from the core 
     * 
     * @return the view item 
     */ 
    public View getEmptyView() { 
        return mEmptyView; 
    } 
 
    public final void setEmptyView(@LayoutRes int emptyResourceId, final int emptyViewPolicy, final int mEmptyViewInitPolicy) { 
        setEmptyView(emptyResourceId, emptyViewPolicy); 
        if (mAdapter != null) { 
            mAdapter.setEmptyViewOnInitPolicy(mEmptyViewInitPolicy); 
        } else { 
            Log.d(VIEW_LOG_TAG, "unabled to empty view policy because the adapter is null"); 
        } 
    } 
 
    /** 
     * Set custom empty view.The view will be shown if the adapter is null or the size of the adapter is zero. 
     * You can customize it as loading view. 
     * 
     * @param emptyResourceId the Resource Id from the empty view 
     * @param emptyViewPolicy the Resource Id from the empty view 
     */ 
    public final void setEmptyView(@LayoutRes int emptyResourceId, final int emptyViewPolicy) { 
        //  mEmptyViewPolicy = emptyViewPolicy; 
        if (mEmptyView == null && emptyResourceId != 0) { 
            mEmptyId = emptyResourceId; 
            mEmpty.setLayoutResource(mEmptyId); 
            mEmptyView = mEmpty.inflate(); 
        } else { 
            Log.d(VIEW_LOG_TAG, "unabled to set empty view because the empty has been set"); 
        } 
        if (mAdapter != null) { 
            mAdapter.setEmptyViewPolicy(emptyViewPolicy); 
            mAdapter.setEmptyViewOnInitPolicy(UltimateRecyclerView.STARTWITH_OFFLINE_ITEMS); 
        } else { 
            Log.d(VIEW_LOG_TAG, "unabled to empty view policy because the adapter is null"); 
        } 
        mEmpty.setVisibility(View.GONE); 
    } 
 
    public final void setEmptyView(@LayoutRes int emptyResourceId, final int emptyViewPolicy, final emptyViewOnShownListener listener) { 
        setEmptyView(emptyResourceId, emptyViewPolicy); 
        mEmptyViewListener = listener; 
    } 
 
    public final void setEmptyView(@LayoutRes int emptyResourceId, final int emptyViewPolicy, final int emptyViewInitPolicy, final emptyViewOnShownListener listener) { 
        setEmptyView(emptyResourceId, emptyViewPolicy, emptyViewInitPolicy); 
        mEmptyViewListener = listener; 
    } 
 
 
    /** 
     * Show the custom or default empty view 
     * You can customize it as loading view 
     */ 
    public boolean showEmptyView() { 
        if (mEmpty != null && mEmptyView != null && mAdapter != null) { 
            if (mAdapter.getEmptyViewPolicy() == EMPTY_CLEAR_ALL || mAdapter.getEmptyViewPolicy() == EMPTY_KEEP_HEADER) { 
                mEmpty.setVisibility(View.VISIBLE); 
                if (mEmptyViewListener != null) { 
                    mEmptyViewListener.onEmptyViewShow(mEmptyView); 
                } 
            } 
            return true; 
        } else { 
            Log.d(VIEW_LOG_TAG, "it is unable to show empty view"); 
            return false; 
        } 
    } 
 
    /** 
     * Hide the custom or default empty view 
     */ 
    public void hideEmptyView() { 
        if (mEmpty != null && mEmptyView != null) { 
            mEmpty.setVisibility(View.GONE); 
        } else { 
            Log.d(VIEW_LOG_TAG, "there is no such empty view"); 
        } 
    } 
 
    /** 
     * setting up the loading more layout 
     * 
     * @param layout the res layout 
     */ 
    public void setLoadMoreView(@LayoutRes final int layout) { 
        mLoadMoreView = LayoutInflater.from(getContext()).inflate(layout, null); 
        enableLoadmore(); 
    } 
 
 
    /** 
     * Show the custom floating button view. 
     */ 
    public void showFloatingButtonView() { 
        if (mFloatingButtonId != 0 && mFloatingButtonView == null) { 
            mFloatingButtonView = mFloatingButtonViewStub.inflate(); 
            mFloatingButtonView.setVisibility(View.VISIBLE); 
        } else { 
            Log.d(VIEW_LOG_TAG, "floating button cannot be inflated because it has inflated already"); 
        } 
    } 
 
    /** 
     * Add ScrollBar of Recyclerview 
     */ 
    protected void setScrollbars() { 
        LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
        switch (mScrollbarsStyle) { 
            case SCROLLBARS_VERTICAL: 
                mSwipeRefreshLayout.removeView(mRecyclerView); 
                View verticalView = inflater.inflate(R.layout.vertical_recycler_view, mSwipeRefreshLayout, true); 
                mRecyclerView = (RecyclerView) verticalView.findViewById(R.id.ultimate_list); 
                break; 
            case SCROLLBARS_HORIZONTAL: 
                mSwipeRefreshLayout.removeView(mRecyclerView); 
                View horizontalView = inflater.inflate(R.layout.horizontal_recycler_view, mSwipeRefreshLayout, true); 
                mRecyclerView = (RecyclerView) horizontalView.findViewById(R.id.ultimate_list); 
                break; 
            default: 
                break; 
        } 
    } 
 
    protected void initAttrs(AttributeSet attrs) { 
        TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.UltimateRecyclerview); 
 
        try { 
            mPadding = (int) typedArray.getDimension(R.styleable.UltimateRecyclerview_recyclerviewPadding, -1.1f); 
            mPaddingTop = (int) typedArray.getDimension(R.styleable.UltimateRecyclerview_recyclerviewPaddingTop, 0.0f); 
            mPaddingBottom = (int) typedArray.getDimension(R.styleable.UltimateRecyclerview_recyclerviewPaddingBottom, 0.0f); 
            mPaddingLeft = (int) typedArray.getDimension(R.styleable.UltimateRecyclerview_recyclerviewPaddingLeft, 0.0f); 
            mPaddingRight = (int) typedArray.getDimension(R.styleable.UltimateRecyclerview_recyclerviewPaddingRight, 0.0f); 
            mClipToPadding = typedArray.getBoolean(R.styleable.UltimateRecyclerview_recyclerviewClipToPadding, false); 
            mEmptyId = typedArray.getResourceId(R.styleable.UltimateRecyclerview_recyclerviewEmptyView, 0); 
            mFloatingButtonId = typedArray.getResourceId(R.styleable.UltimateRecyclerview_recyclerviewFloatingActionView, 0); 
            mScrollbarsStyle = typedArray.getInt(R.styleable.UltimateRecyclerview_recyclerviewScrollbars, SCROLLBARS_NONE); 
            int colorList = typedArray.getResourceId(R.styleable.UltimateRecyclerview_recyclerviewDefaultSwipeColor, 0); 
            if (colorList != 0) { 
                defaultSwipeToDismissColors = getResources().getIntArray(colorList); 
            } 
            //mEmptyViewPolicy = EMPTY_VIEW_POLICY_EMPTY_SHOW; 
        } finally { 
            typedArray.recycle(); 
        } 
    } 
 
 
    private void setObserableScrollListener() { 
        mRecyclerView.removeOnScrollListener(mOnScrollListener); 
        mOnScrollListener = new RecyclerView.OnScrollListener() { 
            @Override 
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) { 
                super.onScrolled(recyclerView, dx, dy); 
                enableShoworHideToolbarAndFloatingButton(recyclerView); 
            } 
        }; 
        mRecyclerView.addOnScrollListener(mOnScrollListener); 
    } 
 
    private int[] mlastPositionsStaggeredGridLayout; 
 
    private void scroll_load_more_detection(RecyclerView recyclerView) { 
 
        RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); 
 
        if (layoutManagerType == null) { 
            if (layoutManager instanceof GridLayoutManager) { 
                layoutManagerType = LAYOUT_MANAGER_TYPE.GRID; 
            } else if (layoutManager instanceof StaggeredGridLayoutManager) { 
                layoutManagerType = LAYOUT_MANAGER_TYPE.STAGGERED_GRID; 
            } else if (layoutManager instanceof LinearLayoutManager) { 
                layoutManagerType = LAYOUT_MANAGER_TYPE.LINEAR; 
            } else { 
                throw new RuntimeException("Unsupported LayoutManager used. Valid ones are LinearLayoutManager, GridLayoutManager and StaggeredGridLayoutManager"); 
            } 
        } 
 
        mTotalItemCount = layoutManager.getItemCount(); 
        mVisibleItemCount = layoutManager.getChildCount(); 
 
        switch (layoutManagerType) { 
            case LINEAR: 
                mFirstVisibleItem = mRecyclerViewHelper.findFirstVisibleItemPosition(); 
                lastVisibleItemPosition = mRecyclerViewHelper.findLastVisibleItemPosition(); 
                break; 
            case GRID: 
                if (layoutManager instanceof GridLayoutManager) { 
                    GridLayoutManager ly = (GridLayoutManager) layoutManager; 
                    lastVisibleItemPosition = ly.findLastVisibleItemPosition(); 
                    mFirstVisibleItem = ly.findFirstVisibleItemPosition(); 
                } 
                break; 
            case STAGGERED_GRID: 
                if (layoutManager instanceof StaggeredGridLayoutManager) { 
                    StaggeredGridLayoutManager sy = (StaggeredGridLayoutManager) layoutManager; 
 
                    if (mlastPositionsStaggeredGridLayout == null) 
                        mlastPositionsStaggeredGridLayout = new int[sy.getSpanCount()]; 
 
                    sy.findLastVisibleItemPositions(mlastPositionsStaggeredGridLayout); 
                    lastVisibleItemPosition = findMax(mlastPositionsStaggeredGridLayout); 
 
                    sy.findFirstVisibleItemPositions(mlastPositionsStaggeredGridLayout); 
                    mFirstVisibleItem = findMin(mlastPositionsStaggeredGridLayout); 
                } 
                break; 
        } 
 
        if (isLoadingMore) { 
            //todo: there are some bugs needs to be adjusted for admob adapter 
            if (mTotalItemCount > previousTotal) { 
                isLoadingMore = false; 
                previousTotal = mTotalItemCount; 
            } 
        } 
 
        boolean bottomEdgeHit = (mTotalItemCount - mVisibleItemCount) <= mFirstVisibleItem; 
        if (!isLoadingMore && bottomEdgeHit) { 
            onLoadMoreListener.loadMore(mRecyclerView.getAdapter().getItemCount(), lastVisibleItemPosition); 
            isLoadingMore = true; 
            previousTotal = mTotalItemCount; 
        } 
 
    } 
 
    protected void setDefaultScrollListener() { 
        mRecyclerView.removeOnScrollListener(mOnScrollListener); 
        mOnScrollListener = new RecyclerView.OnScrollListener() { 
 
            @Override 
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) { 
                super.onScrolled(recyclerView, dx, dy); 
                if (mHeader != null) { 
                    mTotalYScrolled += dy; 
                    if (isParallaxHeader) 
                        translateHeader(mTotalYScrolled); 
                } 
                if (mIsLoadMoreWidgetEnabled) { 
                    scroll_load_more_detection(recyclerView); 
                } 
                enableShoworHideToolbarAndFloatingButton(recyclerView); 
            } 
        }; 
 
        mRecyclerView.addOnScrollListener(mOnScrollListener); 
    } 
 
    /** 
     * Enable loading more of the recyclerview 
     */ 
    private void enableLoadmore() { 
        if (mAdapter != null && mAdapter.getCustomLoadMoreView() == null && mLoadMoreView != null) { 
            mAdapter.setCustomLoadMoreView(mLoadMoreView); 
            mAdapter.enableLoadMore(true); 
            mAdapter.notifyDataSetChanged(); 
        } else { 
 
        } 
        mIsLoadMoreWidgetEnabled = true; 
    } 
 
    /** 
     * If you have used {@link #disableLoadmore()} and want to enable loading more again,you can use this method. 
     */ 
    public void reenableLoadmore() { 
        if (mAdapter != null && mLoadMoreView != null) { 
            mAdapter.enableLoadMore(true); 
        } 
        mIsLoadMoreWidgetEnabled = true; 
    } 
 
    public boolean isLoadMoreEnabled() { 
        return mIsLoadMoreWidgetEnabled; 
    } 
 
    /** 
     * Remove loading more scroll listener 
     */ 
    public void disableLoadmore() { 
        mIsLoadMoreWidgetEnabled = false; 
        if (mAdapter != null && mLoadMoreView != null) { 
            mAdapter.enableLoadMore(false); 
        } 
    } 
 
 
    protected void enableShoworHideToolbarAndFloatingButton(RecyclerView recyclerView) { 
        if (mCallbacks != null) { 
            if (getChildCount() > 0) { 
                int firstVisiblePosition = recyclerView.getChildAdapterPosition(recyclerView.getChildAt(0)); 
                int lastVisiblePosition = recyclerView.getChildAdapterPosition(recyclerView.getChildAt(recyclerView.getChildCount() - 1)); 
                try { 
                    for (int i = firstVisiblePosition, j = 0; i <= lastVisiblePosition; i++, j++) { 
                        int childHeight = 0; 
                        View child = recyclerView.getChildAt(j); 
                        if (mChildrenHeights.indexOfKey(i) < 0 || (child != null && child.getHeight() != mChildrenHeights.get(i))) { 
                            if (child != null) 
                                childHeight = child.getHeight(); 
                        } 
                        mChildrenHeights.put(i, childHeight); 
                    } 
 
                } catch (NullPointerException e) { 
                    e.printStackTrace(); 
                    //todo: need to solve this issue when the first child is missing from the scroll. Please also see the debug from the RV error. 
                    //todo: 07-01 11:50:36.359  32348-32348/com.marshalchen.ultimaterecyclerview.demo D/RVerror? Attempt to invoke virtual method 'int android.view.View.getHeight()' on a null object reference 
                    URLogs.e(e, ""); 
                } 
 
                View firstVisibleChild = recyclerView.getChildAt(0); 
                if (firstVisibleChild != null) { 
                    if (mPrevFirstVisiblePosition < firstVisiblePosition) { 
                        // scroll down 
                        int skippedChildrenHeight = 0; 
                        if (firstVisiblePosition - mPrevFirstVisiblePosition != 1) { 
                            for (int i = firstVisiblePosition - 1; i > mPrevFirstVisiblePosition; i--) { 
                                if (0 < mChildrenHeights.indexOfKey(i)) { 
                                    skippedChildrenHeight += mChildrenHeights.get(i); 
                                } else { 
                                    // Approximate each item's height to the first visible child. 
                                    // It may be incorrect, but without this, scrollY will be broken 
                                    // when scrolling from the bottom. 
                                    skippedChildrenHeight += firstVisibleChild.getHeight(); 
                                } 
                            } 
                        } 
                        mPrevScrolledChildrenHeight += mPrevFirstVisibleChildHeight + skippedChildrenHeight; 
                        mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight(); 
                    } else if (firstVisiblePosition < mPrevFirstVisiblePosition) { 
                        // scroll up 
                        int skippedChildrenHeight = 0; 
                        if (mPrevFirstVisiblePosition - firstVisiblePosition != 1) { 
                            for (int i = mPrevFirstVisiblePosition - 1; i > firstVisiblePosition; i--) { 
                                if (0 < mChildrenHeights.indexOfKey(i)) { 
                                    skippedChildrenHeight += mChildrenHeights.get(i); 
                                } else { 
                                    // Approximate each item's height to the first visible child. 
                                    // It may be incorrect, but without this, scrollY will be broken 
                                    // when scrolling from the bottom. 
                                    skippedChildrenHeight += firstVisibleChild.getHeight(); 
                                } 
                            } 
                        } 
                        mPrevScrolledChildrenHeight -= firstVisibleChild.getHeight() + skippedChildrenHeight; 
                        mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight(); 
                    } else if (firstVisiblePosition == 0) { 
                        mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight(); 
                        mPrevScrolledChildrenHeight = 0; 
                    } 
                    if (mPrevFirstVisibleChildHeight < 0) { 
                        mPrevFirstVisibleChildHeight = 0; 
                    } 
                    mScrollY = mPrevScrolledChildrenHeight - firstVisibleChild.getTop(); 
                    mPrevFirstVisiblePosition = firstVisiblePosition; 
 
                    mCallbacks.onScrollChanged(mScrollY, mFirstScroll, mDragging); 
//                    if (mFirstScroll) { 
//                        mFirstScroll = false; 
//                    } 
 
//                    if (mPrevScrollY < mScrollY) { 
//                        //down 
//                        mObservableScrollState = ObservableScrollState.UP; 
//                    } else if (mScrollY < mPrevScrollY) { 
//                        //up 
//                        mObservableScrollState = ObservableScrollState.DOWN; 
//                    } else { 
//                        mObservableScrollState = ObservableScrollState.STOP; 
//                    } 
 
                    if (mPrevScrollY < mScrollY) { 
                        //down 
                        if (mFirstScroll) { // first scroll down , mPrevScrollY == 0, reach here. 
                            mFirstScroll = false; 
                            mObservableScrollState = ObservableScrollState.STOP; 
                        } 
                        mObservableScrollState = ObservableScrollState.UP; 
                    } else if (mScrollY < mPrevScrollY) { 
                        //up 
                        mObservableScrollState = ObservableScrollState.DOWN; 
                    } else { 
                        mObservableScrollState = ObservableScrollState.STOP; 
                    } 
                    if (mFirstScroll) { 
                        mFirstScroll = false; 
                    } 
                    mPrevScrollY = mScrollY; 
                } 
            } 
        } 
    } 
 
    /** 
     * Set a listener that will be notified of any changes in scroll state or position. 
     * 
     * @param customOnScrollListener to set or null to clear 
     * @deprecated Use {@link #addOnScrollListener(RecyclerView.OnScrollListener)} and 
     * {@link #removeOnScrollListener(RecyclerView.OnScrollListener)} 
     */ 
    public void setOnScrollListener(RecyclerView.OnScrollListener customOnScrollListener) { 
        mRecyclerView.setOnScrollListener(customOnScrollListener); 
    } 
 
    public void addOnScrollListener(RecyclerView.OnScrollListener customOnScrollListener) { 
        mRecyclerView.addOnScrollListener(customOnScrollListener); 
    } 
 
    public void removeOnScrollListener(RecyclerView.OnScrollListener customOnScrollListener) { 
        mRecyclerView.removeOnScrollListener(customOnScrollListener); 
    } 
 
    public void addItemDividerDecoration(Context context) { 
        RecyclerView.ItemDecoration itemDecoration = 
                new DividerItemDecoration(context, DividerItemDecoration.VERTICAL_LIST); 
        mRecyclerView.addItemDecoration(itemDecoration); 
    } 
 
 
    /** 
     * Swaps the current adapter with the provided one. It is similar to 
     * {@link #setAdapter(UltimateViewAdapter)} but assumes existing adapter and the new adapter uses the same 
     * ViewHolder and does not clear the RecycledViewPool. 
     * Note that it still calls onAdapterChanged callbacks. 
     * 
     * @param adapter                       The new adapter to set, or null to set no adapter. 
     * @param removeAndRecycleExistingViews If set to true, RecyclerView will recycle all existing Views. If adapters have stable ids and/or you want to animate the disappearing views, you may prefer to set this to false. 
     */ 
    public void swapAdapter(UltimateViewAdapter adapter, boolean removeAndRecycleExistingViews) { 
        mRecyclerView.swapAdapter(adapter, removeAndRecycleExistingViews); 
    } 
 
    /** 
     * Add an {@link RecyclerView.ItemDecoration} to this RecyclerView. Item decorations can affect both measurement and drawing of individual item views. Item decorations are ordered. Decorations placed earlier in the list will be run/queried/drawn first for their effects on item views. Padding added to views will be nested; a padding added by an earlier decoration will mean further item decorations in the list will be asked to draw/pad within the previous decoration's given area. 
     * 
     * @param itemDecoration Decoration to add 
     */ 
    public void addItemDecoration(RecyclerView.ItemDecoration itemDecoration) { 
        mRecyclerView.addItemDecoration(itemDecoration); 
    } 
 
    /** 
     * Add an {@link RecyclerView.ItemDecoration} to this RecyclerView. Item decorations can affect both measurement and drawing of individual item views. 
     * <p>Item decorations are ordered. Decorations placed earlier in the list will be run/queried/drawn first for their effects on item views. Padding added to views will be nested; a padding added by an earlier decoration will mean further item decorations in the list will be asked to draw/pad within the previous decoration's given area.</p> 
     * 
     * @param itemDecoration Decoration to add 
     * @param index          Position in the decoration chain to insert this decoration at. If this value is negative the decoration will be added at the end. 
     */ 
    public void addItemDecoration(RecyclerView.ItemDecoration itemDecoration, int index) { 
        mRecyclerView.addItemDecoration(itemDecoration, index); 
    } 
 
    /** 
     * Sets the {@link RecyclerView.ItemAnimator} that will handle animations involving changes 
     * to the items in this RecyclerView. By default, RecyclerView instantiates and 
     * uses an instance of {@link android.support.v7.widget.DefaultItemAnimator}. Whether item animations are enabled for the RecyclerView depends on the ItemAnimator and whether 
     * the LayoutManager {@link android.support.v7.widget.RecyclerView.LayoutManager#supportsPredictiveItemAnimations() 
     * supports item animations}. 
     * 
     * @param animator The ItemAnimator being set. If null, no animations will occur 
     *                 when changes occur to the items in this RecyclerView. 
     */ 
    public void setItemAnimator(RecyclerView.ItemAnimator animator) { 
        mRecyclerView.setItemAnimator(animator); 
    } 
 
    /** 
     * Gets the current ItemAnimator for this RecyclerView. A null return value 
     * indicates that there is no animator and that item changes will happen without 
     * any animations. By default, RecyclerView instantiates and 
     * uses an instance of {@link android.support.v7.widget.DefaultItemAnimator}. 
     * 
     * @return ItemAnimator The current ItemAnimator. If null, no animations will occur 
     * when changes occur to the items in this RecyclerView. 
     */ 
    public RecyclerView.ItemAnimator getItemAnimator() { 
        return mRecyclerView.getItemAnimator(); 
    } 
 
    /** 
     * Set the listener when refresh is triggered and enable the SwipeRefreshLayout 
     * 
     * @param listener SwipeRefreshLayout 
     */ 
    public void setDefaultOnRefreshListener(SwipeRefreshLayout.OnRefreshListener listener) { 
 
        mSwipeRefreshLayout.setEnabled(true); 
        if (defaultSwipeToDismissColors != null && defaultSwipeToDismissColors.length > 0) { 
            mSwipeRefreshLayout.setColorSchemeColors(defaultSwipeToDismissColors); 
        } else { 
            mSwipeRefreshLayout.setColorSchemeResources( 
                    android.R.color.holo_blue_bright, 
                    android.R.color.holo_green_light, 
                    android.R.color.holo_orange_light, 
                    android.R.color.holo_red_light); 
 
        } 
 
        mSwipeRefreshLayout.setOnRefreshListener(listener); 
    } 
 
    /** 
     * Set the color resources used in the progress animation from color resources. The first color will also be the color of the bar that grows in response to a user swipe gesture. 
     * 
     * @param colors colors in array 
     */ 
    public void setDefaultSwipeToRefreshColorScheme(int... colors) { 
        mSwipeRefreshLayout.setColorSchemeColors(colors); 
    } 
 
    /** 
     * Set the load more listener of recyclerview 
     * 
     * @param onLoadMoreListener load listen 
     */ 
    public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) { 
        this.onLoadMoreListener = onLoadMoreListener; 
    } 
 
 
    /** 
     * Set the layout manager to the recycler 
     * 
     * @param manager lm 
     */ 
    public void setLayoutManager(RecyclerView.LayoutManager manager) { 
        mRecyclerView.setLayoutManager(manager); 
    } 
 
    /** 
     * Get the adapter of UltimateRecyclerview 
     * 
     * @return ad 
     */ 
    public RecyclerView.Adapter getAdapter() { 
        return mRecyclerView.getAdapter(); 
    } 
 
    /** 
     * Set a UltimateViewAdapter or the subclass of UltimateViewAdapter to the recyclerview 
     * 
     * @param adapter the adapter in normal 
     */ 
    public void setAdapter(UltimateViewAdapter adapter) { 
        mAdapter = adapter; 
        mRecyclerView.setAdapter(mAdapter); 
        if (mSwipeRefreshLayout != null) 
            mSwipeRefreshLayout.setRefreshing(false); 
        if (mAdapter != null) 
            mAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { 
                @Override 
                public void onItemRangeChanged(int positionStart, int itemCount) { 
                    super.onItemRangeChanged(positionStart, itemCount); 
                    updateHelperDisplays(); 
                } 
 
                @Override 
                public void onItemRangeInserted(int positionStart, int itemCount) { 
                    super.onItemRangeInserted(positionStart, itemCount); 
                    updateHelperDisplays(); 
                } 
 
                @Override 
                public void onItemRangeRemoved(int positionStart, int itemCount) { 
                    super.onItemRangeRemoved(positionStart, itemCount); 
                    updateHelperDisplays(); 
                } 
 
                @Override 
                public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { 
                    super.onItemRangeMoved(fromPosition, toPosition, itemCount); 
                    updateHelperDisplays(); 
                } 
 
                @Override 
                public void onChanged() { 
                    super.onChanged(); 
                    updateHelperDisplays(); 
                } 
            }); 
        if (mAdapter.getAdapterItemCount() == 0 && adapter.getEmptyViewInitPolicy() == STARTWITH_OFFLINE_ITEMS) { 
            // mEmpty.setVisibility(View.VISIBLE); 
            //setRefreshing(true); 
            // isFirstLoadingOnlineAdapter = true; 
            showEmptyView(); 
        } 
 
 
        mRecyclerViewHelper = RecyclerViewPositionHelper.createHelper(mRecyclerView); 
    } 
 
 
    private void updateHelperDisplays() { 
        isLoadingMore = false; 
        if (mSwipeRefreshLayout != null) 
            mSwipeRefreshLayout.setRefreshing(false); 
        if (mAdapter == null) 
            return; 
        /** 
         * fixed by jjHesk 
         * + empty layout is NONE 
         * + getItemCount is zero 
         */ 
        if (!isFirstLoadingOnlineAdapter) { 
            if (mAdapter.getAdapterItemCount() == 0) { 
 
                mEmpty.setVisibility(mEmptyView == null ? View.VISIBLE : View.GONE); 
 
 
            } else if (mEmptyId != 0) { 
                implementLoadMorebehavior(); 
                mEmpty.setVisibility(View.GONE); 
            } 
        } else { 
            isFirstLoadingOnlineAdapter = false; 
            setRefreshing(false); 
            implementLoadMorebehavior(); 
        } 
 
    } 
 
    private void implementLoadMorebehavior() { 
        if (mAdapter.getCustomLoadMoreView() != null) { 
            if (mAdapter.enableLoadMore()) { 
                mAdapter.getCustomLoadMoreView().setVisibility(View.VISIBLE); 
            } else { 
                mAdapter.getCustomLoadMoreView().setVisibility(View.GONE); 
            } 
        } 
    } 
 
    public void setHasFixedSize(boolean hasFixedSize) { 
        mRecyclerView.setHasFixedSize(hasFixedSize); 
    } 
 
    /** 
     * Notify the widget that refresh state has changed. Do not call this when refresh is triggered by a swipe gesture. 
     * 
     * @param refreshing enable the refresh loading icon 
     */ 
    public void setRefreshing(boolean refreshing) { 
        if (mSwipeRefreshLayout != null) 
            mSwipeRefreshLayout.setRefreshing(refreshing); 
    } 
 
 
    /** 
     * Enable or disable the SwipeRefreshLayout. 
     * Default is false 
     * 
     * @param isSwipeRefresh is now operating in refresh 
     */ 
    public void enableDefaultSwipeRefresh(boolean isSwipeRefresh) { 
        if (mSwipeRefreshLayout != null) 
            mSwipeRefreshLayout.setEnabled(isSwipeRefresh); 
    } 
 
 
    public interface OnLoadMoreListener { 
        void loadMore(int itemsCount, final int maxLastVisiblePosition); 
    } 
 
    public enum LAYOUT_MANAGER_TYPE { 
        LINEAR, 
        GRID, 
        STAGGERED_GRID, 
        PUZZLE, 
    } 
 
    private int findMax(int[] lastPositions) { 
        int max = Integer.MIN_VALUE; 
        for (int value : lastPositions) { 
            if (value > max) 
                max = value; 
        } 
        return max; 
    } 
 
    private int findMin(int[] lastPositions) { 
        int min = Integer.MAX_VALUE; 
        for (int value : lastPositions) { 
            if (value != RecyclerView.NO_POSITION && value < min) 
                min = value; 
        } 
        return min; 
    } 
 
 
    /** 
     * allow resource layout id to be introduced 
     * 
     * @param mLayout res id 
     */ 
    public void setParallaxHeader(@LayoutRes int mLayout) { 
        View h_layout = LayoutInflater.from(getContext()).inflate(mLayout, null); 
        setParallaxHeader(h_layout); 
    } 
 
    /** 
     * Set the parallax header of the recyclerview 
     * 
     * @param header the view 
     */ 
    public void setParallaxHeader(View header) { 
        mHeader = new CustomRelativeWrapper(header.getContext()); 
        mHeader.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); 
        mHeader.addView(header, new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); 
        if (mAdapter != null) 
            mAdapter.setCustomHeaderView(mHeader); 
        isParallaxHeader = true; 
    } 
 
 
    /** 
     * Set the normal header of the recyclerview 
     * 
     * @param header na 
     */ 
    public void setNormalHeader(View header) { 
        setParallaxHeader(header); 
        isParallaxHeader = false; 
    } 
 
    /** 
     * Set the on scroll method of parallax header 
     * 
     * @param parallaxScroll na 
     */ 
    public void setOnParallaxScroll(OnParallaxScroll parallaxScroll) { 
        mParallaxScroll = parallaxScroll; 
        mParallaxScroll.onParallaxScroll(0, 0, mHeader); 
    } 
 
    private void translateHeader(float of) { 
        float ofCalculated = of * SCROLL_MULTIPLIER; 
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 
            //Logs.d("ofCalculated    " + ofCalculated+"   "+mHeader.getHeight()); 
            mHeader.setTranslationY(ofCalculated); 
        } else { 
            TranslateAnimation anim = new TranslateAnimation(0, 0, ofCalculated, ofCalculated); 
            anim.setFillAfter(true); 
            anim.setDuration(0); 
            mHeader.startAnimation(anim); 
        } 
        mHeader.setClipY(Math.round(ofCalculated)); 
        if (mParallaxScroll != null) { 
            float left = Math.min(1, ((ofCalculated) / (mHeader.getHeight() * SCROLL_MULTIPLIER))); 
            mParallaxScroll.onParallaxScroll(left, of, mHeader); 
        } 
    } 
 
    public interface OnParallaxScroll { 
        void onParallaxScroll(float percentage, float offset, View parallax); 
    } 
 
    /** 
     * Custom layout for the Parallax Header. 
     */ 
    public static class CustomRelativeWrapper extends RelativeLayout { 
 
        private int mOffset; 
 
        public CustomRelativeWrapper(Context context) { 
            super(context); 
        } 
 
        @Override 
        protected void dispatchDraw(Canvas canvas) { 
            if (isParallaxHeader) 
                canvas.clipRect(new Rect(getLeft(), getTop(), getRight(), getBottom() + mOffset)); 
            super.dispatchDraw(canvas); 
        } 
 
        public void setClipY(int offset) { 
            mOffset = offset; 
            invalidate(); 
        } 
    } 
 
    /** 
     * the observable scroll view call backs 
     * 
     * @param listener listener to set 
     */ 
    public void setScrollViewCallbacks(ObservableScrollViewCallbacks listener) { 
        mCallbacks = listener; 
    } 
 
    public void setItemViewCacheSize(final int off_screen_items) { 
        mRecyclerView.setItemViewCacheSize(off_screen_items); 
    } 
 
    @Override 
    public void onRestoreInstanceState(Parcelable state) { 
        SavedStateScrolling ss = (SavedStateScrolling) state; 
        mPrevFirstVisiblePosition = ss.prevFirstVisiblePosition; 
        mPrevFirstVisibleChildHeight = ss.prevFirstVisibleChildHeight; 
        mPrevScrolledChildrenHeight = ss.prevScrolledChildrenHeight; 
        mPrevScrollY = ss.prevScrollY; 
        mScrollY = ss.scrollY; 
        mChildrenHeights = ss.childrenHeights; 
        RecyclerView.LayoutManager layoutManager = getLayoutManager(); 
 
        /** 
         * enhanced and store the previous scroll position 
         */ 
        if (layoutManager != null) { 
            int count = layoutManager.getChildCount(); 
            if (mPrevScrollY != RecyclerView.NO_POSITION && mPrevScrollY < count) { 
                layoutManager.scrollToPosition(mPrevScrollY); 
            } 
        } 
 
        super.onRestoreInstanceState(ss.getSuperState()); 
    } 
 
    @Override 
    public Parcelable onSaveInstanceState() { 
        Parcelable superState = super.onSaveInstanceState(); 
        SavedStateScrolling ss = new SavedStateScrolling(superState); 
        ss.prevFirstVisiblePosition = mPrevFirstVisiblePosition; 
        ss.prevFirstVisibleChildHeight = mPrevFirstVisibleChildHeight; 
        ss.prevScrolledChildrenHeight = mPrevScrolledChildrenHeight; 
        ss.prevScrollY = mPrevScrollY; 
        ss.scrollY = mScrollY; 
        ss.childrenHeights = mChildrenHeights; 
        return ss; 
    } 
 
    @Override 
    public boolean onInterceptTouchEvent(MotionEvent ev) { 
        if (mCallbacks != null) { 
            switch (ev.getActionMasked()) { 
                case MotionEvent.ACTION_DOWN: 
                    mFirstScroll = mDragging = true; 
                    mCallbacks.onDownMotionEvent(); 
                    break; 
                case MotionEvent.ACTION_UP: 
                case MotionEvent.ACTION_CANCEL: 
                    mIntercepted = false; 
                    mDragging = false; 
                    mCallbacks.onUpOrCancelMotionEvent(mObservableScrollState); 
                    break; 
            } 
        } 
        return super.onInterceptTouchEvent(ev); 
 
    } 
 
 
    @Override 
    public void setTouchInterceptionViewGroup(ViewGroup viewGroup) { 
        mTouchInterceptionViewGroup = viewGroup; 
        setObserableScrollListener(); 
    } 
 
    @Override 
    public void scrollVerticallyTo(int y) { 
        URLogs.d("vertically"); 
        View firstVisibleChild = getChildAt(0); 
        if (firstVisibleChild != null) { 
            int baseHeight = firstVisibleChild.getHeight(); 
            int position = y / baseHeight; 
            scrollVerticallyToPosition(position); 
        } 
    } 
 
    public void scrollVerticallyToPosition(int position) { 
        RecyclerView.LayoutManager lm = getLayoutManager(); 
 
        if (lm != null && lm instanceof LinearLayoutManager) { 
            ((LinearLayoutManager) lm).scrollToPositionWithOffset(position, 0); 
        } else { 
            lm.scrollToPosition(position); 
        } 
    } 
 
    @Override 
    public int getCurrentScrollY() { 
        return mScrollY; 
    } 
 
    @Override 
    public boolean onTouchEvent(MotionEvent ev) { 
        URLogs.d("ev---" + ev); 
        if (mCallbacks != null) { 
 
            switch (ev.getActionMasked()) { 
                case MotionEvent.ACTION_UP: 
                case MotionEvent.ACTION_CANCEL: 
                    mIntercepted = false; 
                    mDragging = false; 
                    mCallbacks.onUpOrCancelMotionEvent(mObservableScrollState); 
                    break; 
                case MotionEvent.ACTION_MOVE: 
                    if (mPrevMoveEvent == null) { 
                        mPrevMoveEvent = ev; 
                    } 
                    float diffY = ev.getY() - mPrevMoveEvent.getY(); 
                    mPrevMoveEvent = MotionEvent.obtainNoHistory(ev); 
                    if (getCurrentScrollY() - diffY <= 0) { 
                        // Can't scroll anymore. 
 
                        if (mIntercepted) { 
                            // Already dispatched ACTION_DOWN event to parents, so stop here. 
                            return false; 
                        } 
 
                        // Apps can set the interception target other than the direct parent. 
                        final ViewGroup parent; 
                        if (mTouchInterceptionViewGroup == null) { 
                            parent = (ViewGroup) getParent(); 
                        } else { 
                            parent = mTouchInterceptionViewGroup; 
                        } 
 
                        // Get offset to parents. If the parent is not the direct parent, 
                        // we should aggregate offsets from all of the parents. 
                        float offsetX = 0; 
                        float offsetY = 0; 
                        for (View v = this; v != null && v != parent; v = (View) v.getParent()) { 
                            offsetX += v.getLeft() - v.getScrollX(); 
                            offsetY += v.getTop() - v.getScrollY(); 
                        } 
                        final MotionEvent event = MotionEvent.obtainNoHistory(ev); 
                        event.offsetLocation(offsetX, offsetY); 
 
                        if (parent.onInterceptTouchEvent(event)) { 
                            mIntercepted = true; 
 
                            // If the parent wants to intercept ACTION_MOVE events, 
                            // we pass ACTION_DOWN event to the parent 
                            // as if these touch events just have began now. 
                            event.setAction(MotionEvent.ACTION_DOWN); 
 
                            // Return this onTouchEvent() first and set ACTION_DOWN event for parent 
                            // to the queue, to keep events sequence. 
                            post(new Runnable() { 
                                @Override 
                                public void run() { 
                                    parent.dispatchTouchEvent(event); 
                                } 
                            }); 
                            return false; 
                        } 
                        // Even when this can't be scrolled anymore, 
                        // simply returning false here may cause subView's click, 
                        // so delegate it to super. 
                        return super.onTouchEvent(ev); 
                    } 
                    break; 
            } 
        } 
        return super.onTouchEvent(ev); 
    } 
 
    public boolean toolbarIsShown(Toolbar mToolbar) { 
        return ViewCompat.getTranslationY(mToolbar) == 0; 
    } 
 
    public boolean toolbarIsHidden(Toolbar mToolbar) { 
        return ViewCompat.getTranslationY(mToolbar) == -mToolbar.getHeight(); 
    } 
 
    @Deprecated 
    public void showToolbarAndFAB(Toolbar mToolbar, UltimateRecyclerView ultimateRecyclerView, int screenHeight) { 
        showToolbar(mToolbar, ultimateRecyclerView, screenHeight); 
        showDefaultFloatingActionButton(); 
    } 
 
    @Deprecated 
    public void hideToolbarAndFAB(Toolbar mToolbar, UltimateRecyclerView ultimateRecyclerView, int screenHeight) { 
        hideToolbar(mToolbar, ultimateRecyclerView, screenHeight); 
        hideDefaultFloatingActionButton(); 
    } 
 
    public void showToolbar(Toolbar mToolbar, UltimateRecyclerView ultimateRecyclerView, int screenHeight) { 
        moveToolbar(mToolbar, ultimateRecyclerView, screenHeight, 0); 
    } 
 
    public void hideToolbar(Toolbar mToolbar, UltimateRecyclerView ultimateRecyclerView, int screenHeight) { 
        moveToolbar(mToolbar, ultimateRecyclerView, screenHeight, -mToolbar.getHeight()); 
    } 
 
    public void showView(View mView, UltimateRecyclerView ultimateRecyclerView, int screenHeight) { 
        moveView(mView, ultimateRecyclerView, screenHeight, 0); 
    } 
 
    public void hideView(View mView, UltimateRecyclerView ultimateRecyclerView, int screenHeight) { 
        moveView(mView, ultimateRecyclerView, screenHeight, -mView.getHeight()); 
    } 
 
    @TargetApi(Build.VERSION_CODES.HONEYCOMB) 
    protected void moveToolbar(final Toolbar mToolbar, final UltimateRecyclerView ultimateRecyclerView, final int screenheight, float toTranslationY) { 
        if (ViewCompat.getTranslationY(mToolbar) == toTranslationY) { 
            return; 
        } 
        ValueAnimator animator = ValueAnimator.ofFloat(ViewCompat.getTranslationY(mToolbar), toTranslationY).setDuration(200); 
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 
            @Override 
            public void onAnimationUpdate(ValueAnimator animation) { 
                float translationY = (float) animation.getAnimatedValue(); 
                ViewCompat.setTranslationY(mToolbar, translationY); 
                ViewCompat.setTranslationY((View) ultimateRecyclerView, translationY); 
                // FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) ((View) ultimateRecyclerView).getLayoutParams(); 
                MarginLayoutParams layoutParams = (MarginLayoutParams) ((View) ultimateRecyclerView).getLayoutParams(); 
                layoutParams.height = (int) -translationY + screenheight - layoutParams.topMargin; 
                ((View) ultimateRecyclerView).requestLayout(); 
            } 
        }); 
        animator.start(); 
    } 
 
    @TargetApi(Build.VERSION_CODES.HONEYCOMB) 
    protected void moveView(final View mView, final UltimateRecyclerView ultimateRecyclerView, final int screenheight, float toTranslationY) { 
        if (ViewCompat.getTranslationY(mView) == toTranslationY) { 
            return; 
        } 
        ValueAnimator animator = ValueAnimator.ofFloat(ViewCompat.getTranslationY(mView), toTranslationY).setDuration(200); 
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 
            @Override 
            public void onAnimationUpdate(ValueAnimator animation) { 
                float translationY = (float) animation.getAnimatedValue(); 
                ViewCompat.setTranslationY(mView, translationY); 
                ViewCompat.setTranslationY((View) ultimateRecyclerView, translationY); 
                // FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) ((View) ultimateRecyclerView).getLayoutParams(); 
                MarginLayoutParams layoutParams = (MarginLayoutParams) ((View) ultimateRecyclerView).getLayoutParams(); 
                layoutParams.height = (int) -translationY + screenheight - layoutParams.topMargin; 
                ((View) ultimateRecyclerView).requestLayout(); 
            } 
        }); 
        animator.start(); 
    } 
 
 
    public FloatingActionButton getDefaultFloatingActionButton() { 
        return defaultFloatingActionButton; 
    } 
 
    public void setDefaultFloatingActionButton(FloatingActionButton defaultFloatingActionButton) { 
        this.defaultFloatingActionButton = defaultFloatingActionButton; 
    } 
 
    public View getCustomFloatingActionView() { 
        return mFloatingButtonView; 
    } 
 
//    public void setCustomFloatingActionView(View customFloatingActionView) { 
//        this.floatingActionMenu = floatingActionMenu; 
//    } 
 
    public void showFloatingActionMenu() { 
        if (mFloatingButtonView != null) 
            ((FloatingActionsMenu) mFloatingButtonView).hide(false); 
    } 
 
    public void hideFloatingActionMenu() { 
        if (mFloatingButtonView != null) ((FloatingActionsMenu) mFloatingButtonView).hide(true); 
    } 
 
    public void showFloatingActionButton() { 
        if (mFloatingButtonView != null) 
            ((FloatingActionButton) mFloatingButtonView).hide(false); 
    } 
 
    public void hideFloatingActionButton() { 
        if (mFloatingButtonView != null) ((FloatingActionButton) mFloatingButtonView).hide(true); 
    } 
 
    public void showDefaultFloatingActionButton() { 
        defaultFloatingActionButton.hide(false); 
    } 
 
    public void hideDefaultFloatingActionButton() { 
        defaultFloatingActionButton.hide(true); 
    } 
 
    public void displayCustomFloatingActionView(boolean b) { 
        if (mFloatingButtonView != null) 
            mFloatingButtonView.setVisibility(b ? VISIBLE : INVISIBLE); 
    } 
 
    public void displayDefaultFloatingActionButton(boolean b) { 
        defaultFloatingActionButton.setVisibility(b ? VISIBLE : INVISIBLE); 
    } 
 
    public void removeItemDecoration(RecyclerView.ItemDecoration decoration) { 
        mRecyclerView.removeItemDecoration(decoration); 
    } 
 
    public void addOnItemTouchListener(RecyclerView.OnItemTouchListener listener) { 
        mRecyclerView.addOnItemTouchListener(listener); 
    } 
 
    public void removeOnItemTouchListener(RecyclerView.OnItemTouchListener listener) { 
        mRecyclerView.removeOnItemTouchListener(listener); 
    } 
 
    public RecyclerView.LayoutManager getLayoutManager() { 
        return mRecyclerView.getLayoutManager(); 
    } 
}