지난번에 위젯을 움직인 후, 그 자리가 아닌 양 사이드로 보내보았다.
이번에는 구글링과 공부를 통해 클릭했을 때 앱의 정보가 나오거나 앱 홈 화면으로 다시 돌아가는 방법과 그 외 코드적인 부분을 수정해 구현해보았다.
저번에 했던 위젯 코드랑 비슷하지만 다른 부분이 여러 곳이므로 주석을 잘 보면서 확인하면 좋을 것 같다.
위젯 코드는 다음과 같다.
import android.app.Service;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.graphics.Point;
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;
public class FloatingWidgetShowService extends Service{
WindowManager windowManager;
WindowManager.LayoutParams params ;
private int x_init_cord, y_init_cord ;
private final Point szWindow = new Point();
float height, width;
long time_start = 0, time_end = 0;
View floatingView, collapsedView, expandedView;
ImageView imageClose;
//플로팅 위젯 보기가 왼쪽에 있는지 오른쪽에 있는지 확인하는 변수
// 처음에는 플로팅 위젯 보기를 왼쪽에 표시하므로 true로 설정합니다.
private boolean isLeft = true;
public FloatingWidgetShowService() {
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
//윈도우매니저 초기화
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
// 우리가 만든 플로팅 뷰 레이아웃 확장
floatingView = LayoutInflater.from(this).inflate(R.layout.floating_widget_layout, null);
//창에 위젯 아이콘 뷰를 추가
params = new WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
// 보기 위치 지정
// 처음에는보기가 왼쪽 중앙에 추가되며 필요에 따라 x-y 좌표를 변경
params.gravity = Gravity.CENTER | Gravity.LEFT;
params.x = 0;
params.y = 100;
//창에 제거 이미지 뷰를 추가합니다.
WindowManager.LayoutParams imageParams = new WindowManager.LayoutParams( 140,
140,
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
// 보기 위치 지정
imageParams.gravity = Gravity.BOTTOM|Gravity.CENTER;
imageParams.y = 100;
// 제거 이미지 정보
imageClose = new ImageView(this);
imageClose.setImageResource(R.drawable.close_0);
// //초기에는 제거 이미지가 표시되지 않으므로 가시성을 GONE으로 설정
imageClose.setVisibility(View.GONE);
//윈도우에 뷰 추가
windowManager.addView(imageClose, imageParams);
windowManager.addView(floatingView, params);
// 접힌 뷰 레이아웃의 ID 찾기
expandedView = floatingView.findViewById(R.id.Layout_Expended);
collapsedView = floatingView.findViewById(R.id.Layout_Collapsed);
// 뷰 높이, 너비
height = windowManager.getDefaultDisplay().getHeight();
width = windowManager.getDefaultDisplay().getWidth();
// 확장된 위젯을 클릭할 경우
expandedView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
collapsedView.setVisibility(View.VISIBLE);
expandedView.setVisibility(View.GONE);
}
});
// 사용자가 확장 아이콘을 클릭하면 시작화면으로 이동
floatingView.findViewById(R.id.Widget_expand_Icon).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(FloatingWidgetShowService.this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
stopSelf();
}
});
//사용자의 터치 동작을 사용하여 플로팅 뷰를 드래그 앤 이동합니다.
floatingView.findViewById(R.id.MainParentRelativeLayout).setOnTouchListener(new View.OnTouchListener() {
int X_Axis, Y_Axis;
float TouchX, TouchY;
boolean inBounded = false; // 플로팅 뷰가 뷰를 제거할 경계인지 판단하는 변수
@Override
public boolean onTouch(View v, MotionEvent event) {
// 터치 위치 좌표 가져오기(위젯 클릭 이벤트)
int x_cord = (int) event.getRawX();
int y_cord = (int) event.getRawY();
// 제거 이미지 뷰 절대 좌표
int[] imageClose_location = new int[2];
imageClose.getLocationOnScreen(imageClose_location);
Log.d("Tag", String.valueOf(imageClose_location[0]));
Log.d("Tag", String.valueOf(imageClose_location[1]));
// 위젯 뷰 절대 좌표
int[] widget_location = new int[2];
floatingView.getLocationOnScreen(widget_location);
Log.d("ttt", String.valueOf(widget_location[0]));
Log.d("ttt", String.valueOf(widget_location[1]));
switch (event.getAction()) {
// ACTION_DOWN(View 를 손으로 누르기 시작하는 시점)
case MotionEvent.ACTION_DOWN:
// 제거 이미지 보임
imageClose.setVisibility(View.VISIBLE);
// 시작 시간
time_start = System.currentTimeMillis();
// 현재 좌표(위젯 클릭 이벤트)
x_init_cord = x_cord;
y_init_cord = y_cord;
//터치 위치 좌표 가져오기(위젯 드로우)
TouchX = event.getRawX();
TouchY = event.getRawY();
// 현재 좌표(위젯 드로우)
X_Axis = params.x;
Y_Axis = params.y;
return true;
// ACTION_MOVE(View 를 손으로 누르고 드래그 하는 시점)
case MotionEvent.ACTION_UP:
// 제거 이미지 숨김
imageClose.setVisibility(View.GONE);
// 초기 좌표와 현재 좌표의 차이 구하기
int x_diff = x_cord - x_init_cord;
int y_diff = y_cord - y_init_cord;
// 위젯 클릭 이벤트
// 클릭하는 동안 요소가 약간 움직이기 때문에 x_diff <5 && y_diff< 5를 확인
if (Math.abs(x_diff) < 5 && Math.abs(y_diff) < 5) {
// 종료 시간
time_end = System.currentTimeMillis();
// 또한 시작 시간과 종료 시간의 차이가 300ms 미만이어야 하는지 확인
if ((time_end - time_start) < 300)
collapsedView.setVisibility(View.GONE);
expandedView.setVisibility(View.VISIBLE);
}
// 이미지 뷰 절대 좌표가 조건 안에 있으면 제거한다.
if (imageClose_location[0] - imageParams.height <= widget_location[0] && imageClose_location[0] + imageParams.height >= widget_location[0]
&& imageClose_location[1] - imageParams.width <= widget_location[1] && imageClose_location[1] + imageParams.width >= widget_location[1]){
stopSelf();
}else {
// 사용자가 플로팅 뷰를 드래그하면 위치 재설정
resetPosition(x_cord);
}
return true;
// ACTION_UP(View 로부터 손을 뗀 시점)
case MotionEvent.ACTION_MOVE:
// 위젯 드로우
params.x = X_Axis + (int) (event.getRawX() - TouchX);
params.y = Y_Axis + (int) (event.getRawY() - TouchY);
// 새로운 X & Y 좌표로 레이아웃 업데이트
windowManager.updateViewLayout(floatingView, params);
// 제거 이미지에 가까이 가면 이미지 전환
if (imageClose_location[0] - imageParams.height <= widget_location[0] && imageClose_location[0] + imageParams.height >= widget_location[0]
&& imageClose_location[1] - imageParams.width <= widget_location[1] && imageClose_location[1] + imageParams.width >= widget_location[1]){
imageClose.setImageResource(R.drawable.close_1);
}
else {
imageClose.setImageResource(R.drawable.close_0);
}
return true;
}
return false;
}
});
}
/* 드래깅 시 플로팅 위젯 보기의 위치 재설정 */
private void resetPosition(int x_cord_now) {
if (x_cord_now <= width / 2) {
isLeft = true;
moveToLeft(x_cord_now);
} else {
isLeft = false;
moveToRight(x_cord_now);
}
}
/* 플로팅 위젯 보기를 왼쪽으로 이동하는 방법 */
private void moveToLeft(final int current_x_cord) {
final int x = (int) (width - current_x_cord);
// 움직이는 횟수와 시간 0으로 초기화
new CountDownTimer(0, 0) {
// 플로팅 뷰 매개변수 제거 가져오기
WindowManager.LayoutParams mParams = (WindowManager.LayoutParams) floatingView.getLayoutParams();
public void onTick(long t) {
long step = (500 - t) / 5;
mParams.x = -(int) (current_x_cord * current_x_cord * step);
// 새로운 X & Y 좌표로 레이아웃 업데이트
windowManager.updateViewLayout(floatingView, mParams);
}
public void onFinish() {
mParams.x = 0;
// 새로운 X & Y 좌표로 레이아웃 업데이트
windowManager.updateViewLayout(floatingView, mParams);
}
}.start();
}
/* 플로팅 위젯 보기를 오른쪽으로 이동하는 방법 */
private void moveToRight(final int current_x_cord) {
// 움직이는 횟수와 시간 0으로 초기화
new CountDownTimer(0, 0) {
// 플로팅 뷰 매개변수 제거 가져오기
WindowManager.LayoutParams mParams = (WindowManager.LayoutParams) floatingView.getLayoutParams();
public void onTick(long t) {
long step = (500 - t) / 5;
mParams.x = (int) (width + (current_x_cord * current_x_cord * step) - floatingView.getWidth());
// 새로운 X & Y 좌표로 레이아웃 업데이트
windowManager.updateViewLayout(floatingView, mParams);
}
public void onFinish() {
mParams.x = (int) (width- floatingView.getWidth());
// 새로운 X & Y 좌표로 레이아웃 업데이트
windowManager.updateViewLayout(floatingView, mParams);
}
}.start();
}
// 앱이 종료될때 실행
@Override
public void onDestroy(){
super.onDestroy();
if(floatingView != null){
windowManager.removeView(floatingView);
}
if (imageClose != null){
windowManager.removeView(imageClose);
}
}
main.activity는 전에 위젯을 만들었던 코드와 비슷하지만 혹시라도 구동이 안될 수 있음으로 코드를 올려보았다.
코드는 다음과 같다.
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
public static final int SYSTEM_ALERT_WINDOW_PERMISSION = 7;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 버튼 정의
Button button ;
button = (Button)findViewById(R.id.buttonShow);
// 권한을 확인한다.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {
RuntimePermissionForUser();
}
// 버튼 클릭
button.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View view) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
// 권한이 설정돼있으면 위젯 액티비티로 연결
startService(new Intent(MainActivity.this, FloatingWidgetShowService.class));
finish();
}
else if (Settings.canDrawOverlays(MainActivity.this)) {
// 권한이 설정돼있으면 위젯 액티비티로 연결
startService(new Intent(MainActivity.this, FloatingWidgetShowService.class));
finish();
}
else {
RuntimePermissionForUser();
Toast.makeText(MainActivity.this, "System Alert Window Permission Is Required For Floating Widget.", Toast.LENGTH_LONG).show();
}
}
});
}
// M 버전(안드로이드 6.0 마시멜로우 버전) 보다 같거나 큰 API에서만 설정창 이동 가능
public void RuntimePermissionForUser() {
Intent PermissionIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
startActivityForResult(PermissionIntent, SYSTEM_ALERT_WINDOW_PERMISSION);
}
}
여기서 바뀐 게 제일 많은 위젯 xml 코드를 보면 다음과 같다.
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:context="FloatingWidgetShowService">
<RelativeLayout
android:id="@+id/MainParentRelativeLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:ignore="UselessParent">
<!-- This layout is the Collapsed layout -->
<RelativeLayout
android:id="@+id/Layout_Collapsed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="visible">
<ImageView
android:id="@+id/Logo_Icon"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_marginTop="10dp"
android:src="@drawable/pin" />
</RelativeLayout>
<!-- 이 레이아웃은 확장 레이아웃입니다-->
<LinearLayout
android:id="@+id/Layout_Expended"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#FFF3E0"
android:gravity="center"
android:orientation="horizontal"
android:padding="8dp"
android:visibility="gone">
<ImageView
android:id="@+id/WebsiteLogoIcon"
android:layout_width="60dp"
android:layout_height="60dp"
android:src="@drawable/pin"
tools:ignore="ContentDescription" />
<LinearLayout
android:id="@+id/LinearLayout_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:paddingTop="5dp"
android:text="Android-Examples"
android:textAlignment="center"
android:textSize="18dp"
android:textColor="#000"
android:textStyle="bold" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="www.android-examples.com"
android:textAlignment="center"
android:textSize="13dp"
android:textColor="#000"
android:textStyle="bold" />
</LinearLayout>
<ImageView
android:id="@+id/Widget_expand_Icon"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_weight="1"
android:padding="10dp"
android:src="@drawable/expended" />
</LinearLayout>
</RelativeLayout>
</FrameLayout>
코드는 클릭하기 전과 클릭 후에 모습인 두 가지 모습을 모두 다 구현했고 추가적으로 클릭하고 나서 확장된 모습에서 버튼을 삽입해 위젯을 다양한 방법으로 응용해보았다.
코드를 수정했다면 run을 눌러 잘 실행되는지 확인해본다.
위 코드를 실행하게 되면 다음 출력 화면과 같은 결과를 얻을 수 있다.
참고
github
'Develop > Java' 카테고리의 다른 글
[AWS] 아마존 웹 서비스 Android + Amazon Cognito 로그인 구현 (0) | 2021.07.15 |
---|---|
[AWS] 아마존 웹 서비스 Android + Amazon Cognito 구현 (0) | 2021.07.14 |
[AWS] 아마존 웹 서비스 Amplify + Android 프로젝트 연동 (0) | 2021.07.12 |
[Java] 자바 android Floating Widget 구현(2) (0) | 2021.07.08 |
[Java] 자바 android Floating Widget 구현(1) (0) | 2021.07.07 |