Hướng Dẫn Tạo ứng Dụng Sử Dụng Floating Widget Giống Facebook ...

Floating widgets 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 và 1 widget từ máy nghe nhạc đang nổi trên màn hình, bạn có thể kiểm soát nhạc của bạn cùng một lúc

Trong hướng dẫn này, chúng ta sẽ tìm hiểu làm thế nào để tạo widget nổi đơn giản và cho phép người sử dụng để kéo chúng trên màn hình. Người dùng có thể điều chỉnh vị trí của widget trôi nổi trên màn hình. Chúng ta sẽ phát triển một floating nổi đó là có nút để điều khiển âm nhạc.

Xem trước kết quả

1. Hiểu khái quát

Hệ thống Android cho phép các ứng dụng vẽ trên ứng dụng khác nếu ứng dụng có quyền android.permission.SYSTEM_ALERT_WINDOW. Chúng tôi sẽ sử dụng các dịch vụ nền để thêm các widget nổi vào hệ thống phân cấp điểm của màn hình hiện tại. Vì vậy, floating view luôn luôn trên cùng của cửa sổ ứng dụng.

Để kéo thả lên trên màn hình chúng ta sẽ override OnTouchListener () để lắng nghe sự kiện kéo thả và sự thay đổi vị trí của các điểm có trong màn hình.

2. Hướng dẫn tạo project

Bước 1. Tạo mới ứng dụng trong Android Studio: File ⇒ New Project

Bước 2. Download resource và thêm chúng vào thư mục của project. Thư mục này có chứa nhưng icon, ảnh cần thiết cho ứng dụng demo này

Bước 3. Thêm quyền android.permission.SYSTEM_ALERT_WINDOW permission vào file AndroidManifest.xml file. Quyền này cho phép một ứng dụng có thể tạo cửa sổ, hiển thị nó lên trên top của tất cả các ứng dụng khác. Và chúng ta cũng sẽ thêm tên của dịch vụ FloatingViewService

AndroidManifest.xml <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="info.androidhive.floatingview"> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name="info.androidhive.floatingview.MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <service android:name="info.androidhive.floatingview.FloatingViewService" android:enabled="true" android:exported="false"/> </application> </manifest>

Bước 4. Tạo 1 layout với tên: layout_floating_widget.xml cho floating view. Layout này sẽ bao gồm 2 phần chính:

Collapsed view: floating widget sẽ co lại khi view được launched. Khi người dùng click vào view này này, nó sẽ đượcc mở rộng ra.

Expanded View: view này sẽ chứa các nút để chơi nhạc, thay đổi bài hát: next / previous và mở ứng dụng.

layout_floating_widget.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"> <!--Root container--> <RelativeLayout android:id="@+id/root_container" android:layout_width="wrap_content" android:layout_height="wrap_content" tools:ignore="UselessParent"> <!--View while view is collapsed--> <RelativeLayout android:id="@+id/collapse_view" android:layout_width="wrap_content" android:visibility="visible" android:layout_height="wrap_content" android:orientation="vertical"> <!--Icon of floating widget --> <ImageView android:id="@+id/collapsed_iv" android:layout_width="60dp" android:layout_height="60dp" android:layout_marginTop="8dp" android:src="@drawable/ic_android_circle" tools:ignore="ContentDescription"/> <!--Close button--> <ImageView android:id="@+id/close_btn" android:layout_width="20dp" android:layout_height="20dp" android:layout_marginLeft="40dp" android:src="@drawable/ic_close" tools:ignore="ContentDescription"/> </RelativeLayout> <!--View while view is expanded--> <LinearLayout android:id="@+id/expanded_container" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="#F8BBD0" android:visibility="gone" android:orientation="horizontal" android:padding="8dp"> <!--Album image for the song currently playing.--> <ImageView android:layout_width="80dp" android:layout_height="80dp" android:src="@drawable/music_player" tools:ignore="ContentDescription"/> <!--Previous button--> <ImageView android:id="@+id/prev_btn" android:layout_width="30dp" android:layout_height="30dp" android:layout_gravity="center_vertical" android:layout_marginLeft="20dp" android:src="@mipmap/ic_previous" tools:ignore="ContentDescription"/> <!--Play button--> <ImageView android:id="@+id/play_btn" android:layout_width="50dp" android:layout_height="50dp" android:layout_gravity="center_vertical" android:layout_marginLeft="10dp" android:src="@mipmap/ic_play" tools:ignore="ContentDescription"/> <!--Next button--> <ImageView android:id="@+id/next_btn" android:layout_width="30dp" android:layout_height="30dp" android:layout_gravity="center_vertical" android:layout_marginLeft="10dp" android:src="@mipmap/ic_play_next" tools:ignore="ContentDescription"/> <RelativeLayout android:layout_width="wrap_content" android:layout_height="match_parent" android:orientation="vertical"> <ImageView android:id="@+id/close_button" android:layout_width="20dp" android:layout_height="20dp" android:src="@drawable/ic_close"/> <ImageView android:id="@+id/open_button" android:layout_width="20dp" android:layout_height="20dp" android:layout_alignParentBottom="true" android:src="@drawable/ic_open"/> </RelativeLayout> </LinearLayout> </RelativeLayout> </FrameLayout>

2.1. Thêm Floating Widget (Music Controls) and xử ký kéo thả

Bây giờ tạo ra một dịch vụ có tên là FloatingViewService.java. Bất cứ khi nào bạn muốn hiển thị floating view, bắt đầu sử dụng dịch vụ bằng lệnh startService (). Trong onCreate () của dịch vụ, chúng ta sẽ bổ sung thêm lauout của floating view ở góc trên bên trái của cửa sổ.

FloatingViewService.java public class FloatingViewService extends Service { private WindowManager mWindowManager; private View mFloatingView; public FloatingViewService() { } @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); //Inflate the floating view layout we created mFloatingView = LayoutInflater.from(this).inflate(R.layout.layout_floating_widget, null); //Add the view to the window. final WindowManager.LayoutParams params = new WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_PHONE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT); //Specify the view position params.gravity = Gravity.TOP | Gravity.LEFT; //Initially view will be added to top-left corner params.x = 0; params.y = 100; //Add the view to the window mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE); mWindowManager.addView(mFloatingView, params); //…. //…. } @Override public void onDestroy() { super.onDestroy(); if (mFloatingView != null) mWindowManager.removeView(mFloatingView); } }

Ngoài ra, chúng ta thực hiện OnClickListner () cho tất cả các nút như chơi, tạm dừng, và ứng dụng mở. Bạn có thể thực hiện một chức năng chơi nhạc tạm dừng trên nút bấm sử dụng MediaPlayer. Khi người dùng nhấp vào nút đóng ở collapsed view, FloatingViewService sẽ bị destroyed và floating view sẽ được loại bỏ khỏi hệ thống phân cấp xem.

Thêm mã dưới đây ở FloatingViewService.java trong phương thức onCrate ().

FloatingViewService.java //The root element of the collapsed view layout final View collapsedView = mFloatingView.findViewById(R.id.collapse_view); //The root element of the expanded view layout final View expandedView = mFloatingView.findViewById(R.id.expanded_container); //Set the close button ImageView closeButtonCollapsed = (ImageView) mFloatingView.findViewById(R.id.close_btn); closeButtonCollapsed.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //close the service and remove the from from the window stopSelf(); } }); //Set the view while floating view is expanded. //Set the play button. ImageView playButton = (ImageView) mFloatingView.findViewById(R.id.play_btn); playButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(FloatingViewService.this, "Playing the song.", Toast.LENGTH_LONG).show(); } }); //Set the next button. ImageView nextButton = (ImageView) mFloatingView.findViewById(R.id.next_btn); nextButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(FloatingViewService.this, "Playing next song.", Toast.LENGTH_LONG).show(); } }); //Set the pause button. ImageView prevButton = (ImageView) mFloatingView.findViewById(R.id.prev_btn); prevButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(FloatingViewService.this, "Playing previous song.", Toast.LENGTH_LONG).show(); } }); //Set the close button ImageView closeButton = (ImageView) mFloatingView.findViewById(R.id.close_button); closeButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { collapsedView.setVisibility(View.VISIBLE); expandedView.setVisibility(View.GONE); } }); //Open the application on thi button click ImageView openButton = (ImageView) mFloatingView.findViewById(R.id.open_button); openButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //Open the application click. Intent intent = new Intent(FloatingViewService.this, MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); //close the service and remove view from the view hierarchy stopSelf(); } });

Để kéo thả floating view cùng với việc chạm của người sử dụng, chúng ta phải ghi đè OnTouchListener (). Bất cứ khi nào người dùng chạm vào thư mục gốc của view, chúng ta sẽ ghi lại các tọa độ giá trị khởi tạo x, y, và khi người dùng di chuyển các ngón tay, ứng dụng sẽ tính toán tọa độ X mới và tọa độ Y phối hợp và di chuyển float view.

FloatingViewService.java //Drag and move floating view using user's touch action. mFloatingView.findViewById(R.id.root_container).setOnTouchListener(new View.OnTouchListener() { 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: //remember the initial position. initialX = params.x; initialY = params.y; //get the touch location initialTouchX = event.getRawX(); initialTouchY = event.getRawY(); return true; case MotionEvent.ACTION_MOVE: //Calculate the X and Y coordinates of the view. params.x = initialX + (int) (event.getRawX() - initialTouchX); params.y = initialY + (int) (event.getRawY() - initialTouchY); //Update the layout with new X & Y coordinate mWindowManager.updateViewLayout(mFloatingView, params); return true; } return false; } });

2.2. Xử lý Collapsing và Expanding của Floating Widget

Khi người click vào icon của collapsed layout, việc không hiển thị của collapsed layout sẽ thay đổi thành View.GONE và mở rộng sẽ trở nên nhìn thấy được như dưới đây:

FloatingViewService.java case MotionEvent.ACTION_UP: int Xdiff = (int) (event.getRawX() - initialTouchX); int Ydiff = (int) (event.getRawY() - initialTouchY); //The check for Xdiff <10 && YDiff< 10 because sometime elements moves a little while clicking. //So that is click event. if (Xdiff < 10 && Ydiff < 10) { if (isViewCollapsed()) { //When user clicks on the image view of the collapsed layout, //visibility of the collapsed layout will be changed to "View.GONE" //and expanded view will become visible. collapsedView.setVisibility(View.GONE); expandedView.setVisibility(View.VISIBLE); } } return true;

Cuối cùng FloatingViewService.java sẽ như dưới đây:

FloatingViewService.java import android.app.Service; import android.content.Intent; import android.graphics.PixelFormat; import android.opengl.Visibility; import android.os.IBinder; 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.Toast; public class FloatingViewService extends Service { private WindowManager mWindowManager; private View mFloatingView; public FloatingViewService() { } @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); //Inflate the floating view layout we created mFloatingView = LayoutInflater.from(this).inflate(R.layout.layout_floating_widget, null); //Add the view to the window. final WindowManager.LayoutParams params = new WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_PHONE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT); //Specify the view position params.gravity = Gravity.TOP | Gravity.LEFT; //Initially view will be added to top-left corner params.x = 0; params.y = 100; //Add the view to the window mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE); mWindowManager.addView(mFloatingView, params); //The root element of the collapsed view layout final View collapsedView = mFloatingView.findViewById(R.id.collapse_view); //The root element of the expanded view layout final View expandedView = mFloatingView.findViewById(R.id.expanded_container); //Set the close button ImageView closeButtonCollapsed = (ImageView) mFloatingView.findViewById(R.id.close_btn); closeButtonCollapsed.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //close the service and remove the from from the window stopSelf(); } }); //Set the view while floating view is expanded. //Set the play button. ImageView playButton = (ImageView) mFloatingView.findViewById(R.id.play_btn); playButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(FloatingViewService.this, "Playing the song.", Toast.LENGTH_LONG).show(); } }); //Set the next button. ImageView nextButton = (ImageView) mFloatingView.findViewById(R.id.next_btn); nextButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(FloatingViewService.this, "Playing next song.", Toast.LENGTH_LONG).show(); } }); //Set the pause button. ImageView prevButton = (ImageView) mFloatingView.findViewById(R.id.prev_btn); prevButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(FloatingViewService.this, "Playing previous song.", Toast.LENGTH_LONG).show(); } }); //Set the close button ImageView closeButton = (ImageView) mFloatingView.findViewById(R.id.close_button); closeButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { collapsedView.setVisibility(View.VISIBLE); expandedView.setVisibility(View.GONE); } }); //Open the application on thi button click ImageView openButton = (ImageView) mFloatingView.findViewById(R.id.open_button); openButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //Open the application click. Intent intent = new Intent(FloatingViewService.this, MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); //close the service and remove view from the view hierarchy stopSelf(); } }); //Drag and move floating view using user's touch action. mFloatingView.findViewById(R.id.root_container).setOnTouchListener(new View.OnTouchListener() { 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: //remember the initial position. initialX = params.x; initialY = params.y; //get the touch location initialTouchX = event.getRawX(); initialTouchY = event.getRawY(); return true; case MotionEvent.ACTION_UP: int Xdiff = (int) (event.getRawX() - initialTouchX); int Ydiff = (int) (event.getRawY() - initialTouchY); //The check for Xdiff <10 && YDiff< 10 because sometime elements moves a little while clicking. //So that is click event. if (Xdiff < 10 && Ydiff < 10) { if (isViewCollapsed()) { //When user clicks on the image view of the collapsed layout, //visibility of the collapsed layout will be changed to "View.GONE" //and expanded view will become visible. collapsedView.setVisibility(View.GONE); expandedView.setVisibility(View.VISIBLE); } } return true; case MotionEvent.ACTION_MOVE: //Calculate the X and Y coordinates of the view. params.x = initialX + (int) (event.getRawX() - initialTouchX); params.y = initialY + (int) (event.getRawY() - initialTouchY); //Update the layout with new X & Y coordinate mWindowManager.updateViewLayout(mFloatingView, params); return true; } return false; } }); } /** * Detect if the floating view is collapsed or expanded. * * @return true if the floating view is collapsed. */ private boolean isViewCollapsed() { return mFloatingView == null || mFloatingView.findViewById(R.id.collapse_view).getVisibility() == View.VISIBLE; } @Override public void onDestroy() { super.onDestroy(); if (mFloatingView != null) mWindowManager.removeView(mFloatingView); } }

2.3. Xử lý Overdraw permission

Bước cuối cùng còn lại là thêm floating view bằng cách bắt đầu FloatingViewService. Vì vậy, chúng ta cần phải kiểm tra xem các ứng dụng có quyền android.permission.SYSTEM_ALERT_WINDOW phép hay không? Đối với phiên bản Android <= API22, quyền này được cấp theo mặc định. Nhưng đối với các phiên bản Android chạy API> 22, chúng ta cần phải kiểm tra quyền runtime

Nếu quyền không được kích hoạt, chúng tôi sẽ mở màn hình quản lý cho quyền để cho phép người sử dụng để cấp phép sử dụng Settings.ACTION_MANAGE_OVERLAY_PERMISSION. Nó này sẽ mở ra bên dưới màn hình thuận lợi cho người sử dụng để cấp quyền android.permission.SYSTEM_ALERT_WINDOW.

Dưới đây là đoạn mã cho MainActivity đó sẽ hiển thị các floating view khi nút được click bằng cách kiểm tra sự cho phép SYSTEM_ALERT_WINDOW.

MainActivity.java import android.content.Intent; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.provider.Settings; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Toast; public class MainActivity extends AppCompatActivity { private static final int CODE_DRAW_OVER_OTHER_APP_PERMISSION = 2084; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //Check if the application has draw over other apps permission or not? //This permission is by default available for API<23. But for API > 23 //you have to ask for the permission in runtime. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) { //If the draw over permission is not available open the settings screen //to grant the permission. Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())); startActivityForResult(intent, CODE_DRAW_OVER_OTHER_APP_PERMISSION); } else { initializeView(); } } /** * Set and initialize the view elements. */ private void initializeView() { findViewById(R.id.notify_me).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { startService(new Intent(MainActivity.this, FloatingViewService.class)); finish(); } }); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == CODE_DRAW_OVER_OTHER_APP_PERMISSION) { //Check if the permission is granted or not. if (resultCode == RESULT_OK) { initializeView(); } else { //Permission is not available Toast.makeText(this, "Draw over other app permission not available. Closing the application", Toast.LENGTH_SHORT).show(); finish(); } } else { super.onActivityResult(requestCode, resultCode, data); } } }

Bây giờ build and run project để xem kết quả

Source: http://www.androidhive.info/2016/11/android-floating-widget-like-facebook-chat-head/

Từ khóa » Float ứng Dụng