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
반응형