Skip to content

Instantly share code, notes, and snippets.

@deyanm
Last active August 8, 2019 09:37
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save deyanm/58cbd80cd9907cf520c7a06b567eefba to your computer and use it in GitHub Desktop.
Save deyanm/58cbd80cd9907cf520c7a06b567eefba to your computer and use it in GitHub Desktop.
Dragging panel with ViewDragHelper
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white">
<Button
android:id="@+id/hidden_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Hidden button"
android:textSize="64sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Some text"
android:layout_below="@id/hidden_button"
android:padding="10dp"
android:textSize="20sp"/>
<com.whiterabbit.dragqueen.DraggingPanel
android:id="@+id/outer_layout"
android:layout_width="match_parent"
android:layout_height="350dp"
android:layout_alignParentBottom="true">
<LinearLayout
android:id="@+id/main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:background="#1E90FF"
android:orientation="vertical">
<LinearLayout
android:id="@+id/queen_button"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="10dp"
android:orientation="horizontal"
android:weightSum="3">
<EditText
android:id="@+id/resolverComentar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_weight="2.5"
android:ems="10"
android:inputType="textMultiLine"
android:padding="10dp"
android:scrollbars="vertical" />
<Button
android:id="@+id/sendMessage"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center"
android:layout_marginEnd="5dp"
android:layout_weight="0.5"
android:background="@android:drawable/ic_menu_send"
android:enabled="false"
android:textSize="16sp"
android:textStyle="bold" />
</LinearLayout>
<LinearLayout
android:id="@+id/buttonsLayout"
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="?android:colorBackground"
android:orientation="horizontal"
android:weightSum="2">
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_margin="5dp"
android:layout_weight="1"
android:gravity="center"
android:background="#ADFF2F"
android:text="Accept"
android:textAllCaps="false"
android:textSize="20sp" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_margin="5dp"
android:layout_weight="1"
android:gravity="center"
android:padding="10dp"
android:text="Decline"
android:background="#FF4500"
android:textAllCaps="false"
android:textSize="20sp" />
</LinearLayout>
<WebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</com.whiterabbit.dragqueen.DraggingPanel>
</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.whiterabbit.dragqueen" >
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:windowSoftInputMode="adjustPan">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
import android.content.Context;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
public class DraggingPanel extends RelativeLayout {
private final double AUTO_OPEN_SPEED_LIMIT = 800.0;
private int mDraggingState = 0;
private LinearLayout mQueenButton;
private ViewDragHelper mDragHelper;
private int mDraggingBorder;
private int mVerticalRange;
private boolean mIsOpen;
public class DragHelperCallback extends ViewDragHelper.Callback {
@Override
public void onViewDragStateChanged(int state) {
if (state == mDraggingState) { // no change
return;
}
if ((mDraggingState == ViewDragHelper.STATE_DRAGGING || mDraggingState == ViewDragHelper.STATE_SETTLING) &&
state == ViewDragHelper.STATE_IDLE) {
// the view stopped from moving.
if (mDraggingBorder == 0) {
onStopDraggingToClosed();
} else if (mDraggingBorder == mVerticalRange) {
mIsOpen = true;
}
}
if (state == ViewDragHelper.STATE_DRAGGING) {
onStartDragging();
}
mDraggingState = state;
}
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
mDraggingBorder = top;
}
public int getViewVerticalDragRange(View child) {
return mVerticalRange;
}
@Override
public boolean tryCaptureView(View view, int i) {
return (view.getId() == R.id.main_layout);
}
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
final int topBound = getPaddingTop();
final int bottomBound = mVerticalRange;
return Math.min(Math.max(top, topBound), bottomBound);
}
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
final float rangeToCheck = mVerticalRange;
if (mDraggingBorder == 0) {
mIsOpen = false;
return;
}
if (mDraggingBorder == rangeToCheck) {
mIsOpen = true;
return;
}
boolean settleToOpen = false;
if (yvel > AUTO_OPEN_SPEED_LIMIT) { // speed has priority over position
settleToOpen = true;
} else if (yvel < -AUTO_OPEN_SPEED_LIMIT) {
settleToOpen = false;
} else if (mDraggingBorder > rangeToCheck / 2) {
settleToOpen = true;
} else if (mDraggingBorder < rangeToCheck / 2) {
settleToOpen = false;
}
final int settleDestY = settleToOpen ? mVerticalRange : 0;
if(mDragHelper.settleCapturedViewAt(0, settleDestY)) {
ViewCompat.postInvalidateOnAnimation(DraggingPanel.this);
}
}
}
public DraggingPanel(Context context, AttributeSet attrs) {
super(context, attrs);
mIsOpen = false;
}
@Override
protected void onFinishInflate() {
mQueenButton = findViewById(R.id.queen_button);
mDragHelper = ViewDragHelper.create(this, 1.0f, new DragHelperCallback());
mIsOpen = false;
super.onFinishInflate();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mVerticalRange = (int) (h * 0.84);
super.onSizeChanged(w, h, oldw, oldh);
}
private void onStopDraggingToClosed() {
// To be implemented
}
private void onStartDragging() {
}
private boolean isQueenTarget(MotionEvent event) {
int[] queenLocation = new int[2];
mQueenButton.getLocationOnScreen(queenLocation);
int upperLimit = queenLocation[1] + mQueenButton.getMeasuredHeight();
int lowerLimit = queenLocation[1];
int y = (int) event.getRawY();
return (y > lowerLimit && y < upperLimit);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (isQueenTarget(event) && mDragHelper.shouldInterceptTouchEvent(event)) {
return true;
} else {
return false;
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (isQueenTarget(event) || isMoving()) {
mDragHelper.processTouchEvent(event);
return true;
} else {
return super.onTouchEvent(event);
}
}
@Override
public void computeScroll() { // needed for automatic settling.
if (mDragHelper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
public boolean isMoving() {
return (mDraggingState == ViewDragHelper.STATE_DRAGGING ||
mDraggingState == ViewDragHelper.STATE_SETTLING);
}
public boolean isOpen() {
return mIsOpen;
}
}
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Toast;
public class MainActivity extends Activity implements View.OnClickListener {
private LinearLayout mQueen;
private Button mHidden, accept, decline;
private OuterLayout mOuterLayout;
private LinearLayout mMainLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mOuterLayout = findViewById(R.id.outer_layout);
mMainLayout = findViewById(R.id.main_layout);
mHidden = findViewById(R.id.hidden_button);
accept = findViewById(R.id.button1);
accept.setOnClickListener(this);
decline = findViewById(R.id.button2);
decline.setOnClickListener(this);
mHidden.setOnClickListener(this);
mQueen = findViewById(R.id.queen_button);
mQueen.setOnClickListener(this);
mMainLayout.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
if (mOuterLayout.isMoving()) {
v.setTop(oldTop);
v.setBottom(oldBottom);
v.setLeft(oldLeft);
v.setRight(oldRight);
}
}
});
}
@Override
public void onClick(View v) {
Button b = (Button) v;
Toast t = Toast.makeText(this, b.getText() + " clicked", Toast.LENGTH_SHORT);
t.show();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment