+4

Tạo Floating View giống ứng dụng Message Facebook trong Android

1. Giới thiệu

Nếu bạn là 1 tín đồ của Facebook hẳn đã quen với việc sử dụng ứng dụng Messages FB, nó xuất hiện như 1 bong bóng chat nổi trên màn hình điện thoại. Bạn có thể kéo thả view chat lên trên các ứng dụng khác một cách dễ dàng.

Trong bài này, mình sẽ hướng dẫn các bạn tạo một ứng dụng có view giống như ứng dụng Message của FB.

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 Message Facebook.

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 :


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí