Android/개발 정보
Android floating button 움직이도록 만들기
일놀
2024. 6. 24. 11:19
728x90
반응형
안드로이드에서 화면에 떠다니는 버튼 만들기 ( Floating Button 만들기 )
그냥 floating button은 아이콘과 같이 하나만 넣을 수 있는데 ExtendedFloatingActionButton 같은 경우에는 아이콘과 텍스트를 다 사용할 수 있어서 이걸로 진행.
1. build.gradle의 dependencies 추가
implementation 'com.google.android.material:material:1.12.0'
2. 테마 추가
theme.xml파일에 floating버튼에 대한 테마 추가
Floating 버튼은 MaterialComponents의 구성요소이기 때문에 테마설정을 해주어야함.
<style name="FloatingTheme" parent="Theme.MaterialComponents.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
3. AndroidManifest.xml에 테마 설정
floating 버튼을 추가해야하는 Activity의 테마를 설정해준다.
<activity
android:screenOrientation="landscape"
android:exported="true"
android:name=".HomeActivity"
android:theme="@style/FloatingTheme"
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
2. xml에 ExtendedFloatingActionButton 추가
버튼 클릭 시 토글되어 나올 수 있도록 서브 button들도 넣어줌
서브 버튼들은 처음에 안보이도록 android:visibility="invisible"설정 해줌
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/menuFab"
android:layout_width="120dp"
android:layout_height="36dp"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_marginRight="16dp"
android:layout_marginTop="10dp"
android:textAlignment="center"
android:text="@string/menu"
android:backgroundTint="@color/colorPrimary"
android:textSize="10dp" />
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/fab1"
android:layout_width="120dp"
android:layout_height="36dp"
android:layout_below="@+id/menuFab"
android:layout_alignParentEnd="true"
android:layout_marginRight="16dp"
android:layout_marginTop="5dp"
android:text="fab1"
android:backgroundTint="@android:color/white"
android:textSize="10dp"
android:visibility="invisible"
android:textAlignment="center" />
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/fab2"
android:layout_width="120dp"
android:layout_height="36dp"
android:layout_below="@+id/fab1"
android:layout_alignParentEnd="true"
android:layout_marginRight="16dp"
android:layout_marginTop="5dp"
android:textAlignment="center"
android:text="fab2"
android:backgroundTint="@android:color/white"
android:textSize="10dp"
android:visibility="invisible"/>
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/fab3"
android:layout_width="120dp"
android:layout_height="36dp"
android:layout_below="@+id/fab2"
android:layout_alignParentEnd="true"
android:layout_marginRight="16dp"
android:layout_marginTop="5dp"
android:textAlignment="center"
android:text="fab3"
android:backgroundTint="@android:color/white"
android:textSize="10dp"
android:visibility="invisible"/>
3. Activity에 코드 선언
floating 버튼을 움직이게 하기 위해서는 OnTouch 를 사용해야한다.
OnTouch는 버튼에 대한 터치가 이루어졌을 때 발생하는 리스너인데 여기서 onClick리스너를 호출할 수도 있고 상황에 따라 onClick리스너를 대체할 수도 있다.
float prvDx; // 플로팅 버튼 초기 x축 위치
float prvDy; // 플로팅 버튼 초기 y축 위치
ExtendedFloatingActionButton fabMenu;
ExtendedFloatingActionButton fab1;
ExtendedFloatingActionButton fab2;
ExtendedFloatingActionButton fab3;
private boolean fabMain_status = false; // toggle을 위한 flag
@Override public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
fabMenu = findViewById(R.id.menuFab);
fab1 = findViewById(R.id.fab1);
fab2 = findViewById(R.id.fab2);
fab3 = findViewById(R.id.fab3);
// button 클릭 리스너
fabMenu.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(HomeActivity.this, "메뉴 버튼이 클릭되었습니다.", Toast.LENGTH_SHORT).show();
toggleFab();
}
});
// 터치 리스너
fabMenu.setOnTouchListener(new View.OnTouchListener() {
float initialX, initialY;
int lastAction;
@Override
public boolean onTouch(View view, MotionEvent event) {
switch (event.getActionMasked()) {
// 버튼 눌려졌을 때
case MotionEvent.ACTION_DOWN:
initialX = view.getX() - event.getRawX();
initialY = view.getY() - event.getRawY();
lastAction = MotionEvent.ACTION_DOWN;
prvDx = event.getRawX();
prvDy = event.getRawY();
return true;
// 버튼 움직일 때
case MotionEvent.ACTION_MOVE:
// 플로팅 버튼 자유롭게 움직이게 하기
// view.animate()
// .x(event.getRawX() + initialX)
// .y(event.getRawY() + initialY)
// .setDuration(0)
// .start();
//
// fab1.animate()
// .x(event.getRawX() + initialX)
// .y(event.getRawY() + initialY + fab1.getHeight() + dpToPx(5))
// .setDuration(0)
// .start();
//
// fab2.animate()
// .x(event.getRawX() + initialX)
// .y(event.getRawY() + initialY + fab1.getHeight() + fab2.getHeight() + dpToPx(10))
// .setDuration(0)
// .start();
//
// fab3.animate()
// .x(event.getRawX() + initialX)
// .y(event.getRawY() + initialY + fab1.getHeight() + fab2.getHeight() + fab3.getHeight() + dpToPx(15))
// .setDuration(0)
// .start();
// 플로팅 버튼 가로로만 움직이게 하기
float newX = event.getRawX() + initialX;
if (newX > 0 && newX + view.getWidth() < ((ViewGroup) view.getParent()).getWidth()) {
view.setX(newX);
fab1.setX(newX);
fab2.setX(newX);
fab3.setX(newX);
}
view.animate()
.setDuration(0)
.start();
fab1.animate()
.setDuration(0)
.start();
fab2.animate()
.setDuration(0)
.start();
fab3.animate()
.setDuration(0)
.start();
lastAction = MotionEvent.ACTION_MOVE;
return true;
// 버튼에서 떨어질 때
case MotionEvent.ACTION_UP:
float endX = event.getRawX();
float endY = event.getRawY();
// 이동범위가 미미할 때 버튼 클릭으로 인식하게 하기
if (Math.abs(prvDx-endX )< 5 || Math.abs(prvDy - endY) < 5) {
// Handle click event
view.performClick(); // setOnClickListener로 보내기
}
return true;
default:
return false;
}
}
});
// 플로팅 액션 버튼 클릭시 애니메이션 효과
public void toggleFab() {
if(fabMain_status) {
// 플로팅 액션 버튼 닫기
fab1.setVisibility(View.GONE);
fab2.setVisibility(View.GONE);
fab3.setVisibility(View.GONE);
}else {
// 플로팅 액션 버튼 열기
fab1.setVisibility(View.VISIBLE);
fab2.setVisibility(View.VISIBLE);
fab3.setVisibility(View.VISIBLE);
}
// 플로팅 버튼 상태 변경
fabMain_status = !fabMain_status;
}
// dp변환
private int dpToPx(int dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
}
}
728x90
반응형