first implementation of sliding panel
diff --git a/res/drawable/bg_108.png b/res/drawable/bg_108.png
new file mode 100644
index 0000000..ef5b1b4
--- /dev/null
+++ b/res/drawable/bg_108.png
Binary files differ
diff --git a/res/drawable/bg_144.png b/res/drawable/bg_144.png
new file mode 100644
index 0000000..f6db8e2
--- /dev/null
+++ b/res/drawable/bg_144.png
Binary files differ
diff --git a/res/drawable/bg_216.png b/res/drawable/bg_216.png
new file mode 100644
index 0000000..b7270e9
--- /dev/null
+++ b/res/drawable/bg_216.png
Binary files differ
diff --git a/res/layout/activity_sflphone_home.xml b/res/layout/activity_sflphone_home.xml
index 946c05b..f13a251 100644
--- a/res/layout/activity_sflphone_home.xml
+++ b/res/layout/activity_sflphone_home.xml
@@ -36,52 +36,55 @@
android:layout_width="match_parent"
android:layout_height="match_parent" >
- <RelativeLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res/org.sflphone"
+ <org.sflphone.views.SlidingUpPanelLayout
+ android:id="@+id/contact_panel"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical" >
+ android:layout_height="match_parent" >
- <ImageView
- android:layout_width="match_parent"
- android:layout_height="?android:attr/actionBarSize"
- android:layout_alignParentTop="true"
- android:background="@color/sfl_dark_blue" />
-
- <org.sflphone.views.PagerSlidingTabStrip
- android:id="@+id/pts_main"
- android:layout_width="match_parent"
- android:layout_height="?android:attr/actionBarSize"
- android:layout_alignParentTop="true"
- android:layout_marginTop="?android:attr/actionBarSize"
- android:background="@color/sfl_blue_0"
- app:indicatorColor="@color/sfl_light_blue"
- app:underlineColor="@color/sfl_light_blue" />
-
- <android.support.v4.view.ViewPager
- android:id="@+id/pager"
+ <RelativeLayout
+ xmlns:app="http://schemas.android.com/apk/res/org.sflphone"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_below="@+id/pts_main"
- android:paddingBottom="@dimen/contact_drawer_handle_height" />
+ android:orientation="vertical" >
- <ImageView
- android:id="@+id/overall_shadow"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:alpha="0"
- android:background="@color/black"
- android:clickable="false"
- android:focusable="false" />
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/actionBarSize"
+ android:layout_alignParentTop="true"
+ android:background="@color/sfl_dark_blue" />
- <include
- android:id="@+id/custom_sliding_drawer"
+ <org.sflphone.views.PagerSlidingTabStrip
+ android:id="@+id/pts_main"
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/actionBarSize"
+ android:layout_alignParentTop="true"
+ android:layout_marginTop="?android:attr/actionBarSize"
+ android:background="@color/sfl_blue_0"
+ app:indicatorColor="@color/sfl_light_blue"
+ app:underlineColor="@color/sfl_light_blue" />
+
+ <android.support.v4.view.ViewPager
+ android:id="@+id/pager"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_below="@+id/pts_main"
+ android:paddingBottom="@dimen/contact_drawer_handle_height" />
+
+ <ImageView
+ android:id="@+id/overall_shadow"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:alpha="0"
+ android:background="@color/black"
+ android:clickable="false"
+ android:focusable="false" />
+ </RelativeLayout>
+
+ <FrameLayout
+ android:id="@+id/contacts_frame"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- layout="@layout/contact_drawer" />
- </RelativeLayout>
+ android:layout_height="match_parent" />
+ </org.sflphone.views.SlidingUpPanelLayout>
<FrameLayout
android:id="@+id/left_drawer"
diff --git a/res/layout/contact_drawer.xml b/res/layout/contact_drawer.xml
index d435548..f2dc4e2 100644
--- a/res/layout/contact_drawer.xml
+++ b/res/layout/contact_drawer.xml
@@ -44,24 +44,6 @@
android:layout_width="match_parent"
android:layout_height="72dp" >
-<!-- <ImageView -->
-<!-- android:id="@+id/menu_top_shadow_left" -->
-<!-- android:layout_width="0dp" -->
-<!-- android:layout_height="4dp" -->
-<!-- android:layout_above="@+id/coucou" -->
-<!-- android:layout_alignParentLeft="true" -->
-<!-- android:layout_toLeftOf="@+id/hello" -->
-<!-- android:src="@drawable/defaultshadowtop" /> -->
-
-<!-- <ImageView -->
-<!-- android:id="@+id/menu_top_shadow_right" -->
-<!-- android:layout_width="0dp" -->
-<!-- android:layout_height="4dp" -->
-<!-- android:layout_above="@+id/coucou" -->
-<!-- android:layout_alignParentRight="true" -->
-<!-- android:layout_toRightOf="@+id/hello" -->
-<!-- android:src="@drawable/defaultshadowtop" /> -->
-
<RelativeLayout
android:id="@+id/coucou"
android:layout_width="match_parent"
diff --git a/res/layout/frag_contact_list.xml b/res/layout/frag_contact_list.xml
index ecbdd41..a668315 100644
--- a/res/layout/frag_contact_list.xml
+++ b/res/layout/frag_contact_list.xml
@@ -36,6 +36,52 @@
android:layout_height="match_parent"
android:orientation="vertical" >
+ <RelativeLayout
+ android:id="@+id/slider_button"
+ android:layout_width="match_parent"
+ android:layout_height="68dp" >
+
+ <RelativeLayout
+ android:id="@+id/coucou"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/contact_drawer_handle_height"
+ android:layout_alignParentBottom="true"
+ android:background="@color/sfl_dark_blue" >
+
+ <TextView
+ android:id="@+id/handle_title"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignParentBottom="true"
+ android:layout_centerInParent="true"
+ android:gravity="center"
+ android:text="Contacts"
+ android:textColor="@color/white"
+ android:textStyle="bold" />
+
+ <ImageButton
+ android:id="@+id/contact_search_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentRight="true"
+ android:background="@color/sfl_dark_blue"
+ android:gravity="center"
+ android:src="@drawable/ic_btn_search" >
+ </ImageButton>
+ </RelativeLayout>
+
+ <!-- Declared after for implicit z order -->
+
+ <org.sflphone.views.HalfCircleImageView
+ android:id="@+id/hello"
+ android:layout_width="112dp"
+ android:layout_height="68dp"
+ android:layout_alignParentBottom="true"
+ android:layout_centerHorizontal="true"
+ android:src="@drawable/ic_action_group" />
+ </RelativeLayout>
+
<org.sflphone.views.stickylistheaders.StickyListHeadersListView
android:id="@+id/contacts_list"
android:layout_width="match_parent"
diff --git a/src/org/sflphone/client/HomeActivity.java b/src/org/sflphone/client/HomeActivity.java
index 8e9833b..854420f 100644
--- a/src/org/sflphone/client/HomeActivity.java
+++ b/src/org/sflphone/client/HomeActivity.java
@@ -43,7 +43,6 @@
import org.sflphone.fragments.HomeFragment;
import org.sflphone.fragments.MenuFragment;
import org.sflphone.interfaces.CallInterface;
-import org.sflphone.loaders.LoaderConstants;
import org.sflphone.model.CallContact;
import org.sflphone.model.Conference;
import org.sflphone.model.SipCall;
@@ -51,9 +50,9 @@
import org.sflphone.service.CallManagerCallBack;
import org.sflphone.service.ISipService;
import org.sflphone.service.SipService;
-import org.sflphone.views.CustomSlidingDrawer;
-import org.sflphone.views.CustomSlidingDrawer.OnDrawerScrollListener;
import org.sflphone.views.PagerSlidingTabStrip;
+import org.sflphone.views.SlidingUpPanelLayout;
+import org.sflphone.views.SlidingUpPanelLayout.PanelSlideListener;
import android.app.Activity;
import android.app.AlertDialog;
@@ -65,6 +64,7 @@
import android.content.ServiceConnection;
import android.content.res.Configuration;
import android.database.Cursor;
+import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
@@ -103,7 +103,7 @@
private static final int REQUEST_CODE_CALL = 2;
RelativeLayout mSliderButton;
- CustomSlidingDrawer mContactDrawer;
+ SlidingUpPanelLayout mContactDrawer;
private DrawerLayout mNavigationDrawer;
private ActionBarDrawerToggle mDrawerToggle;
ImageView mShadow;
@@ -116,10 +116,6 @@
private boolean isClosing = false;
private Timer t = new Timer();
- // private TabHost mTabHost;
-
- // public SFLPhoneHome extends Activity implements ActionBar.TabListener, OnClickListener
-
/* called before activity is killed, e.g. rotation */
@Override
protected void onSaveInstanceState(Bundle bundle) {
@@ -152,37 +148,46 @@
getFragmentManager().beginTransaction().replace(R.id.contacts_frame, mContactsFragment).commit();
}
- mContactDrawer = (CustomSlidingDrawer) findViewById(R.id.custom_sliding_drawer);
-
- mContactDrawer.setOnDrawerScrollListener(new OnDrawerScrollListener() {
+ mContactDrawer = (SlidingUpPanelLayout) findViewById(R.id.contact_panel);
+ // mContactDrawer.setShadowDrawable(getResources().getDrawable(R.drawable.above_shadow));
+ mContactDrawer.setAnchorPoint(0.3f);
+ mContactDrawer.setDragView(findViewById(R.id.contacts_frame));
+ mContactDrawer.setEnableDragViewTouchEvents(true);
+// mContactDrawer.setCoveredFadeColor(0xff000000);
+ mContactDrawer.setPanelSlideListener(new PanelSlideListener() {
@Override
- public void onScrollStarted() {
- // getActionBar().hide();
-
- }
-
- @Override
- public void onScrollEnded() {
-
- }
-
- @Override
- public void onScroll(int offset) {
-
- if (offset < 400) {
- getActionBar().hide();
- // mNavigationDrawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
- } else if (offset > 450) {
- getActionBar().show();
- // mNavigationDrawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
+ public void onPanelSlide(View panel, float slideOffset) {
+ if (slideOffset < 0.2) {
+ if (getActionBar().isShowing()) {
+ getActionBar().hide();
+ }
+ } else {
+ if (!getActionBar().isShowing()) {
+ getActionBar().show();
+ }
}
}
+
+ @Override
+ public void onPanelExpanded(View panel) {
+
+ }
+
+ @Override
+ public void onPanelCollapsed(View panel) {
+
+ }
+
+ @Override
+ public void onPanelAnchored(View panel) {
+
+ }
});
- mContactsFragment.setHandleView((RelativeLayout) findViewById(R.id.slider_button));
+ // TODO
+ // mContactsFragment.setHandleView((RelativeLayout) findViewById(R.id.slider_button));
mShadow = (ImageView) findViewById(R.id.overall_shadow);
- mContactDrawer.setmTrackHandle(findViewById(R.id.slider_button));
// Set up the ViewPager with the sections adapter.
mViewPager = (ViewPager) findViewById(R.id.pager);
@@ -226,7 +231,7 @@
super.onPostCreate(savedInstanceState);
// Sync the toggle state after onRestoreInstanceState has occurred.
mDrawerToggle.syncState();
- if (mContactDrawer.isOpened()) {
+ if (mContactDrawer.isExpanded()) {
getActionBar().hide();
}
}
@@ -269,16 +274,9 @@
mNavigationDrawer.closeDrawer(Gravity.LEFT);
return;
}
- if (getActionBar().getCustomView() != null) {
- getActionBar().setDisplayShowCustomEnabled(false);
- getActionBar().setCustomView(null);
- // Display all the contacts again
- getLoaderManager().restartLoader(LoaderConstants.CONTACT_LOADER, null, mContactsFragment);
- return;
- }
- if (mContactDrawer.isOpened()) {
- mContactDrawer.animateClose();
+ if (mContactDrawer.isExpanded() || mContactDrawer.isAnchored()) {
+ mContactDrawer.collapsePane();
return;
}
@@ -501,7 +499,7 @@
}
});
launcher.start();
- mContactDrawer.animateClose();
+ mContactDrawer.collapsePane();
}
@@ -574,12 +572,12 @@
@Override
public void onContactDragged() {
- mContactDrawer.animateClose();
+ mContactDrawer.collapsePane();
}
@Override
public void openDrawer() {
- mContactDrawer.animateOpen();
+ mContactDrawer.expandPane();
}
@Override
diff --git a/src/org/sflphone/fragments/ContactListFragment.java b/src/org/sflphone/fragments/ContactListFragment.java
index 07da1c9..28c0407 100644
--- a/src/org/sflphone/fragments/ContactListFragment.java
+++ b/src/org/sflphone/fragments/ContactListFragment.java
@@ -168,8 +168,20 @@
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View inflatedView = inflater.inflate(R.layout.frag_contact_list, container, false);
mHeader = (LinearLayout) inflater.inflate(R.layout.frag_contact_list_header, null);
- // mPlaceHolder = mHeader.findViewById(R.id.placeholder);
mContactList = (StickyListHeadersListView) inflatedView.findViewById(R.id.contacts_list);
+
+ ((ImageButton) inflatedView.findViewById(R.id.contact_search_button)).setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ mContactList.smoothScrollToPosition(0);
+ mQuickReturnSearchView.setOnQueryTextListener(ContactListFragment.this);
+ mQuickReturnSearchView.setIconified(false);
+ mQuickReturnSearchView.setFocusable(true);
+ mCallbacks.openDrawer();
+ }
+ });
+
mQuickReturnSearchView = (SearchView) mHeader.findViewById(R.id.contact_search);
mStarredGrid = (GridView) mHeader.findViewById(R.id.favorites_grid);
llMain = (LinearLayout) mHeader.findViewById(R.id.llMain);
@@ -196,15 +208,6 @@
}
});
- // mQuickReturnSearchView.setOnClickListener(new OnClickListener() {
- //
- // @Override
- // public void onClick(View v) {
- // // TODO Stub de la méthode généré automatiquement
- //
- // }
- // });
-
getLoaderManager().initLoader(LoaderConstants.CONTACT_LOADER, null, this);
}
@@ -214,9 +217,7 @@
public boolean onItemLongClick(AdapterView<?> av, View view, int pos, long id) {
DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view.findViewById(R.id.photo));
view.startDrag(null, shadowBuilder, view, 0);
- // view.setVisibility(View.INVISIBLE);
mCallbacks.onContactDragged();
- // ((SearchView) mHandle.findViewById(R.id.contact_search_text)).setIconified(true);
return true;
}
diff --git a/src/org/sflphone/views/SlidingUpPanelLayout.java b/src/org/sflphone/views/SlidingUpPanelLayout.java
new file mode 100644
index 0000000..e53ab79
--- /dev/null
+++ b/src/org/sflphone/views/SlidingUpPanelLayout.java
@@ -0,0 +1,1084 @@
+package org.sflphone.views;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+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.util.Log;
+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 static final String TAG = SlidingUpPanelLayout.class.getSimpleName();
+
+ /**
+ * 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;
+
+ /**
+ * If no fade color is given by default it will fade to 80% gray.
+ */
+ private static final int DEFAULT_FADE_COLOR = 0x99000000;
+
+ /**
+ * Minimum velocity that will be detected as a fling
+ */
+ private static final int MIN_FLING_VELOCITY = 400; // dips per second
+
+ /**
+ * 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();
+
+ /**
+ * Drawable used to draw the shadow between panes.
+ */
+ private Drawable mShadowDrawable;
+
+ /**
+ * The size of the overhang in pixels.
+ */
+ private int mPanelHeight;
+
+ /**
+ * The size of the shadow in pixels.
+ */
+ private final int mShadowHeight;
+
+ /**
+ * True if a panel can slide with the current measurements
+ */
+ private boolean mCanSlide;
+
+ /**
+ * If provided, the panel can be dragged by only this view. Otherwise, the entire panel can be used for dragging.
+ */
+ private View mDragView;
+
+ /**
+ * 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;
+
+ /**
+ * A panel view is locked into internal scrolling or another condition that is preventing a drag.
+ */
+ private boolean mIsUnableToDrag;
+
+ /**
+ * Flag indicating that sliding feature is enabled\disabled
+ */
+ private boolean mIsSlidingEnabled;
+
+ /**
+ * 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;
+
+ /**
+ * Threshold to tell if there was a scroll touch event.
+ */
+ private int mScrollTouchSlop;
+
+ private float mInitialMotionX;
+ private float mInitialMotionY;
+ private boolean mDragViewHit;
+ private float mAnchorPoint = 0.f;
+
+ private PanelSlideListener mPanelSlideListener;
+
+ private final ViewDragHelper mDragHelper;
+
+ /**
+ * 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;
+ private boolean mFirstLayout = true;
+
+ private final Rect mTmpRect = new Rect();
+
+ /**
+ * Listener for monitoring events about sliding panes.
+ */
+ public interface PanelSlideListener {
+ /**
+ * 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);
+
+ /**
+ * 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);
+
+ public void onPanelAnchored(View panel);
+ }
+
+ /**
+ * 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 onPanelSlide(View panel, float slideOffset) {
+ }
+
+ @Override
+ public void onPanelCollapsed(View panel) {
+ }
+
+ @Override
+ public void onPanelExpanded(View panel) {
+ }
+
+ @Override
+ public void onPanelAnchored(View panel) {
+ }
+ }
+
+ public SlidingUpPanelLayout(Context context) {
+ this(context, null);
+ }
+
+ public SlidingUpPanelLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public SlidingUpPanelLayout(Context context, AttributeSet attrs, 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);
+
+ ViewConfiguration vc = ViewConfiguration.get(context);
+ mScrollTouchSlop = vc.getScaledTouchSlop();
+ }
+
+ /**
+ * 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(int color) {
+ mCoveredFadeColor = color;
+ invalidate();
+ }
+
+ /**
+ * @return The ARGB-packed color value used to fade the fixed pane
+ */
+ public int getCoveredFadeColor() {
+ return mCoveredFadeColor;
+ }
+
+ /**
+ * Set the collapsed panel height in pixels
+ *
+ * @param val
+ * A height in pixels
+ */
+ public void setPanelHeight(int val) {
+ mPanelHeight = val;
+ requestLayout();
+ }
+
+ /**
+ * @return The current collapsed panel height
+ */
+ public int getPanelHeight() {
+ return mPanelHeight;
+ }
+
+ public void setPanelSlideListener(PanelSlideListener listener) {
+ mPanelSlideListener = listener;
+ }
+
+ /**
+ * 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(View dragView) {
+ mDragView = dragView;
+ }
+
+ /**
+ * 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(float anchorPoint) {
+ if (anchorPoint > 0 && anchorPoint < 1)
+ mAnchorPoint = anchorPoint;
+ }
+
+ /**
+ * Set the shadow for the sliding panel
+ *
+ */
+ public void setShadowDrawable(Drawable drawable) {
+ mShadowDrawable = drawable;
+ }
+
+ void dispatchOnPanelSlide(View panel) {
+ if (mPanelSlideListener != null) {
+ mPanelSlideListener.onPanelSlide(panel, mSlideOffset);
+ }
+ }
+
+ void dispatchOnPanelExpanded(View panel) {
+ if (mPanelSlideListener != null) {
+ mPanelSlideListener.onPanelExpanded(panel);
+ }
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ }
+
+ void dispatchOnPanelCollapsed(View panel) {
+ if (mPanelSlideListener != null) {
+ mPanelSlideListener.onPanelCollapsed(panel);
+ }
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ }
+
+ void dispatchOnPanelAnchored(View panel) {
+ if (mPanelSlideListener != null) {
+ mPanelSlideListener.onPanelAnchored(panel);
+ }
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ }
+
+ 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;
+ }
+ 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);
+ }
+
+ void setAllChildrenVisible() {
+ for (int i = 0, childCount = getChildCount(); i < childCount; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() == INVISIBLE) {
+ child.setVisibility(VISIBLE);
+ }
+ }
+ }
+
+ private static boolean hasOpaqueBackground(View v) {
+ final Drawable bg = v.getBackground();
+ if (bg != null) {
+ return bg.getOpacity() == PixelFormat.OPAQUE;
+ }
+ return false;
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mFirstLayout = true;
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mFirstLayout = true;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, 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");
+ }
+
+ 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).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;
+ if (lp.width == LayoutParams.WRAP_CONTENT) {
+ childWidthSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.AT_MOST);
+ } else if (lp.width == LayoutParams.MATCH_PARENT) {
+ childWidthSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY);
+ } else {
+ childWidthSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
+ }
+
+ int childHeightSpec;
+ if (lp.height == LayoutParams.WRAP_CONTENT) {
+ childHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
+ } else if (lp.height == LayoutParams.MATCH_PARENT) {
+ childHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
+ } else {
+ childHeightSpec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
+ }
+
+ child.measure(childWidthSpec, childHeightSpec);
+ }
+
+ setMeasuredDimension(widthSize, heightSize);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, 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 (child.getVisibility() == GONE) {
+ continue;
+ }
+
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+ 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 onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ // Recalculate sliding panes and their details
+ if (h != oldh) {
+ mFirstLayout = true;
+ }
+ }
+
+ /**
+ * Set sliding enabled flag
+ *
+ * @param enabled
+ * flag value
+ */
+ public void setSlidingEnabled(boolean enabled) {
+ mIsSlidingEnabled = enabled;
+ }
+
+ /**
+ * 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(boolean enabled) {
+ mIsUsingDragViewTouchEvents = enabled;
+ }
+
+ private boolean isDragViewHit(int x, int y) {
+ View v = mDragView != null ? mDragView : mSlideableView;
+ if (v == null)
+ return false;
+ int[] viewLocation = new int[2];
+ v.getLocationOnScreen(viewLocation);
+ int[] parentLocation = new int[2];
+ this.getLocationOnScreen(parentLocation);
+ int screenX = parentLocation[0] + x;
+ int screenY = parentLocation[1] + y;
+ return screenX >= viewLocation[0] && screenX < viewLocation[0] + v.getWidth() && screenY >= viewLocation[1]
+ && screenY < viewLocation[1] + v.getHeight();
+ }
+
+ @Override
+ public void requestChildFocus(View child, View focused) {
+ super.requestChildFocus(child, focused);
+ if (!isInTouchMode() && !mCanSlide) {
+ mPreservedExpandedState = child == mSlideableView;
+ }
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(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
+ public boolean onTouchEvent(MotionEvent ev) {
+ if (!mCanSlide || !mIsSlidingEnabled) {
+ return super.onTouchEvent(ev);
+ }
+
+ mDragHelper.processTouchEvent(ev);
+
+ final int action = ev.getAction();
+ 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)) {
+ View v = mDragView != null ? mDragView : mSlideableView;
+ v.playSoundEffect(SoundEffectConstants.CLICK);
+ if (!isExpanded() && !isAnchored()) {
+ expandPane(mSlideableView, 0, mAnchorPoint);
+ } else {
+ collapsePane();
+ }
+ break;
+ }
+ break;
+ }
+ }
+
+ return wantTouchEvents;
+ }
+
+ private boolean expandPane(View pane, int initialVelocity, float mSlideOffset) {
+ if (mFirstLayout || smoothSlideTo(mSlideOffset, initialVelocity)) {
+ mPreservedExpandedState = true;
+ return true;
+ }
+ return false;
+ }
+
+ private boolean collapsePane(View pane, int initialVelocity) {
+ if (mFirstLayout || smoothSlideTo(1.f, initialVelocity)) {
+ mPreservedExpandedState = false;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * 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(float mSlideOffset) {
+ if (!isPaneVisible()) {
+ showPane();
+ }
+ return expandPane(mSlideableView, 0, mSlideOffset);
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Check if the layout is anchored in an intermediate point.
+ *
+ * @return true if sliding panels are anchored
+ */
+ public boolean isAnchored() {
+ int anchoredTop = (int) (mAnchorPoint * mSlideRange);
+ return !mFirstLayout && mCanSlide && mSlideOffset == (float) anchoredTop / (float) mSlideRange;
+ }
+
+ /**
+ * 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;
+ }
+
+ public boolean isPaneVisible() {
+ if (getChildCount() < 2) {
+ return false;
+ }
+ View slidingPane = getChildAt(1);
+ return slidingPane.getVisibility() == View.VISIBLE;
+ }
+
+ public void showPane() {
+ if (getChildCount() < 2) {
+ return;
+ }
+ View slidingPane = getChildAt(1);
+ slidingPane.setVisibility(View.VISIBLE);
+ requestLayout();
+ }
+
+ public void hidePane() {
+ if (mSlideableView == null) {
+ return;
+ }
+ mSlideableView.setVisibility(View.GONE);
+ requestLayout();
+ }
+
+ private void onPanelDragged(int newTop) {
+ final int topBound = getPaddingTop();
+ mSlideOffset = (float) (newTop - topBound) / mSlideRange;
+ dispatchOnPanelSlide(mSlideableView);
+ }
+
+ @Override
+ protected boolean drawChild(Canvas canvas, View child, 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 = (int) Math.min(mTmpRect.bottom, mSlideableView.getTop() + getResources().getDisplayMetrics().density * 68); // + 60 cause of the rounded shape handle
+ 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;
+ }
+
+ /**
+ * 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(float slideOffset, int velocity) {
+ if (!mCanSlide) {
+ // Nothing to do.
+ return false;
+ }
+
+ final int topBound = getPaddingTop();
+ int y = (int) (topBound + slideOffset * mSlideRange);
+
+ if (mDragHelper.smoothSlideViewTo(mSlideableView, mSlideableView.getLeft(), y)) {
+ setAllChildrenVisible();
+ ViewCompat.postInvalidateOnAnimation(this);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void computeScroll() {
+ if (mDragHelper.continueSettling(true)) {
+ if (!mCanSlide) {
+ mDragHelper.abort();
+ return;
+ }
+
+ ViewCompat.postInvalidateOnAnimation(this);
+ }
+ }
+
+ @Override
+ public void draw(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);
+ }
+ }
+
+ /**
+ * 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(View v, boolean checkV, int dx, int x, 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 ViewGroup.LayoutParams generateDefaultLayoutParams() {
+ return new LayoutParams();
+ }
+
+ @Override
+ protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+ return p instanceof MarginLayoutParams ? new LayoutParams((MarginLayoutParams) p) : new LayoutParams(p);
+ }
+
+ @Override
+ protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+ return p instanceof LayoutParams && super.checkLayoutParams(p);
+ }
+
+ @Override
+ public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return new LayoutParams(getContext(), attrs);
+ }
+
+ @Override
+ protected Parcelable onSaveInstanceState() {
+ Parcelable superState = super.onSaveInstanceState();
+
+ SavedState ss = new SavedState(superState);
+ ss.isExpanded = isSlideable() ? isExpanded() : mPreservedExpandedState;
+
+ return ss;
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Parcelable state) {
+ SavedState ss = (SavedState) state;
+ super.onRestoreInstanceState(ss.getSuperState());
+
+ if (ss.isExpanded) {
+ expandPane();
+ } else {
+ collapsePane();
+ }
+ mPreservedExpandedState = ss.isExpanded;
+ }
+
+ private class DragHelperCallback extends ViewDragHelper.Callback {
+
+ @Override
+ public boolean tryCaptureView(View child, int pointerId) {
+ if (mIsUnableToDrag) {
+ return false;
+ }
+
+ return ((LayoutParams) child.getLayoutParams()).slideable;
+ }
+
+ @Override
+ public void onViewDragStateChanged(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 onViewCaptured(View capturedChild, int activePointerId) {
+ // Make all child views visible in preparation for sliding things around
+ setAllChildrenVisible();
+ }
+
+ @Override
+ public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
+ onPanelDragged(top);
+ invalidate();
+ }
+
+ @Override
+ public void onViewReleased(View releasedChild, float xvel, float yvel) {
+ int top = getPaddingTop();
+
+ if (mAnchorPoint != 0) {
+ int anchoredTop = (int) (mAnchorPoint * mSlideRange);
+ 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 int getViewVerticalDragRange(View child) {
+ return mSlideRange;
+ }
+
+ @Override
+ public int clampViewPositionVertical(View child, int top, int dy) {
+ final int topBound = getPaddingTop();
+ final int bottomBound = topBound + mSlideRange;
+
+ final int newLeft = Math.min(Math.max(top, topBound), bottomBound);
+
+ return newLeft;
+ }
+
+ }
+
+ public static class LayoutParams extends ViewGroup.MarginLayoutParams {
+ private static final int[] ATTRS = new int[] { android.R.attr.layout_weight };
+
+ /**
+ * True if this pane is the slideable pane in the layout.
+ */
+ boolean slideable;
+
+ /**
+ * True if this view should be drawn dimmed when it's been offset from its default position.
+ */
+ boolean dimWhenOffset;
+
+ Paint dimPaint;
+
+ public LayoutParams() {
+ super(MATCH_PARENT, MATCH_PARENT);
+ }
+
+ public LayoutParams(int width, int height) {
+ super(width, height);
+ }
+
+ public LayoutParams(android.view.ViewGroup.LayoutParams source) {
+ super(source);
+ }
+
+ public LayoutParams(MarginLayoutParams source) {
+ super(source);
+ }
+
+ public LayoutParams(LayoutParams source) {
+ super(source);
+ }
+
+ public LayoutParams(Context c, AttributeSet attrs) {
+ super(c, attrs);
+
+ final TypedArray a = c.obtainStyledAttributes(attrs, ATTRS);
+ a.recycle();
+ }
+
+ }
+
+ static class SavedState extends BaseSavedState {
+ boolean isExpanded;
+
+ SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ private SavedState(Parcel in) {
+ super(in);
+ isExpanded = in.readInt() != 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ out.writeInt(isExpanded ? 1 : 0);
+ }
+
+ public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
+ @Override
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ @Override
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+}