Skip to content

Commit

Permalink
Add support for other kinds of drawer (#2)
Browse files Browse the repository at this point in the history
Add a `FullDraggableHelper` to hold the reusable part so that we can use it for other kinds of drawers.

* Make _drawerlayout_ as an optional and `compileOnly` dependency
* Rename `dismissDrawer` to `smoothCloseDrawer`
* Bump to 1.0.0
  • Loading branch information
drakeet committed Jan 3, 2021
1 parent 86f838b commit 244f91f
Show file tree
Hide file tree
Showing 7 changed files with 243 additions and 161 deletions.
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ In your `build.gradle`:

```groovy
dependencies {
implementation 'com.drakeet.drawer:drawer:0.9.0'
implementation 'com.drakeet.drawer:drawer:1.0.0'
// Optional: No need if you just use the FullDraggableHelper
implementation 'androidx.drawerlayout:drawerlayout:1.1.1'
}
```

Expand Down Expand Up @@ -50,10 +52,14 @@ Replace the main Layout of `DrawerLayout` with the `FullDraggableContainer` (or

**That's all, you're good to go!**

### Advanced usage

See `com.drakeet.drawer.FullDraggableHelper`

## TODO

- [x] Add support for the right drawer / RTL
- [ ] Add support for other kinds of drawer
- [x] Add support for other kinds of drawer

License
-------
Expand Down
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
buildscript {

ext.kotlinVersion = '1.4.21'
ext.buildConfig = ['versionCode' : 2,
'versionName' : "0.9.1",
ext.buildConfig = ['versionCode' : 3,
'versionName' : "1.0.0",
'compileSdkVersion': 30,
'minSdkVersion' : 19,
'targetSdkVersion' : 30]
Expand Down
2 changes: 1 addition & 1 deletion library/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,6 @@ android {
}

dependencies {
api 'androidx.drawerlayout:drawerlayout:1.1.1'
compileOnly 'androidx.drawerlayout:drawerlayout:1.1.1'
testImplementation 'junit:junit:4.13.1'
}
2 changes: 1 addition & 1 deletion library/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ POM_PACKAGING=aar

GROUP=com.drakeet.drawer

POM_DESCRIPTION=Easier and more flexible to create multiple types for Android RecyclerView.
POM_DESCRIPTION=Make Android DrawerLayout can be dragged out in real-time within the range of fullscreen.
POM_URL=https://github.com/PureWriter/FullDraggableDrawer
POM_SCM_URL=https://github.com/PureWriter/FullDraggableDrawer
POM_SCM_CONNECTION=scm:[email protected]:PureWriter/FullDraggableDrawer.git
Expand Down
188 changes: 34 additions & 154 deletions library/src/main/java/com/drakeet/drawer/FullDraggableContainer.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.drakeet.drawer;

import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
Expand All @@ -36,33 +32,17 @@
import java.lang.reflect.Method;
import java.util.List;

import static java.lang.Math.abs;
import static java.util.Objects.requireNonNull;

/**
* TODO: Add support for the right drawer
* TODO: Add support for other kinds of drawer
*
* @author Drakeet Xu
*/
public class FullDraggableContainer extends FrameLayout {

private float initialMotionX;
private float initialMotionY;
private float lastMotionX;
private float lastMotionY;
private final int touchSlop;
private final int swipeSlop;
private final int distanceThreshold;
private final int xVelocityThreshold;
public class FullDraggableContainer extends FrameLayout implements FullDraggableHelper.Callback {

private boolean isDraggingDrawer = false;
private boolean shouldOpenDrawer = false;
@NonNull
private final FullDraggableHelper helper;

@Nullable
private VelocityTracker velocityTracker = null;
private DrawerLayout drawerLayout;
private int gravity = Gravity.NO_GRAVITY;

public FullDraggableContainer(@NonNull Context context) {
this(context, null);
Expand All @@ -74,10 +54,7 @@ public FullDraggableContainer(@NonNull Context context, @Nullable AttributeSet a

public FullDraggableContainer(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
swipeSlop = dipsToPixels(8);
distanceThreshold = dipsToPixels(80);
xVelocityThreshold = dipsToPixels(150);
helper = new FullDraggableHelper(context, this);
}

@Override
Expand All @@ -96,131 +73,50 @@ private void ensureDrawerLayout() {

@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
boolean intercepted = false;
int action = event.getActionMasked();
float x = event.getX();
float y = event.getY();
if (action == MotionEvent.ACTION_DOWN) {
lastMotionX = initialMotionX = x;
lastMotionY = initialMotionY = y;
return false;
} else if (action == MotionEvent.ACTION_MOVE) {
if (canNestedViewScroll(this, false, (int) (x - lastMotionX), (int) x, (int) y)) {
return false;
}
lastMotionX = x;
float diffX = x - initialMotionX;
intercepted = abs(diffX) > touchSlop
&& abs(diffX) > abs(y - initialMotionY)
&& isDrawerEnabled(diffX);
}
return intercepted;
return helper.onInterceptTouchEvent(event);
}

private boolean canNestedViewScroll(View view, boolean checkSelf, int dx, int x, int y) {
if (view instanceof ViewGroup) {
ViewGroup group = (ViewGroup) view;
int scrollX = view.getScrollX();
int scrollY = view.getScrollY();
int count = group.getChildCount();
for (int i = count - 1; i >= 0; i--) {
View child = group.getChildAt(i);
if (child.getVisibility() != View.VISIBLE) continue;
if (x + scrollX >= child.getLeft()
&& x + scrollX < child.getRight()
&& y + scrollY >= child.getTop()
&& y + scrollY < child.getBottom()
&& canNestedViewScroll(child, true, dx, x + scrollX - child.getLeft(), y + scrollY - child.getTop())) {
return true;
}
}
}
return checkSelf && view.canScrollHorizontally(-dx);
@Override
@SuppressLint("ClickableViewAccessibility")
public boolean onTouchEvent(MotionEvent event) {
return helper.onTouchEvent(event);
}

@NonNull
@Override
@SuppressLint({ "RtlHardcoded", "ClickableViewAccessibility" })
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
int action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_MOVE: {
float diffX = x - initialMotionX;
if (isDrawerOpen() || !isDrawerEnabled(diffX)) {
return false;
}
float absDiffX = abs(diffX);
if (absDiffX > swipeSlop || isDraggingDrawer) {
if (velocityTracker == null) {
velocityTracker = VelocityTracker.obtain();
}
velocityTracker.addMovement(event);
boolean lastDraggingDrawer = isDraggingDrawer;
isDraggingDrawer = true;
shouldOpenDrawer = absDiffX > distanceThreshold;
public View getDrawerMainContainer() {
return this;
}

// Not allowed to change direction in a process
if (gravity == Gravity.NO_GRAVITY) {
gravity = diffX > 0 ? Gravity.LEFT : Gravity.RIGHT;
}
offsetDrawer(gravity, absDiffX - swipeSlop);
@Override
public boolean isDrawerOpen(int gravity) {
return drawerLayout.isDrawerOpen(gravity);
}

if (!lastDraggingDrawer) {
notifyDrawerDragging();
}
}
return isDraggingDrawer;
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP: {
if (isDraggingDrawer) {
if (velocityTracker != null) {
velocityTracker.computeCurrentVelocity(1000);
float xVelocity = velocityTracker.getXVelocity();
boolean fromLeft = (gravity == Gravity.LEFT);
if (xVelocity > xVelocityThreshold) {
shouldOpenDrawer = fromLeft;
} else if (xVelocity < -xVelocityThreshold) {
shouldOpenDrawer = !fromLeft;
}
}
if (shouldOpenDrawer) {
openDrawer(gravity);
} else {
dismissDrawer(gravity);
}
}
shouldOpenDrawer = false;
isDraggingDrawer = false;
gravity = Gravity.NO_GRAVITY;
if (velocityTracker != null) {
velocityTracker.recycle();
velocityTracker = null;
}
}
}
return true;
@Override
public boolean hasEnabledDrawer(int gravity) {
return drawerLayout.getDrawerLockMode(gravity) == DrawerLayout.LOCK_MODE_UNLOCKED
&& findDrawerWithGravity(gravity) != null;
}

@SuppressLint("RtlHardcoded")
private boolean isDrawerOpen() {
return drawerLayout.isDrawerOpen(Gravity.LEFT) || drawerLayout.isDrawerOpen(Gravity.RIGHT);
@Override
public void offsetDrawer(int gravity, float offset) {
setDrawerToOffset(gravity, offset);
drawerLayout.invalidate();
}

private void openDrawer(int gravity) {
@Override
public void smoothOpenDrawer(int gravity) {
drawerLayout.openDrawer(gravity, true);
}

private void dismissDrawer(int gravity) {
@Override
public void smoothCloseDrawer(int gravity) {
drawerLayout.closeDrawer(gravity, true);
}

private void offsetDrawer(int gravity, float dx) {
setDrawerToOffset(gravity, dx);
drawerLayout.invalidate();
}

private void notifyDrawerDragging() {
@Override
public void onDrawerDragging() {
List<DrawerLayout.DrawerListener> drawerListeners = getDrawerListeners();
if (drawerListeners != null) {
int listenerCount = drawerListeners.size();
Expand All @@ -243,13 +139,13 @@ protected List<DrawerLayout.DrawerListener> getDrawerListeners() {
}
}

protected void setDrawerToOffset(int gravity, float dx) {
protected void setDrawerToOffset(int gravity, float offset) {
View drawerView = findDrawerWithGravity(gravity);
float slideOffset = dx / requireNonNull(drawerView).getWidth();
float slideOffsetPercent = offset / requireNonNull(drawerView).getWidth();
try {
Method method = DrawerLayout.class.getDeclaredMethod("moveDrawerToOffset", View.class, float.class);
method.setAccessible(true);
method.invoke(drawerLayout, drawerView, slideOffset);
method.invoke(drawerLayout, drawerView, slideOffsetPercent);
drawerView.setVisibility(VISIBLE);
} catch (Exception e) {
// throw to let developer know the api is changed
Expand Down Expand Up @@ -277,20 +173,4 @@ private int getDrawerViewAbsoluteGravity(View drawerView) {
final int gravity = ((DrawerLayout.LayoutParams) drawerView.getLayoutParams()).gravity;
return GravityCompat.getAbsoluteGravity(gravity, ViewCompat.getLayoutDirection(drawerLayout));
}

@SuppressLint("RtlHardcoded")
private boolean isDrawerEnabled(float diffX) {
return diffX > 0 && hasUnlockedDrawer(Gravity.LEFT)
|| diffX < 0 && hasUnlockedDrawer(Gravity.RIGHT);
}

private boolean hasUnlockedDrawer(int gravity) {
return drawerLayout.getDrawerLockMode(gravity) == DrawerLayout.LOCK_MODE_UNLOCKED
&& findDrawerWithGravity(gravity) != null;
}

private int dipsToPixels(int dips) {
float scale = getContext().getResources().getDisplayMetrics().density;
return (int) (dips * scale + 0.5f);
}
}
Loading

0 comments on commit 244f91f

Please sign in to comment.