Develop/Java

[Java] 자바 android Floating Widget 구현(2)

JunJangE 2021. 7. 8. 18:17

지난번에 자바로 android Floating Widget을 구현해보았다.

 

[Java] 자바 android Floating Widget 구현

android Floating Widget 구현을 자바로 알아보자. 우성 mainfests -> AndroidManifest.xml에서 앱을 다른 모든 앱 위에 표시할 수 있게 권한을 설정하고 Service를 시작하도록 호출하고 enabled를 true로 하여 활..

fre2-dom.tistory.com

그런데 위젯을 움직인 후, 그 자리가 아닌 양 사이드로 보내고 싶어 다시 한번 공부한 후 구현해보았다.

WidgetService 코드만 바꿔주면 구현할 수 있었다.

import android.app.Service;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.os.Build;
import android.os.CountDownTimer;
import android.os.IBinder;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.Nullable;
import java.util.Calendar;

public class  WidgetService extends Service {

    int LAYOUT_FLAG;
    View mFloatingView;
    WindowManager windowManager;
    ImageView imageClose;
    TextView tvWidth;
    float height, width;

    // 플로팅 위젯 뷰가 왼쪽에 있는지 오른쪽에 있는지 확인하는 변수
    // 처음에는 플로팅 위젯 뷰를 오른쪽에 표시하므로 false로 설정
    private boolean isLeft = false;


    @Nullable
    @Override
    public IBinder onBind(Intent intent){
        return null;
    }



    @Override
    public int onStartCommand(Intent intent, int flags, int startId){

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){

            LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;


        }else{

            LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_PHONE;

        }

        // 우리가 만든 플로팅 뷰 레이아웃 확장
        mFloatingView = LayoutInflater.from(this).inflate(R.layout.layout_widget, null);

        WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
                LAYOUT_FLAG,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                PixelFormat.TRANSLUCENT);


        // 보기 위치 지정
        // 처음에는보기가 오른쪽 상단 모서리에 추가되며 필요에 따라 x-y 좌표를 변경
        layoutParams.gravity = Gravity.TOP|Gravity.RIGHT;
        layoutParams.x = 0;
        layoutParams.y = 100;

        WindowManager.LayoutParams imageParams = new WindowManager.LayoutParams( 140,
                140,
                LAYOUT_FLAG,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                PixelFormat.TRANSLUCENT);
        imageParams.gravity = Gravity.BOTTOM|Gravity.CENTER;
        imageParams.y = 100;


        windowManager = (WindowManager)getSystemService(WINDOW_SERVICE);
        imageClose = new ImageView(this);
        imageClose.setImageResource(R.drawable.close);
        imageClose.setVisibility(View.INVISIBLE);
        windowManager.addView(imageClose, imageParams);
        windowManager.addView(mFloatingView, layoutParams);
        mFloatingView.setVisibility(View.VISIBLE);

        height = windowManager.getDefaultDisplay().getHeight();
        width  = windowManager.getDefaultDisplay().getWidth();

        tvWidth = (TextView) mFloatingView.findViewById(R.id.imageView);



        // 사용자의 터치 동작을 사용하여 플로팅 뷰를 드래그하여 이동
        tvWidth.setOnTouchListener(new View.OnTouchListener() {

            int initialx, initialy;
            float initialTouchX, initialTouchY;


            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {


                switch (motionEvent.getAction()){
                    case MotionEvent.ACTION_DOWN:

                        imageClose.setVisibility(View.VISIBLE);



                        //터치 위치 좌표 얻기
                        initialTouchX = motionEvent.getRawX();
                        initialTouchY = motionEvent.getRawY();

                        // 초기 위치 기억
                        initialx = layoutParams.x;
                        initialy = layoutParams.y;

                        return true;

                    case MotionEvent.ACTION_UP:

                        imageClose.setVisibility(view.GONE);


                        // 초기 좌표와 현재 좌표의 차이 가져 오기
                        layoutParams.x = initialx+(int) (initialTouchX-motionEvent.getRawX());
                        layoutParams.y = initialy+(int)(motionEvent.getRawY()-initialTouchY);


                        // 제거 이미지 주변 거리
                        if (layoutParams.y>(height * 0.6 )) {
                            stopSelf();

                        }
                        else {
                            //사용자가 플로팅 뷰를 드래그하면 위치 재설정
                            if ( layoutParams.x <=  500) {
                                isLeft = false;
                                layoutParams.x = 0;
                                windowManager.updateViewLayout(mFloatingView, layoutParams);

                            }
                            else {
                                isLeft = true;
                                layoutParams.x = 1000;
                                windowManager.updateViewLayout(mFloatingView, layoutParams);

                            }
                        }






                        return true;

                    case MotionEvent.ACTION_MOVE:

                        // 초기 좌표와 현재 좌표의 차이 가져 오기
                        layoutParams.x = initialx+(int)(initialTouchX-motionEvent.getRawX());
                        layoutParams.y = initialy +(int) (motionEvent.getRawY()- initialTouchY);

                        // 새로운 X 및 Y 좌표로 레이아웃 업데이트
                        windowManager.updateViewLayout(mFloatingView, layoutParams);

                        if (layoutParams.y> (height * 0.6)){

                            imageClose.setImageResource(R.drawable.close);
                        }
                        else {
                            imageClose.setImageResource(R.drawable.close);
                        }
                        return true;

                }
                return false;
            }
        });



        return START_STICKY;
    }


    // 앱이 종료될때 실행
    @Override
    public void onDestroy(){
        super.onDestroy();

        if(mFloatingView != null){
            windowManager.removeView(mFloatingView);

        }
        if (imageClose != null){

            windowManager.removeView(imageClose);
        }

    }
}

바뀐 코드만을 보면 다음과 같다.

private boolean isLeft = false;


			// 제거 이미지 주변 거리
                        if (layoutParams.y>(height * 0.6 )) {
                            stopSelf();

                        }
                        else {
                            //사용자가 플로팅 뷰를 드래그하면 위치 재설정
                            if ( layoutParams.x <=  500) {
                                isLeft = false;
                                layoutParams.x = 0;
                                windowManager.updateViewLayout(mFloatingView, layoutParams);

                            }
                            else {
                                isLeft = true;
                                layoutParams.x = 1000;
                                windowManager.updateViewLayout(mFloatingView, layoutParams);

                            }
                        }

뷰 위치에서 가운데를 기준으로 왼쪽에 위젯이 위치하면 왼쪽으로 보내고 오른쪽에 위치해 있으면 오른쪽으로 보내도록 했다. 그런데 위젯이 양 사이드로 움직일 때 너무 빠른 속도로 움직이는 것 같아 조금 느리게 움직이게 할 수 있는지 더 알아봐야 할 것 같다.

코드를 수정했다면 run을 눌러 잘 실행되는지 확인해본다.

위 코드를 실행하게 되면 다음 출력 화면과 같은 결과를 얻을 수 있다.

<결과화면>

참고

 

Android Floating Widget like Facebook Messenger Chat Head - Androhub

Floating widgets are the views that float over the screen. We all love the chat heads (or chat bubbles) from the popular Facebook Messenger. This provides very handy and easy access to the chat conversation screen no matter on which screen you are. Chat he

www.androhub.com