Skip to content

Commit b363a7e

Browse files
hyuwahMuhamad Wahyudin
authored and
Muhamad Wahyudin
committed
Add DraggableListener for listening to the view while dragging / moving the view
- refactor code - update example code & readme
1 parent 990f277 commit b363a7e

File tree

9 files changed

+291
-105
lines changed

9 files changed

+291
-105
lines changed

README.md

+82-22
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
# DraggableView
22
[![](https://jitpack.io/v/hyuwah/DraggableView.svg)](https://jitpack.io/#hyuwah/DraggableView)
33

4-
DraggableView is an Android library to make floating draggable view easy, currently it only extends from ImageView.
5-
Now you can make any view (or viewgroup) draggable using extensions on Kotlin & provided utils class on Java
4+
DraggableView is an Android library to make floating draggable view easy.
65

76
![Preview](https://miro.medium.com/max/314/1*dMzIJlT12hmSTkVkzNnxEQ.gif)
87

@@ -35,7 +34,11 @@ dependencies {
3534

3635
## Usage
3736

38-
### Customizable Attributes
37+
### CustomView (XML)
38+
39+
Currently i've only provide CustomView that extends ImageView. For other view, see **Programmatically** usage below
40+
41+
#### Customizable Attributes
3942

4043
**DraggableImageView**
4144

@@ -44,12 +47,7 @@ Attribute | Value (Default) | XML | Code
4447
Animate | true, false (false) | animate | setAnimate(boolean isAnimate)
4548
Sticky Axis | NON_STICKY, STICKY_AXIS_X, STICKY_AXIS_Y, STICKY_AXIS_XY (NON_STICKY) | sticky | setStickyAxis(int axis)
4649

47-
48-
### Basic sample
49-
50-
#### Using xml view (DraggableImageView)
51-
52-
On Layout XML file
50+
#### On Layout XML file
5351
```xml
5452
<io.github.hyuwah.draggableviewlib.DraggableImageView
5553
android:src="@mipmap/ic_launcher_round"
@@ -58,18 +56,33 @@ On Layout XML file
5856
android:layout_height="wrap_content"/>
5957
```
6058

61-
On Activity / Fragment file
59+
#### On Activity / Fragment file
6260
```kotlin
6361
var dv = findViewById<DraggableImageView>(R.id.draggableView)
6462
dv.setOnClickListener {
6563
// TODO on click
6664
}
6765
```
6866

69-
#### Using extension (Kotlin only)
67+
You can add a DraggableListener programmatically via `setListener()` method directly on the view, see below explanation about the listener
68+
69+
### Programmatically
70+
71+
#### Using extension (Kotlin)
7072

7173
You can extent any view or viewgroup to be draggable (i.e. Button, FrameLayout, Linearlayout, LottieView, etc)
7274

75+
```kotlin
76+
// Method signature
77+
fun View.makeDraggable(
78+
stickyAxis: Draggable.STICKY = Draggable.STICKY.NONE,
79+
animated: Boolean = true,
80+
draggableListener: DraggableListener? = null
81+
){
82+
...
83+
}
84+
```
85+
7386
Here's some example using TextView:
7487

7588
```xml
@@ -83,22 +96,44 @@ Here's some example using TextView:
8396
```kotlin
8497
var tv = findViewById<TextView>(R.id.tv_test_draggable)
8598

86-
tv.makeDraggable(Draggable.STICKY.AXIS_X, true) // default is STICKY.NONE & animated true
99+
tv.makeDraggable(Draggable.STICKY.AXIS_X, false) // set sticky axis to x & animation to false
100+
tv.makeDraggable(Draggable.STICKY.AXIS_XY) // set sticky axis to xy
101+
tv.makeDraggable() // all default
87102

88-
// First param is the axis:
103+
// First param is the axis (optional)
89104
// - Draggable.STICKY.AXIS_X
90105
// - Draggable.STICKY.AXIS_Y
91106
// - Draggable.STICKY.AXIS_XY
92-
// - Draggable.STICKY.NONE
107+
// - Draggable.STICKY.NONE (default)
108+
109+
// Second param is animation flag (optional)
110+
// - true or false (default is true)
111+
// *Sticky.NONE doesn't get affected by this flag
93112

94-
// Second param is animation toggle
95-
// - true or false
113+
// Third param is listener (optional)
114+
// - DraggableListener implementation (default is null)
96115
```
97116

98-
#### Using DraggableUtils (on Java)
117+
#### Using DraggableUtils (Java)
99118

100119
If you're on java class, you could do it with the help of DraggableUtils
101120

121+
```java
122+
class DraggableUtils {
123+
124+
// Method signature
125+
public static void makeDraggable(
126+
View $self,
127+
Draggable.STICKY stickyAxis,
128+
boolean animated,
129+
DraggableListener draggableListener
130+
) {
131+
...
132+
}
133+
}
134+
135+
```
136+
102137
Here's some example using Button:
103138

104139
```xml
@@ -112,20 +147,45 @@ Here's some example using Button:
112147
```java
113148
Button button = findViewById(R.id.tv_test_draggable);
114149

115-
DraggableUtils.makeDraggable(button, Draggable.STICKY.AXIS_X, true); // default is STICKY.NONE & animated true
150+
DraggableUtils.makeDraggable(button, Draggable.STICKY.AXIS_X, false) // set sticky axis to x & animation to false
151+
DraggableUtils.makeDraggable(button, Draggable.STICKY.AXIS_XY) // set sticky axis to xy
152+
DraggableUtils.makeDraggable(button) // all default
116153

117154
// First param is the view
118155

119-
// Second param is the axis:
156+
// Second param is the axis (optional)
120157
// - Draggable.STICKY.AXIS_X
121158
// - Draggable.STICKY.AXIS_Y
122159
// - Draggable.STICKY.AXIS_XY
123-
// - Draggable.STICKY.NONE
160+
// - Draggable.STICKY.NONE (default)
161+
162+
// Third param is animation flag (optional)
163+
// - true or false (default is true)
164+
// *Sticky.NONE doesn't get affected by this flag
124165

125-
// Third param is animation toggle
126-
// - true or false
166+
// Fourth param is listener (optional)
167+
// - DraggableListener implementation (default is null)
127168
```
128169

170+
#### DraggableListener
171+
There's an interface `DraggableListener` to listen to the `View` while being dragged / moved
172+
173+
```kotlin
174+
interface DraggableListener {
175+
fun onViewMove(view: View)
176+
}
177+
```
178+
Just pass the implementation of the interface to `makeDraggable` method
179+
180+
```kotlin
181+
someView.makeDraggable(object: DraggableListener{
182+
override fun onViewMove(view: View){
183+
// Do something, get coordinates of view, etc
184+
}
185+
})
186+
187+
// *Java counterpart must supply all 3 other params to use the listener
188+
```
129189

130190
Check example module [kotlin](https://github.com/hyuwah/DraggableView/blob/master/example/src/main/java/io/github/hyuwah/draggableview/MainActivity.kt), [java](https://github.com/hyuwah/DraggableView/blob/master/example/src/main/java/io/github/hyuwah/draggableview/JavaMainActivity.java) for actual implementation
131191

draggableviewlib/src/main/java/io/github/hyuwah/draggableviewlib/Draggable.kt

+1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ object Draggable {
1010
}
1111

1212
const val DRAG_TOLERANCE = 16
13+
const val DURATION_MILLIS = 250L
1314
}

draggableviewlib/src/main/java/io/github/hyuwah/draggableviewlib/DraggableImageView.kt

+80-46
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import android.view.View
77
import android.widget.ImageView
88
import io.github.hyuwah.draggableviewlib.Draggable.DRAG_TOLERANCE
99
import kotlin.math.abs
10+
import kotlin.math.max
11+
import kotlin.math.min
1012

1113
/**
1214
* 29/01/2019
@@ -25,10 +27,12 @@ class DraggableImageView(context: Context, attrs: AttributeSet) : ImageView(cont
2527
private var stickyAxis: Int
2628
private var mAnimate: Boolean
2729

30+
private var draggableListener: DraggableListener? = null
31+
2832
// Coordinates
29-
private var widgetXFirst: Float = 0F
33+
private var widgetInitialX: Float = 0F
3034
private var widgetDX: Float = 0F
31-
private var widgetYFirst: Float = 0F
35+
private var widgetInitialY: Float = 0F
3236
private var widgetDY: Float = 0F
3337

3438
init {
@@ -49,90 +53,116 @@ class DraggableImageView(context: Context, attrs: AttributeSet) : ImageView(cont
4953
*/
5054
private fun draggableSetup() {
5155
this.setOnTouchListener { v, event ->
52-
val viewParent: View = (v.parent as View)
53-
val PARENT_HEIGHT = viewParent.height
54-
val PARENT_WIDTH = viewParent.width
56+
val viewParent = v.parent as View
57+
val parentHeight = viewParent.height
58+
val parentWidth = viewParent.width
59+
val xMax = parentWidth - v.width
60+
val xMiddle = parentWidth / 2
61+
val yMax = parentHeight - v.height
62+
val yMiddle = parentHeight / 2
5563

5664
when (event.actionMasked) {
5765
MotionEvent.ACTION_DOWN -> {
58-
this.widgetDX = v.x - event.rawX
59-
this.widgetDY = v.y - event.rawY
60-
this.widgetXFirst = v.x
61-
this.widgetYFirst = v.y
66+
widgetDX = v.x - event.rawX
67+
widgetDY = v.y - event.rawY
68+
widgetInitialX = v.x
69+
widgetInitialY = v.y
6270
}
6371
MotionEvent.ACTION_MOVE -> {
64-
// Screen border Collision
65-
var newX = event.rawX + this.widgetDX
66-
newX = Math.max(0F, newX)
67-
newX = Math.min((PARENT_WIDTH - v.width).toFloat(), newX)
72+
var newX = event.rawX + widgetDX
73+
newX = max(0F, newX)
74+
newX = min(xMax.toFloat(), newX)
6875
v.x = newX
6976

70-
var newY = event.rawY + this.widgetDY
71-
newY = Math.max(0F, newY)
72-
newY = Math.min((PARENT_HEIGHT - v.height).toFloat(), newY)
77+
var newY = event.rawY + widgetDY
78+
newY = max(0F, newY)
79+
newY = min(yMax.toFloat(), newY)
7380
v.y = newY
81+
82+
draggableListener?.onViewMove(v)
7483
}
7584
MotionEvent.ACTION_UP -> {
76-
// If Sticky
77-
when (this.stickyAxis) {
85+
when (stickyAxis) {
7886
STICKY_AXIS_X -> {
79-
if (event.rawX >= PARENT_WIDTH / 2) {
80-
if (this.mAnimate)
81-
v.animate().x((PARENT_WIDTH) - (v.width).toFloat()).setDuration(250).start()
87+
if (event.rawX >= xMiddle) {
88+
if (mAnimate)
89+
v.animate().x(xMax.toFloat())
90+
.setDuration(Draggable.DURATION_MILLIS)
91+
.setUpdateListener { draggableListener?.onViewMove(v) }
92+
.start()
8293
else
83-
v.x = (PARENT_WIDTH) - (v.width).toFloat()
94+
v.x = xMax.toFloat()
8495
} else {
85-
if (this.mAnimate)
86-
v.animate().x(0F).setDuration(250).start()
96+
if (mAnimate)
97+
v.animate().x(0F).setDuration(Draggable.DURATION_MILLIS)
98+
.setUpdateListener { draggableListener?.onViewMove(v) }
99+
.start()
87100
else
88101
v.x = 0F
89102
}
90103
}
91104
STICKY_AXIS_Y -> {
92-
if (event.rawY >= PARENT_HEIGHT / 2) {
93-
if (this.mAnimate)
94-
v.animate().y((PARENT_HEIGHT) - (v.height).toFloat()).setDuration(250).start()
105+
if (event.rawY >= yMiddle) {
106+
if (mAnimate)
107+
v.animate().y(yMax.toFloat())
108+
.setDuration(Draggable.DURATION_MILLIS)
109+
.setUpdateListener { draggableListener?.onViewMove(v) }
110+
.start()
95111
else
96-
v.y = (PARENT_HEIGHT) - (v.height).toFloat()
112+
v.y = yMax.toFloat()
97113
} else {
98-
if (this.mAnimate)
99-
v.animate().y(0F).setDuration(250).start()
114+
if (mAnimate)
115+
v.animate().y(0F)
116+
.setDuration(Draggable.DURATION_MILLIS)
117+
.setUpdateListener { draggableListener?.onViewMove(v) }
118+
.start()
100119
else {
101-
if (this.mAnimate)
102-
v.animate().y(0F).setDuration(250).start()
120+
if (mAnimate)
121+
v.animate().y(0F).setDuration(Draggable.DURATION_MILLIS)
122+
.setUpdateListener { draggableListener?.onViewMove(v) }
123+
.start()
103124
else
104125
v.y = 0F
105126
}
106127
}
107128
}
108129
STICKY_AXIS_XY -> {
109-
if (event.rawX >= PARENT_WIDTH / 2) {
110-
if (this.mAnimate)
111-
v.animate().x((PARENT_WIDTH) - (v.width).toFloat()).setDuration(250).start()
130+
if (event.rawX >= xMiddle) {
131+
if (mAnimate)
132+
v.animate().x(xMax.toFloat())
133+
.setDuration(Draggable.DURATION_MILLIS)
134+
.setUpdateListener { draggableListener?.onViewMove(v) }
135+
.start()
112136
else
113-
v.x = (PARENT_WIDTH) - (v.width).toFloat()
137+
v.x = xMax.toFloat()
114138
} else {
115-
if (this.mAnimate)
116-
v.animate().x(0F).setDuration(250).start()
139+
if (mAnimate)
140+
v.animate().x(0F).setDuration(Draggable.DURATION_MILLIS)
141+
.setUpdateListener { draggableListener?.onViewMove(v) }
142+
.start()
117143
v.x = 0F
118144
}
119145

120-
if (event.rawY >= PARENT_HEIGHT / 2) {
121-
if (this.mAnimate)
122-
v.animate().y((PARENT_HEIGHT) - (v.height).toFloat()).setDuration(250).start()
146+
if (event.rawY >= yMiddle) {
147+
if (mAnimate)
148+
v.animate().y(yMax.toFloat())
149+
.setDuration(Draggable.DURATION_MILLIS)
150+
.setUpdateListener { draggableListener?.onViewMove(v) }
151+
.start()
123152
else
124-
v.y = (PARENT_HEIGHT) - (v.height).toFloat()
153+
v.y = yMax.toFloat()
125154
} else {
126-
if (this.mAnimate)
127-
v.animate().y(0F).setDuration(250).start()
155+
if (mAnimate)
156+
v.animate().y(0F).setDuration(Draggable.DURATION_MILLIS)
157+
.setUpdateListener { draggableListener?.onViewMove(v) }
158+
.start()
128159
else
129160
v.y = 0F
130161
}
131162
}
132163
}
133164

134-
// Will register as clicked if not moved for 16px in both X & Y
135-
if (abs(v.x - widgetXFirst) <= DRAG_TOLERANCE && abs(v.y - widgetYFirst) <= DRAG_TOLERANCE) {
165+
if (abs(v.x - widgetInitialX) <= DRAG_TOLERANCE && abs(v.y - widgetInitialY) <= DRAG_TOLERANCE) {
136166
performClick()
137167
}
138168
}
@@ -169,4 +199,8 @@ class DraggableImageView(context: Context, attrs: AttributeSet) : ImageView(cont
169199
invalidate()
170200
requestLayout()
171201
}
202+
203+
fun setListener(draggableListener: DraggableListener?) {
204+
this.draggableListener = draggableListener
205+
}
172206
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package io.github.hyuwah.draggableviewlib
2+
3+
import android.view.View
4+
5+
interface DraggableListener {
6+
7+
fun onViewMove(view: View)
8+
9+
}

0 commit comments

Comments
 (0)