와챠의 우당탕탕 개발 기록장
[안드로이드] draggable(movable), clickable view 만들기/onTouch, onClick Listener 동시에 달기(밀리의 서재의 그것) 본문
[안드로이드] draggable(movable), clickable view 만들기/onTouch, onClick Listener 동시에 달기(밀리의 서재의 그것)
minWachya 2021. 8. 16. 16:45책을 좀 읽어야겠어서 밀리의 서재를 깔았는데
헉 이거 어케 만든 거임!?!?! 하는 게 또 있었다.
뭐냐면 이거임... :

이렇게
1, 내가 원하는 위치에 드래그도 되고
2, 다시 벽에 붙기도 하고
3, 심지어 클릭도 되는!!!!!
귀엽고 멋진 기능이었다.
이거 관련 라이브러리가 분명 어디 있을 거 같은데
못찾겠어서...^
직접 만들어봤다.
내가 만든 것 :

완전 똑같지는 않지만 내가 구현하고 싶었던 기능들은 다 구현해보았다!!!!!
activity_main.xml

<?xml version="1.0" encoding="utf-8"?> | |
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:app="http://schemas.android.com/apk/res-auto" | |
xmlns:tools="http://schemas.android.com/tools" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
tools:context=".MainActivity" | |
android:orientation="vertical" | |
android:background="@color/white"> | |
<!--floating/draggable/clickable layout--> | |
<!--책 이미지와 책 정보를 담을 레이아웃--> | |
<LinearLayout | |
android:id="@+id/floatingLayout" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:orientation="horizontal" | |
android:gravity="center"> | |
<!--책 정보 레이아웃 --> | |
<LinearLayout | |
android:id="@+id/innerLayout" | |
android:layout_width="180dp" | |
android:layout_height="80dp" | |
android:background="@drawable/custom_layout" | |
android:visibility="gone" | |
android:orientation="horizontal" | |
android:gravity="center"> | |
<!--책 제목, 읽은 정도--> | |
<LinearLayout | |
android:id="@+id/inner1" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:orientation="vertical" | |
android:layout_weight="7"> | |
<TextView | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:text="애린왕자" | |
android:textColor="@color/black" | |
android:textStyle="bold" | |
android:textSize="14sp" | |
android:layout_marginStart="10dp"/> | |
<TextView | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:text="10%" | |
android:textColor="@color/black" | |
android:textSize="10sp" | |
android:layout_marginStart="10dp"/> | |
</LinearLayout> | |
<!--북마크--> | |
<LinearLayout | |
android:id="@+id/inner2" | |
android:layout_width="wrap_content" | |
android:layout_height="match_parent" | |
android:background="@color/gray" | |
android:layout_weight="3" | |
android:gravity="center"> | |
<ImageView | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:src="@drawable/ic_baseline_bookmark_24" | |
android:contentDescription="북마크" /> | |
</LinearLayout> | |
</LinearLayout> | |
<!--책 이미지--> | |
<ImageView | |
android:layout_width="50dp" | |
android:layout_height="80dp" | |
android:layout_marginStart="5dp" | |
android:src="@drawable/img_book" | |
android:contentDescription="책 이미지" /> | |
</LinearLayout> | |
</LinearLayout> |
floatingView 클릭했을 때 innerLayout이 이동하는 애니메이션들
anim > transform_visible.xml
<?xml version="1.0" encoding="utf-8"?> | |
<!--innerLayout이 visible일 때 애니메이션--> | |
<set xmlns:android="http://schemas.android.com/apk/res/android"> | |
<!--(-60%)에서 (20%)만큼 (0.2초간)이동--> | |
<translate | |
android:duration="200" | |
android:fromXDelta="-60%" | |
android:toXDelta="20%"/> | |
<!--현재 위치에서 (-20%)만큼 (0.2초간)이동--> | |
<translate | |
android:startOffset="200" | |
android:duration="200" | |
android:fromXDelta="0" | |
android:toXDelta="-20%"/> | |
</set> |
anim > transform_gone.xml
<?xml version="1.0" encoding="utf-8"?> | |
<!--innerLayout이 gone일 때 애니메이션--> | |
<set xmlns:android="http://schemas.android.com/apk/res/android"> | |
<!--(160%)에서 (-10%)만큼 (0.2초간)이동--> | |
<translate | |
android:duration="200" | |
android:fromXDelta="160%" | |
android:toXDelta="-10%" /> | |
<!--현재 위치에서 (10%)만큼 (0.2초간)이동--> | |
<translate | |
android:startOffset="200" | |
android:duration="200" | |
android:fromXDelta="0" | |
android:toXDelta="10%" /> | |
</set> |
MainActivity.kt
package com.example.millietest | |
import android.annotation.SuppressLint | |
import android.os.Bundle | |
import android.view.MotionEvent | |
import android.view.View | |
import android.view.animation.AnimationUtils | |
import android.widget.LinearLayout | |
import android.widget.Toast | |
import androidx.appcompat.app.AppCompatActivity | |
import kotlin.math.abs | |
import kotlin.math.max | |
import kotlin.math.min | |
class MainActivity : AppCompatActivity() { | |
private var widgetInitialX: Float = 0F | |
private var widgetDX: Float = 0F | |
private var widgetInitialY: Float = 0F | |
private var widgetDY: Float = 0F | |
@SuppressLint("ClickableViewAccessibility") | |
override fun onCreate(savedInstanceState: Bundle?) { | |
super.onCreate(savedInstanceState) | |
setContentView(R.layout.activity_main) | |
val floatingLayout = findViewById<LinearLayout>(R.id.floatingLayout) | |
val innerLayout = findViewById<LinearLayout>(R.id.innerLayout) | |
val inner1 = findViewById<LinearLayout>(R.id.inner1) | |
val inner2 = findViewById<LinearLayout>(R.id.inner2) | |
// 사용자가 드래그한대로 움직이기 | |
floatingLayout.setOnTouchListener { v, event -> | |
val viewParent = v.parent as View | |
val parentHeight = viewParent.height | |
val parentWidth = viewParent.width | |
val xMax = parentWidth - v.width | |
val yMax = parentHeight - v.height | |
when (event.action) { | |
// 드래그 시작 | |
MotionEvent.ACTION_DOWN -> { | |
// 시작 위치 저장 | |
widgetDX = v.x - event.rawX | |
widgetDY = v.y - event.rawY | |
widgetInitialX = v.x | |
widgetInitialY = v.y | |
true | |
} | |
// 드래그 중 | |
MotionEvent.ACTION_MOVE -> { | |
// 드래그된 위치로 view 위치 변경 | |
var newX = event.rawX + widgetDX | |
newX = max(0F, newX) | |
newX = min(xMax.toFloat(), newX) | |
v.x = newX | |
var newY = event.rawY + widgetDY | |
newY = max(0F, newY) | |
newY = min(yMax.toFloat(), newY) | |
v.y = newY | |
true | |
} | |
// 드래그 끝 | |
MotionEvent.ACTION_UP -> { | |
// y는 그대로, x만 벽에 달라붙게 하기 | |
// v.animate().x(xMax.toFloat()).setDuration(300L).start() // 이렇게 하면 오른쪽 벽에 달라붙음 | |
// v.animate().x(0f).setDuration(300L).start() // 왼쪽 벽에 달라붙기 | |
// 벽 안으롱 들어갔다가 나오는 (튕기는) 애니메이션 달기 | |
v.animate().x(-50f).setDuration(200L).withEndAction { | |
v.animate().x(0f).setDuration(200L).start() | |
}.start() | |
// 이동이 별로 없다면 터치로 인식 | |
if (abs(v.x - widgetInitialX) <= 16 && abs(v.y - widgetInitialY) <= 16) | |
v.performClick() | |
true | |
} | |
else -> false | |
} | |
} | |
// innerLayout이 보일 때와 안 보일 때 애니메이션 달기 | |
val aniVisible = AnimationUtils.loadAnimation(applicationContext,R.anim.translate_visible) | |
val aniGone = AnimationUtils.loadAnimation(applicationContext,R.anim.transform_gone) | |
floatingLayout.setOnClickListener { | |
// innerLayout 보이기 | |
if (innerLayout.visibility == View.GONE) { | |
innerLayout.visibility = View.VISIBLE | |
floatingLayout.startAnimation(aniVisible) | |
} | |
// innerLayout 사라지기 | |
else { | |
innerLayout.visibility = View.GONE | |
floatingLayout.startAnimation(aniGone) | |
} | |
} | |
// innerLayout 안의 child에 클릭 리스너 달기 | |
inner1.setOnClickListener { | |
Toast.makeText(applicationContext, "애린왕자 : 10%", Toast.LENGTH_SHORT).show() | |
} | |
inner2.setOnClickListener { | |
Toast.makeText(applicationContext, "북마크 클릭", Toast.LENGTH_SHORT).show() | |
} | |
} | |
} |
floatingLayout 참고
안드로이드 간단히 View 움직이게 하기 (Drag and Drop)
View에 Touch 이벤트로 움직이는 모션을 구현하고자 할 때 var moveX = 0f var moveY = 0f move_view.setOnTouchListener { v, event -> when(event.action) { MotionEvent.ACTION_DOWN -> { moveX = v.x - event...
kimch3617.tistory.com
https://developer.android.com/guide/topics/ui/drag-drop#StartDrag
드래그 앤 드롭 | Android 개발자 | Android Developers
Android 드래그 앤 드롭 프레임워크를 사용하면 사용자가 그래픽 드래그 앤 드롭 동작을 사용하여 데이터를 옮길 수 있습니다. 이 작업은 자체 앱의 뷰 간에 또는 멀티 윈도우 모드를 사용 설정한
developer.android.com
https://stackoverflow.com/questions/46370836/android-movable-draggable-floating-action-button-fab
Android - Movable/Draggable Floating Action Button (FAB)
I am using a FloatingActionButton in my app. Occasionally, it overlaps essential content, so I would like to make it so the user can drag the FAB out of the way. No drag and drop functionality, pe...
stackoverflow.com
https://github.com/hyuwah/DraggableView
GitHub - hyuwah/DraggableView: DraggableView is an Android library to make floating draggable view easily using extensions on Ko
DraggableView is an Android library to make floating draggable view easily using extensions on Kotlin & provided utils class on Java - GitHub - hyuwah/DraggableView: DraggableView is an Android...
github.com
animation 참고
https://m.blog.naver.com/tkddlf4209/220700530627
[Android] 안드로이드 애니메이션(Animation)효과 주기 트윈애니메이션(TweenAnimation)
오늘은 트윈애니메이션(TweenAnimation)을 사용해 보도록 하겠습니다. * 트윈애니메이션 : 위치나 크기, ...
blog.naver.com
Crocus
Beginner와 Developer사이의 Crocus
www.crocus.co.kr
+)아니 분명 관련 라이브러리가 있을 거 같은데,,,,,,,,,,이상하다
++)아!! 이거 만들면서 재밌는 일이 있었다.
내가 코드를 잘못 짰다고 생각했는데 이게 잘 돌아가는 거다 ㅋㅋㅋㅋㅋ
실행 화면을 보면서 물음표를 백만개 띄운 그 순간이 참 재밌었다...킥킥
그동안 코딩하면서 "왜 안되지?" 하는 순간이 많았는데
이번엔 첨으로 "왜 되지?!"하는 순간이 생겨서 넘 재밌었다.
'코딩 일기장 > Android(Kotlin)' 카테고리의 다른 글
Android 12 Splash + 앱 시작 시간 공부 (2) | 2022.01.24 |
---|---|
[Android/Kotlin] recyclerview drag and drop/swipe 기능 만들기 (10) | 2021.08.26 |
[안드로이드] 이미지에서 색상 추출(Palette), toolbar 커스텀 (트위터 따라하기) (0) | 2021.08.15 |
[안드로이드] 메일 보내기 (4) | 2021.08.13 |
[안드로이드] 가로 슬라이드 리사이클러뷰 (0) | 2021.07.31 |