Pływające okna w Androidzie

Android jest dużym kombajnem, z którego możemy dużo wycisnąć. To nie tylko telefony, tv, ale też i normalny sprawny system operacyjny. Możemy z niego korzystać jak z każdego systemu zainstalowanego na komputerze. Prawie na każdym systemie mamy okna, które możemy przesuwać po ekranie. Android też to posiada, dlatego poniżej zobaczysz, w jaki sposób stworzyć pływające okna w Androidzie.  Można to wykorzystać w powiadomieniach, takie zastosowanie możemy znaleźć w aplikacji Messenger od Facebooka jako dymki. Zaczynajmy!

Przygotowania

W przypadku wersji Androida API<=22 nie potrzebujemy żadnych uprawnień, natomiast w nowszym Androidzie musimy uzyskać uprawnienia, aby aplikacja mogła „rysować” widoki nad innymi aplikacjami. W pliku manifestu dodajemy:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

W kodzie aplikacji (na przykład w głównej aktywności) dodajemy coś takiego:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {
    checkPermission();
} else {
    initBtn();
}
....
private void initBtn() {
    Button startFloatingWindow = findViewById(R.id.startFloatingWindow);
    startFloatingWindow.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            startService(new Intent(MainActivity.this, FloatWindowService.class));
        }
    });
}
@TargetApi(Build.VERSION_CODES.M)
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE) {
        if (resultCode == RESULT_OK)
            initBtn();
        else {
            Toast.makeText(this,"Draw over other app permission not available.", Toast.LENGTH_SHORT).show();
        }
    }
}
public void checkPermission() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if (!Settings.canDrawOverlays(this)) {
            Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
            startActivityForResult(intent, ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE);
        }
    }
}

Musisz także zarejestrować usługę w manifeście:

<service android:name=".FloatWindowService" />

Przygotuj serwis

Teraz musisz stworzyć klasę FloatWindowService, która będzie pracowała w tle. Rozszerz ją o klasę Service. Metoda onCreate() może wyglądać tak:

@Override
public void onCreate() {
    super.onCreate();
    windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
    int LAYOUT_FLAG;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
        LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
    else
        LAYOUT_FLAG = WindowManager.LayoutParams.TYPE_PHONE;
    final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.WRAP_CONTENT,
            LAYOUT_FLAG,
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
            PixelFormat.TRANSLUCENT);
    params.x = 0;
    params.y = 0;
    params.gravity = Gravity.CENTER;
    floatingWindow = getXML();
    windowManager.addView(floatingWindow, params);
    floatingWindow.setOnTouchListener( new View.OnTouchListener() {
        WindowManager.LayoutParams updatedParams = params;
        int x,y;
        float touchX,touchY;
        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {
            switch (motionEvent.getAction()){
                case MotionEvent.ACTION_DOWN:
                    //remember the initial position.
                    x= updatedParams.x;
                    y=updatedParams.y;
                    //get the touch location
                    touchX = motionEvent.getRawX();
                    touchY = motionEvent.getRawY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    //Calculate the X and Y coordinates of the view.
                    updatedParams.x = (int)(x+(motionEvent.getRawX() - touchX));
                    updatedParams.y = (int)(y+(motionEvent.getRawY() - touchY));
                    //Update the layout with new X & Y coordinate
                    windowManager.updateViewLayout(view,updatedParams);
                default:break;
            }
            return false;
        }
    });
}

W pierwszej kolejności tworzymy menadżer okna wraz z parametrami, który ma się pojawić. Następnie dodajemy widok do menadżera (zmienna floatingWindow).
Metoda setOnTouchListener() pozwala na przesuwanie elementu na ekranie. Za każdym razem, gdy użytkownik dotknie widoku, zarejestrujemy początkowe współrzędne X i Y, a kiedy użytkownik poruszy element, aplikacja obliczy nowe współrzędne X i Y i przeniesie widok w nowe miejsce.

Tworzenie widoku

Tworząc pływające okna w Androidzie, masz dwie opcję:

  • Stworzyć za pomocą pliku xml jak każdy inny layout.
  • Stworzyć programowo.

Poniżej przedstawiam dwie metody przykładowe. Za pomocą XML:

public View getXML() {
    View myview = LayoutInflater.from(this).inflate(R.layout.window, null);
    Button button = myview.findViewById(R.id.stopWindow);
    button.setOnClickListener(btnListener);
    return myview;
}

lub programowo:

public View getLayout() {
    LinearLayout linearLayout = new LinearLayout(this);
    LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
                                                                            LinearLayout.LayoutParams.MATCH_PARENT);
    linearLayout.setLayoutParams(layoutParams);
    linearLayout.setBackgroundResource(R.color.transparent);
    linearLayout.setOrientation(LinearLayout.VERTICAL);
    TextView textView = new TextView(this);
    textView.setText("This is a floating window");
    textView.setGravity(Gravity.CENTER_HORIZONTAL);
    LinearLayout.LayoutParams layoutParamsText = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
                                                                               LinearLayout.LayoutParams.WRAP_CONTENT);
    textView.setLayoutParams(layoutParamsText);
    linearLayout.addView(textView);
    Button button = new Button(this);
    button.setText("Stop floating window");
    ViewGroup.LayoutParams btnparms = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                                                                 ViewGroup.LayoutParams.WRAP_CONTENT);
    button.setLayoutParams(btnparms);
    button.setGravity(Gravity.CENTER_HORIZONTAL);
    button.setOnClickListener(btnListener);
    linearLayout.addView(button);
    return linearLayout;
}

Nie zapomnij też o obsłudze przycisku, który zamknie okno i wyłączy usługę.

View.OnClickListener btnListener = new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        windowManager.removeView(floatingWindow);
        stopSelf();
    }
};

Efekt:

plywajace okna w androidzie

Podsumowanie

To wszystkie kroki, które musisz wykonać, aby wyświetlić pływające okno w Androidzie. Powyższy przykład jest prosty i pokazuje kwadraz z guzikiem. Nic nie stoi, na przeszkodzi, aby widok zmienić na kółko, trójkąt czy inny wygląd 🙂 Teraz stwórz własny projekt. Efekt fajny, ale pamiętaj, że ma swoje ograniczenia. Musisz zapytać użytkownika o uprawnienia. Warto przed ustawieniem uprawnień wyjaśnić użytkownikowi czemu potrzebujesz takich uprawnień.

Co dalej?

  • Polub stronę MYENV na Facebooku oraz śledź mnie na Twitterze
  • Zachęcam do komentowania i pisania propozycji tematów, o których chcesz przeczytać
  • Poleć ten wpis za pomocą poniższych przycisków. Będę Ci za to bardzo wdzięczny 🙂
  • Życzę Ci miłego dnia i miłego kodowania 🙂