[Android] Hướng dẫn tạo Floating View như ứng dụng Messenger
Floating View là view trôi trên màn hình, thường gặp ở một số ứng dụng nhắn tin như Messenger, Zalo,... dưới dạng bong bóng chat. Người dùng có thể dễ dàng kéo thả, thao tác với view này ngay cả khi đang mở ứng dụng khác. Trong bài viết này, mình sẽ hướng dẫn các bạn tạo floating view tương tự như Messenger của Facebook.
1. Giới thiệu
Floating view là view trôi trên màn hình, nó rất thuận tiện cho việc thao tác đa nhiệm, 1 người có thể làm việc trên các ứng dụng khác nhau và kiểm soát chúng cùng 1 lúc. Điều đó có nghĩa là nếu bạn đang ở trong các ứng dụng tính toán, bạn có thể thấy được tin nhắn đến và trả lời tin nhắn thông qua bong bóng chat của Messenger nổi trên màn hình
Trong bài viết này, chúng ta sẽ tìm hiểu làm thế nào để tạo floating view đơn giản và cho phép người sử dụng để kéo chúng trên màn hình.
2. Bắt đầu
2.1. Tạo mới Project
Các bạn mở Android Studio, tạo mới ứng dụng với một activity: MainActivity và layout là activity_main.xml như sau :
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/btn_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="START" />
<Button
android:id="@+id/btn_stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/btn_start"
android:layout_centerHorizontal="true"
android:text="STOP" />
</RelativeLayout>
Trong MainActivity, chúng ta sẽ có 2 button START, STOP để mở và tắt view giống MessengerFacebook.
2.2 "Draw over other apps" permission
Để view có thể kéo thả lên trên các ứng dụng khác, bạn phải yêu cầu ứng dụng của mình được sử dụng permison ACTION_MANAGE_OVERLAY_PERMISSION. Do đó, xử lý trong MainActivity như sau :
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (!Settings.canDrawOverlays(this)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, 0);
}
}
Ngoài ra, bạn cần phải thêm permission trong AndroidManifest.xml như sau :
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
Lúc này, khi chạy ứng dụng, app mở lên sẽ chuyển sang màn hình setting, để bạn có thể cho phép ứng dụng của mình được hiển thị đè lên các ứng dụng khác như sau :
Bạn hãy enable permission này lên để chúng ta tiếp tục các phần sau.
2.3. Service
Chúng ta sẽ tạo một Service để điểu khiển view mà chúng ta mong muốn. Các bạn hãy tạo một class mới, đặt tên là MyService.
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import androidx.annotation.Nullable;
public class MyService extends Service {
@Override
public void onCreate() {
super.onCreate();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
Nhớ khai báo trong AndroidManifest.xml như sau :
<service
android:name=".MyService"
android:exported="true" />
Trong MyService, chúng ta sẽ tạo ra một ImageView, hiển thị icon của app, và xử lý logic khi người dùng kéo thả như sau :
package com.demo.floating;
import android.app.Service;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.os.IBinder;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
import androidx.annotation.Nullable;
public class MyService extends Service {
private WindowManager mWindowManager;
private ImageView image;
@Override
public void onCreate() {
super.onCreate();
image = new ImageView(this);
image.setImageResource(R.mipmap.ic_launcher);
mWindowManager = (WindowManager)getSystemService(WINDOW_SERVICE);
final WindowManager.LayoutParams paramsF = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
paramsF.gravity = Gravity.TOP | Gravity.LEFT;
paramsF.x=0;
paramsF.y=100;
mWindowManager.addView(image, paramsF);
try{
image.setOnTouchListener(new View.OnTouchListener() {
WindowManager.LayoutParams paramsT = paramsF;
private int initialX;
private int initialY;
private float initialTouchX;
private float initialTouchY;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
initialX = paramsF.x;
initialY = paramsF.y;
initialTouchX = event.getRawX();
initialTouchY = event.getRawY();
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_MOVE:
paramsF.x = initialX + (int) (event.getRawX() - initialTouchX);
paramsF.y = initialY + (int) (event.getRawY() - initialTouchY);
mWindowManager.updateViewLayout(v, paramsF);
break;
}
return false;
}
});
} catch (Exception e){
e.printStackTrace();
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
2.4. Xử lý trong MainActivity
Chúng ta sẽ xử lý khi click vào button START , STOP trong MainActivity như sau :
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import android.view.View;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (!Settings.canDrawOverlays(this)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, 0);
}
findViewById(R.id.btn_start).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//toast
startService(new Intent(MainActivity.this, MyService.class));
}
});
findViewById(R.id.btn_stop).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
stopService(new Intent(MainActivity.this, MyService.class));
}
});
}
}
Chạy ứng dụng, khi bạn click vào START, sẽ xuất hiện 1 view chính là icon của app. Bạn có thể kéo thả nó toàn màn hình, ngay cả khi thoát app như sau :
3. Kết luận
Qua bài viết hy vọng bạn đã biết cách tạo Floating View và có thể ứng dụng cho các dự án trong tương lai.
Cảm ơn bạn đã đọc bài viết.
Chào thân ái và quyết thắng!
Theo dõi VnCoder trên Facebook, để cập nhật những bài viết, tin tức và khoá học mới nhất!