uk.ac.kent.jb509.shopper.utils.SlidingUpPanelLayout.java Source code

Java tutorial

Introduction

Here is the source code for uk.ac.kent.jb509.shopper.utils.SlidingUpPanelLayout.java

Source

package uk.ac.kent.jb509.shopper.utils;

/** Copyright (C) Umano: News Read To You (https://github.com/umano/AndroidSlidingUpPanel)
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except 
 * in compliance with the License. You may obtain a copy of the License in the LICENSE file, or 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.
 */

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SoundEffectConstants;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;

public class SlidingUpPanelLayout extends ViewGroup {

    private class DragHelperCallback extends ViewDragHelper.Callback {

        @Override
        public int clampViewPositionVertical(final View child, final int top, final int dy) {
            final int topBound = getPaddingTop();
            final int bottomBound = topBound + mSlideRange;

            final int newLeft = Math.min(Math.max(top, topBound), bottomBound);

            return newLeft;
        }

        @Override
        public int getViewVerticalDragRange(final View child) {
            return mSlideRange;
        }

        @Override
        public void onViewCaptured(final View capturedChild, final int activePointerId) {
            // Make all child views visible in preparation for sliding things
            // around
            setAllChildrenVisible();
        }

        @Override
        public void onViewDragStateChanged(final int state) {
            if (mDragHelper.getViewDragState() == ViewDragHelper.STATE_IDLE) {
                if (mSlideOffset == 0) {
                    updateObscuredViewVisibility();
                    dispatchOnPanelExpanded(mSlideableView);
                    mPreservedExpandedState = true;
                } else if (isAnchored()) {
                    updateObscuredViewVisibility();
                    dispatchOnPanelAnchored(mSlideableView);
                    mPreservedExpandedState = true;
                } else {
                    dispatchOnPanelCollapsed(mSlideableView);
                    mPreservedExpandedState = false;
                }
            }
        }

        @Override
        public void onViewPositionChanged(final View changedView, final int left, final int top, final int dx,
                final int dy) {
            onPanelDragged(top);
            invalidate();
        }

        @Override
        public void onViewReleased(final View releasedChild, final float xvel, final float yvel) {
            int top = getPaddingTop();

            if (mAnchorPoint != 0) {
                final int anchoredTop = (int) (mAnchorPoint * mSlideRange);
                final float anchorOffset = (float) anchoredTop / (float) mSlideRange;

                if (yvel > 0 || yvel == 0 && mSlideOffset >= (1f + anchorOffset) / 2) {
                    top += mSlideRange;
                } else if (yvel == 0 && mSlideOffset < (1f + anchorOffset) / 2
                        && mSlideOffset >= anchorOffset / 2) {
                    top += mSlideRange * mAnchorPoint;
                }

            } else if (yvel > 0 || yvel == 0 && mSlideOffset > 0.5f) {
                top += mSlideRange;
            }

            mDragHelper.settleCapturedViewAt(releasedChild.getLeft(), top);
            invalidate();
        }

        @Override
        public boolean tryCaptureView(final View child, final int pointerId) {
            if (mIsUnableToDrag) {
                return false;
            }

            return ((LayoutParams) child.getLayoutParams()).slideable;
        }
    }

    public static class LayoutParams extends ViewGroup.MarginLayoutParams {
        private static final int[] ATTRS = new int[] { android.R.attr.layout_weight };

        Paint dimPaint;

        /**
         * True if this view should be drawn dimmed when it's been offset from
         * its default position.
         */
        boolean dimWhenOffset;

        /**
         * True if this pane is the slideable pane in the layout.
         */
        boolean slideable;

        public LayoutParams() {
            super(MATCH_PARENT, MATCH_PARENT);
        }

        public LayoutParams(final android.view.ViewGroup.LayoutParams source) {
            super(source);
        }

        public LayoutParams(final Context c, final AttributeSet attrs) {
            super(c, attrs);

            final TypedArray a = c.obtainStyledAttributes(attrs, ATTRS);
            a.recycle();
        }

        public LayoutParams(final int width, final int height) {
            super(width, height);
        }

        public LayoutParams(final LayoutParams source) {
            super(source);
        }

        public LayoutParams(final MarginLayoutParams source) {
            super(source);
        }

    }

    /**
     * Listener for monitoring events about sliding panes.
     */
    public interface PanelSlideListener {
        public void onPanelAnchored(View panel);

        /**
         * Called when a sliding pane becomes slid completely collapsed. The
         * pane may or may not be interactive at this point depending on if it's
         * shown or hidden
         * 
         * @param panel
         *            The child view that was slid to an collapsed position,
         *            revealing other panes
         */
        public void onPanelCollapsed(View panel);

        /**
         * Called when a sliding pane becomes slid completely expanded. The pane
         * is now guaranteed to be interactive. It may now obscure other views
         * in the layout.
         * 
         * @param panel
         *            The child view that was slid to a expanded position
         */
        public void onPanelExpanded(View panel);

        /**
         * Called when a sliding pane's position changes.
         * 
         * @param panel
         *            The child view that was moved
         * @param slideOffset
         *            The new offset of this sliding pane within its range, from
         *            0-1
         */
        public void onPanelSlide(View panel, float slideOffset);
    }

    static class SavedState extends BaseSavedState {
        public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
            @Override
            public SavedState createFromParcel(final Parcel in) {
                return new SavedState(in);
            }

            @Override
            public SavedState[] newArray(final int size) {
                return new SavedState[size];
            }
        };

        boolean isExpanded;

        private SavedState(final Parcel in) {
            super(in);
            isExpanded = in.readInt() != 0;
        }

        SavedState(final Parcelable superState) {
            super(superState);
        }

        @Override
        public void writeToParcel(final Parcel out, final int flags) {
            super.writeToParcel(out, flags);
            out.writeInt(isExpanded ? 1 : 0);
        }
    }

    /**
     * No-op stubs for {@link PanelSlideListener}. If you only want to implement
     * a subset of the listener methods you can extend this instead of implement
     * the full interface.
     */
    public static class SimplePanelSlideListener implements PanelSlideListener {
        @Override
        public void onPanelAnchored(final View panel) {
        }

        @Override
        public void onPanelCollapsed(final View panel) {
        }

        @Override
        public void onPanelExpanded(final View panel) {
        }

        @Override
        public void onPanelSlide(final View panel, final float slideOffset) {
        }
    }

    /**
     * If no fade color is given by default it will fade to 80% gray.
     */
    private static final int DEFAULT_FADE_COLOR = 0x99000000;

    /**
     * Default peeking out panel height
     */
    private static final int DEFAULT_PANEL_HEIGHT = 68; // dp;

    /**
     * Default height of the shadow above the peeking out panel
     */
    private static final int DEFAULT_SHADOW_HEIGHT = 4; // dp;

    /**
     * Minimum velocity that will be detected as a fling
     */
    private static final int MIN_FLING_VELOCITY = 400; // dips per second

    private static boolean hasOpaqueBackground(final View v) {
        final Drawable bg = v.getBackground();
        if (bg != null) {
            return bg.getOpacity() == PixelFormat.OPAQUE;
        }
        return false;
    }

    /**
     * If panel is transparent, set default panel color
     */
    public String DEFAULT_PANEL_COLOR_TRANSPARENT = "AA000000";

    public boolean enabled = true;

    private float mAnchorPoint = 0.f;

    /**
     * True if a panel can slide with the current measurements
     */
    private boolean mCanSlide;

    /**
     * The fade color used for the panel covered by the slider. 0 = no fading.
     */
    private int mCoveredFadeColor = DEFAULT_FADE_COLOR;

    /**
     * The paint used to dim the main layout when sliding
     */
    private final Paint mCoveredFadePaint = new Paint();

    private final ViewDragHelper mDragHelper;

    /**
     * If provided, the panel can be dragged by only this view. Otherwise, the
     * entire panel can be used for dragging.
     */
    private View mDragView;

    private boolean mDragViewHit;

    private boolean mFirstLayout = true;

    private float mInitialMotionX;

    private float mInitialMotionY;
    /**
     * Flag indicating that sliding feature is enabled\disabled
     */
    private boolean mIsSlidingEnabled;
    /**
     * Flag is Transparent
     */
    private boolean mIsTransparent = true;
    /**
     * A panel view is locked into internal scrolling or another condition that
     * is preventing a drag.
     */
    private boolean mIsUnableToDrag;

    /**
     * Flag indicating if a drag view can have its own touch events. If set to
     * true, a drag view can scroll horizontally and have its own click
     * listener.
     * 
     * Default is set to false.
     */
    private boolean mIsUsingDragViewTouchEvents;

    /**
     * The size of the overhang in pixels.
     */
    private int mPanelHeight;

    private PanelSlideListener mPanelSlideListener;
    /**
     * Stores whether or not the pane was expanded the last time it was
     * slideable. If expand/collapse operations are invoked this state is
     * modified. Used by instance state save/restore.
     */
    private boolean mPreservedExpandedState;

    /**
     * Threshold to tell if there was a scroll touch event.
     */
    private final int mScrollTouchSlop;

    /**
     * Drawable used to draw the shadow between panes.
     */
    private Drawable mShadowDrawable;

    /**
     * The size of the shadow in pixels.
     */
    private final int mShadowHeight;

    /**
     * The child view that can slide, if any.
     */
    private View mSlideableView;

    /**
     * How far the panel is offset from its expanded position. range [0, 1]
     * where 0 = expanded, 1 = collapsed.
     */
    private float mSlideOffset;

    /**
     * How far in pixels the slideable panel may move.
     */
    private int mSlideRange;

    private final Rect mTmpRect = new Rect();

    public SlidingUpPanelLayout(final Context context) {
        this(context, null);
    }

    public SlidingUpPanelLayout(final Context context, final AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SlidingUpPanelLayout(final Context context, final AttributeSet attrs, final int defStyle) {
        super(context, attrs, defStyle);

        final float density = context.getResources().getDisplayMetrics().density;
        mPanelHeight = (int) (DEFAULT_PANEL_HEIGHT * density + 0.5f);
        mShadowHeight = (int) (DEFAULT_SHADOW_HEIGHT * density + 0.5f);

        setWillNotDraw(false);

        mDragHelper = ViewDragHelper.create(this, 0.5f, new DragHelperCallback());
        mDragHelper.setMinVelocity(MIN_FLING_VELOCITY * density);

        mCanSlide = true;
        mIsSlidingEnabled = true;

        setCoveredFadeColor(DEFAULT_FADE_COLOR);

        final ViewConfiguration vc = ViewConfiguration.get(context);
        mScrollTouchSlop = vc.getScaledTouchSlop();
    }

    /**
     * Tests scrollability within child views of v given a delta of dx.
     * 
     * @param v
     *            View to test for horizontal scrollability
     * @param checkV
     *            Whether the view v passed should itself be checked for
     *            scrollability (true), or just its children (false).
     * @param dx
     *            Delta scrolled in pixels
     * @param x
     *            X coordinate of the active touch point
     * @param y
     *            Y coordinate of the active touch point
     * @return true if child views of v can be scrolled by delta of dx.
     */
    protected boolean canScroll(final View v, final boolean checkV, final int dx, final int x, final int y) {
        if (v instanceof ViewGroup) {
            final ViewGroup group = (ViewGroup) v;
            final int scrollX = v.getScrollX();
            final int scrollY = v.getScrollY();
            final int count = group.getChildCount();
            // Count backwards - let topmost views consume scroll distance
            // first.
            for (int i = count - 1; i >= 0; i--) {
                final View child = group.getChildAt(i);
                if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight()
                        && y + scrollY >= child.getTop() && y + scrollY < child.getBottom() && canScroll(child,
                                true, dx, x + scrollX - child.getLeft(), y + scrollY - child.getTop())) {
                    return true;
                }
            }
        }
        return checkV && ViewCompat.canScrollHorizontally(v, -dx);
    }

    @Override
    protected boolean checkLayoutParams(final ViewGroup.LayoutParams p) {
        return p instanceof LayoutParams && super.checkLayoutParams(p);
    }

    /**
     * Collapse the sliding pane if it is currently slideable. If first layout
     * has already completed this will animate.
     * 
     * @return true if the pane was slideable and is now collapsed/in the
     *         process of collapsing
     */
    public boolean collapsePane() {
        return collapsePane(mSlideableView, 0);
    }

    private boolean collapsePane(final View pane, final int initialVelocity) {
        if (mFirstLayout || smoothSlideTo(1.f, initialVelocity)) {
            mPreservedExpandedState = false;
            return true;
        }
        return false;
    }

    @Override
    public void computeScroll() {
        if (mDragHelper.continueSettling(true)) {
            if (!mCanSlide) {
                mDragHelper.abort();
                return;
            }

            ViewCompat.postInvalidateOnAnimation(this);
        }
    }

    void dispatchOnPanelAnchored(final View panel) {
        if (mPanelSlideListener != null) {
            mPanelSlideListener.onPanelAnchored(panel);
        }
        sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
    }

    void dispatchOnPanelCollapsed(final View panel) {
        if (mPanelSlideListener != null) {
            mPanelSlideListener.onPanelCollapsed(panel);
        }
        sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
    }

    void dispatchOnPanelExpanded(final View panel) {
        if (mPanelSlideListener != null) {
            mPanelSlideListener.onPanelExpanded(panel);
        }
        sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
    }

    void dispatchOnPanelSlide(final View panel) {
        if (mPanelSlideListener != null) {
            mPanelSlideListener.onPanelSlide(panel, mSlideOffset);
        }
    }

    @Override
    public void draw(final Canvas c) {
        super.draw(c);

        if (mSlideableView == null) {
            // No need to draw a shadow if we don't have one.
            return;
        }

        final int right = mSlideableView.getRight();
        final int top = mSlideableView.getTop() - mShadowHeight;
        final int bottom = mSlideableView.getTop();
        final int left = mSlideableView.getLeft();

        if (mShadowDrawable != null) {
            mShadowDrawable.setBounds(left, top, right, bottom);
            mShadowDrawable.draw(c);
        }
    }

    @Override
    protected boolean drawChild(final Canvas canvas, final View child, final long drawingTime) {
        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
        boolean result;
        final int save = canvas.save(Canvas.CLIP_SAVE_FLAG);

        boolean drawScrim = false;

        if (mCanSlide && !lp.slideable && mSlideableView != null) {
            // Clip against the slider; no sense drawing what will immediately
            // be covered.
            canvas.getClipBounds(mTmpRect);
            mTmpRect.bottom = Math.min(mTmpRect.bottom, mSlideableView.getTop());

            if (!mIsTransparent) {
                canvas.clipRect(mTmpRect);
            }

            if (mSlideOffset < 1) {
                drawScrim = true;
            }
        }

        result = super.drawChild(canvas, child, drawingTime);
        canvas.restoreToCount(save);

        if (drawScrim) {
            final int baseAlpha = (mCoveredFadeColor & 0xff000000) >>> 24;
            final int imag = (int) (baseAlpha * (1 - mSlideOffset));
            final int color = imag << 24 | mCoveredFadeColor & 0xffffff;
            mCoveredFadePaint.setColor(color);
            canvas.drawRect(mTmpRect, mCoveredFadePaint);
        }

        return result;
    }

    /**
     * Expand the sliding pane if it is currently slideable. If first layout has
     * already completed this will animate.
     * 
     * @return true if the pane was slideable and is now expanded/in the process
     *         of expading
     */
    public boolean expandPane() {
        return expandPane(0);
    }

    /**
     * Partially expand the sliding pane up to a specific offset
     * 
     * @param mSlideOffset
     *            Value between 0 and 1, where 0 is completely expanded.
     * @return true if the pane was slideable and is now expanded/in the process
     *         of expading
     */
    public boolean expandPane(final float mSlideOffset) {
        if (!isPaneVisible()) {
            showPane();
        }
        return expandPane(mSlideableView, 0, mSlideOffset);
    }

    private boolean expandPane(final View pane, final int initialVelocity, final float mSlideOffset) {
        if (mFirstLayout || smoothSlideTo(mSlideOffset, initialVelocity)) {
            mPreservedExpandedState = true;
            return true;
        }
        return false;
    }

    @Override
    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
        return new LayoutParams();
    }

    @Override
    public ViewGroup.LayoutParams generateLayoutParams(final AttributeSet attrs) {
        return new LayoutParams(getContext(), attrs);
    }

    @Override
    protected ViewGroup.LayoutParams generateLayoutParams(final ViewGroup.LayoutParams p) {
        return p instanceof MarginLayoutParams ? new LayoutParams((MarginLayoutParams) p) : new LayoutParams(p);
    }

    /**
     * @return The ARGB-packed color value used to fade the fixed pane
     */
    public int getCoveredFadeColor() {
        return mCoveredFadeColor;
    }

    /**
     * @return The current collapsed panel height
     */
    public int getPanelHeight() {
        return mPanelHeight;
    }

    public void hidePane() {
        if (mSlideableView == null) {
            return;
        }
        mSlideableView.setVisibility(View.GONE);
        requestLayout();
    }

    /**
     * Check if the layout is anchored in an intermediate point.
     * 
     * @return true if sliding panels are anchored
     */
    public boolean isAnchored() {
        final int anchoredTop = (int) (mAnchorPoint * mSlideRange);
        return !mFirstLayout && mCanSlide && mSlideOffset == (float) anchoredTop / (float) mSlideRange;
    }

    private boolean isDragViewHit(final int x, final int y) {
        final View v = mDragView != null ? mDragView : mSlideableView;
        if (v == null) {
            return false;
        }
        final int[] viewLocation = new int[2];
        v.getLocationOnScreen(viewLocation);
        final int[] parentLocation = new int[2];
        this.getLocationOnScreen(parentLocation);
        final int screenX = parentLocation[0] + x;
        final int screenY = parentLocation[1] + y;
        return screenX >= viewLocation[0] && screenX < viewLocation[0] + v.getWidth() && screenY >= viewLocation[1]
                && screenY < viewLocation[1] + v.getHeight();
    }

    /**
     * Check if the layout is completely expanded.
     * 
     * @return true if sliding panels are completely expanded
     */
    public boolean isExpanded() {
        return mFirstLayout && mPreservedExpandedState || !mFirstLayout && mCanSlide && mSlideOffset == 0;
    }

    public boolean isPaneVisible() {
        if (getChildCount() < 2) {
            return false;
        }
        final View slidingPane = getChildAt(1);
        return slidingPane.getVisibility() == View.VISIBLE;
    }

    /**
     * Check if the content in this layout cannot fully fit side by side and
     * therefore the content pane can be slid back and forth.
     * 
     * @return true if content in this layout can be expanded
     */
    public boolean isSlideable() {
        return mCanSlide;
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        mFirstLayout = true;
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        mFirstLayout = true;
    }

    @Override
    public boolean onInterceptTouchEvent(final MotionEvent ev) {
        final int action = MotionEventCompat.getActionMasked(ev);

        if (!mCanSlide || !mIsSlidingEnabled || mIsUnableToDrag && action != MotionEvent.ACTION_DOWN) {
            mDragHelper.cancel();
            return super.onInterceptTouchEvent(ev);
        }

        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
            mDragHelper.cancel();
            return false;
        }

        final float x = ev.getX();
        final float y = ev.getY();
        boolean interceptTap = false;

        switch (action) {
        case MotionEvent.ACTION_DOWN: {
            mIsUnableToDrag = false;
            mInitialMotionX = x;
            mInitialMotionY = y;
            mDragViewHit = isDragViewHit((int) x, (int) y);

            if (mDragViewHit && !mIsUsingDragViewTouchEvents) {
                interceptTap = true;
            }
            break;
        }

        case MotionEvent.ACTION_MOVE: {
            final float adx = Math.abs(x - mInitialMotionX);
            final float ady = Math.abs(y - mInitialMotionY);
            final int dragSlop = mDragHelper.getTouchSlop();

            // Handle any horizontal scrolling on the drag view.
            if (mIsUsingDragViewTouchEvents) {
                if (adx > mScrollTouchSlop && ady < mScrollTouchSlop) {
                    return super.onInterceptTouchEvent(ev);
                }
                // Intercept the touch if the drag view has any vertical scroll.
                // onTouchEvent will determine if the view should drag
                // vertically.
                else if (ady > mScrollTouchSlop) {
                    interceptTap = mDragViewHit;
                }
            }

            if (ady > dragSlop && adx > ady) {
                mDragHelper.cancel();
                mIsUnableToDrag = true;
                return false;
            }
            break;
        }
        }

        final boolean interceptForDrag = mDragViewHit && mDragHelper.shouldInterceptTouchEvent(ev);

        return interceptForDrag || interceptTap;
    }

    @Override
    protected void onLayout(final boolean changed, final int l, final int t, final int r, final int b) {
        final int paddingLeft = getPaddingLeft();
        final int paddingTop = getPaddingTop();

        final int childCount = getChildCount();
        int yStart = paddingTop;
        int nextYStart = yStart;

        if (mFirstLayout) {
            mSlideOffset = mCanSlide && mPreservedExpandedState ? 0.f : 1.f;
        }

        for (int i = 0; i < childCount; i++) {
            final View child = getChildAt(i);

            if (mIsTransparent && i == 1) {
                child.setBackgroundColor(Color.argb(230, 0, 0, 0));
            }

            if (child.getVisibility() == GONE) {
                continue;
            }

            final LayoutParams lp = (LayoutParams) child.getLayoutParams();

            final int childHeight = child.getMeasuredHeight();

            if (lp.slideable) {
                mSlideRange = childHeight - mPanelHeight;
                yStart += (int) (mSlideRange * mSlideOffset);
            } else {
                yStart = nextYStart;
            }

            final int childTop = yStart;
            final int childBottom = childTop + childHeight;
            final int childLeft = paddingLeft;
            final int childRight = childLeft + child.getMeasuredWidth();
            child.layout(childLeft, childTop, childRight, childBottom);

            nextYStart += child.getHeight();
        }

        if (mFirstLayout) {
            updateObscuredViewVisibility();
        }

        mFirstLayout = false;
    }

    @Override
    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
        // final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        // final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        final int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        /*
         * if (widthMode != MeasureSpec.EXACTLY) { throw new
         * IllegalStateException(
         * "Width must have an exact value or MATCH_PARENT"); } else if
         * (heightMode != MeasureSpec.EXACTLY) { throw new
         * IllegalStateException(
         * "Height must have an exact value or MATCH_PARENT"); }
         */

        final int layoutHeight = heightSize - getPaddingTop() - getPaddingBottom();
        int panelHeight = mPanelHeight;

        final int childCount = getChildCount();

        if (childCount > 2) {
            // Log.e(TAG,
            // "onMeasure: More than two child views are not supported.");
        } else if (getChildAt(1) != null && getChildAt(1).getVisibility() == GONE) {
            panelHeight = 0;
        }

        // We'll find the current one below.
        mSlideableView = null;
        mCanSlide = false;

        // First pass. Measure based on child LayoutParams width/height.
        for (int i = 0; i < childCount; i++) {
            final View child = getChildAt(i);
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();

            int height = layoutHeight;
            if (child.getVisibility() == GONE) {
                lp.dimWhenOffset = false;
                continue;
            }

            if (i == 1) {
                lp.slideable = true;
                lp.dimWhenOffset = true;
                mSlideableView = child;
                mCanSlide = true;
            } else {
                height -= panelHeight;
            }

            int childWidthSpec;
            int childHeightSpec;
            if (mIsTransparent && i == 0) {
                childWidthSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY);
                childHeightSpec = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY);
            } else {
                if (lp.width == android.view.ViewGroup.LayoutParams.WRAP_CONTENT) {
                    childWidthSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.AT_MOST);
                } else if (lp.width == android.view.ViewGroup.LayoutParams.MATCH_PARENT) {
                    childWidthSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY);
                } else {
                    childWidthSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
                }

                if (lp.height == android.view.ViewGroup.LayoutParams.WRAP_CONTENT) {
                    childHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
                } else if (lp.height == android.view.ViewGroup.LayoutParams.MATCH_PARENT) {
                    childHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
                } else {
                    childHeightSpec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
                }
            }
            child.measure(childWidthSpec, childHeightSpec);
        }

        setMeasuredDimension(widthSize, heightSize);
    }

    private void onPanelDragged(final int newTop) {
        final int topBound = getPaddingTop();
        mSlideOffset = (float) (newTop - topBound) / mSlideRange;
        dispatchOnPanelSlide(mSlideableView);
    }

    @Override
    protected void onRestoreInstanceState(final Parcelable state) {
        final SavedState ss = (SavedState) state;
        super.onRestoreInstanceState(ss.getSuperState());

        if (ss.isExpanded) {
            expandPane();
        } else {
            collapsePane();
        }
        mPreservedExpandedState = ss.isExpanded;
    }

    @Override
    protected Parcelable onSaveInstanceState() {
        final Parcelable superState = super.onSaveInstanceState();

        final SavedState ss = new SavedState(superState);
        ss.isExpanded = isSlideable() ? isExpanded() : mPreservedExpandedState;

        return ss;
    }

    @Override
    protected void onSizeChanged(final int w, final int h, final int oldw, final int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        // Recalculate sliding panes and their details
        if (h != oldh) {
            mFirstLayout = true;
        }
    }

    @Override
    public boolean onTouchEvent(final MotionEvent ev) {
        if (!enabled) {
            return false;
        }
        if (!mCanSlide || !mIsSlidingEnabled) {
            return super.onTouchEvent(ev);
        }

        mDragHelper.processTouchEvent(ev);

        final int action = ev.getAction();
        final boolean wantTouchEvents = true;

        switch (action & MotionEventCompat.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN: {
            final float x = ev.getX();
            final float y = ev.getY();
            mInitialMotionX = x;
            mInitialMotionY = y;
            break;
        }

        case MotionEvent.ACTION_UP: {
            final float x = ev.getX();
            final float y = ev.getY();
            final float dx = x - mInitialMotionX;
            final float dy = y - mInitialMotionY;
            final int slop = mDragHelper.getTouchSlop();
            if (dx * dx + dy * dy < slop * slop && isDragViewHit((int) x, (int) y)) {
                final View v = mDragView != null ? mDragView : mSlideableView;
                v.playSoundEffect(SoundEffectConstants.CLICK);
                if (!isExpanded() && !isAnchored()) {
                    expandPane(mSlideableView, 0, mAnchorPoint);
                } else {
                    collapsePane();
                }
                break;
            }
            break;
        }
        }

        return wantTouchEvents;
    }

    @Override
    public void requestChildFocus(final View child, final View focused) {
        super.requestChildFocus(child, focused);
        if (!isInTouchMode() && !mCanSlide) {
            mPreservedExpandedState = child == mSlideableView;
        }
    }

    void setAllChildrenVisible() {
        for (int i = 0, childCount = getChildCount(); i < childCount; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() == INVISIBLE) {
                child.setVisibility(VISIBLE);
            }
        }
    }

    /**
     * Set an anchor point where the panel can stop during sliding
     * 
     * @param anchorPoint
     *            A value between 0 and 1, determining the position of the
     *            anchor point starting from the top of the layout.
     */
    public void setAnchorPoint(final float anchorPoint) {
        if (anchorPoint > 0 && anchorPoint < 1) {
            mAnchorPoint = anchorPoint;
        }
    }

    /**
     * Set the color used to fade the pane covered by the sliding pane out when
     * the pane will become fully covered in the expanded state.
     * 
     * @param color
     *            An ARGB-packed color value
     */
    public void setCoveredFadeColor(final int color) {
        mCoveredFadeColor = color;
        invalidate();
    }

    /**
     * Set the draggable view portion. Use to null, to allow the whole panel to
     * be draggable
     * 
     * @param dragView
     *            A view that will be used to drag the panel.
     */
    public void setDragView(final View dragView) {
        mDragView = dragView;
    }

    /**
     * Set if the drag view can have its own touch events. If set to true, a
     * drag view can scroll horizontally and have its own click listener.
     * 
     * Default is set to false.
     */
    public void setEnableDragViewTouchEvents(final boolean enabled) {
        mIsUsingDragViewTouchEvents = enabled;
    }

    /**
     * Set transparent flag
     * 
     */
    public void setIsTransparent(final boolean mIsTransparent) {
        this.mIsTransparent = mIsTransparent;
    }

    /**
     * Set the collapsed panel height in pixels
     * 
     * @param val
     *            A height in pixels
     */
    public void setPanelHeight(final int val) {
        mPanelHeight = val;
        requestLayout();
    }

    public void setPanelSlideListener(final PanelSlideListener listener) {
        mPanelSlideListener = listener;
    }

    /**
     * Set the shadow for the sliding panel
     * 
     */
    public void setShadowDrawable(final Drawable drawable) {
        mShadowDrawable = drawable;
    }

    /**
     * Set sliding enabled flag
     * 
     * @param enabled
     *            flag value
     */
    public void setSlidingEnabled(final boolean enabled) {
        mIsSlidingEnabled = enabled;
    }

    public void showPane() {
        if (getChildCount() < 2) {
            return;
        }
        final View slidingPane = getChildAt(1);
        slidingPane.setVisibility(View.VISIBLE);
        requestLayout();
    }

    /**
     * Smoothly animate mDraggingPane to the target X position within its range.
     * 
     * @param slideOffset
     *            position to animate to
     * @param velocity
     *            initial velocity in case of fling, or 0.
     */
    boolean smoothSlideTo(final float slideOffset, final int velocity) {
        if (!mCanSlide) {
            // Nothing to do.
            return false;
        }

        final int topBound = getPaddingTop();
        final int y = (int) (topBound + slideOffset * mSlideRange);

        if (mDragHelper.smoothSlideViewTo(mSlideableView, mSlideableView.getLeft(), y)) {
            setAllChildrenVisible();
            ViewCompat.postInvalidateOnAnimation(this);
            return true;
        }
        return false;
    }

    void updateObscuredViewVisibility() {
        if (getChildCount() == 0) {
            return;
        }
        final int leftBound = getPaddingLeft();
        final int rightBound = getWidth() - getPaddingRight();
        final int topBound = getPaddingTop();
        final int bottomBound = getHeight() - getPaddingBottom();
        final int left;
        final int right;
        final int top;
        final int bottom;
        if (mSlideableView != null && hasOpaqueBackground(mSlideableView)) {
            left = mSlideableView.getLeft();
            right = mSlideableView.getRight();
            top = mSlideableView.getTop();
            bottom = mSlideableView.getBottom();
        } else {
            left = right = top = bottom = 0;
        }
        final View child = getChildAt(0);
        final int clampedChildLeft = Math.max(leftBound, child.getLeft());
        final int clampedChildTop = Math.max(topBound, child.getTop());
        final int clampedChildRight = Math.min(rightBound, child.getRight());
        final int clampedChildBottom = Math.min(bottomBound, child.getBottom());
        final int vis;
        if (clampedChildLeft >= left && clampedChildTop >= top && clampedChildRight <= right
                && clampedChildBottom <= bottom) {
            vis = INVISIBLE;
        } else {
            vis = VISIBLE;
        }
        child.setVisibility(vis);
    }
}