Cách tạo Chat Head như Facebook Messenger

5
511

facebook chathead
Facebook Messenger tung ra chức năng chat head vào năm 2013 và ngay lập tức nhận được sử hưởng ứng của người dùng. Điểm tiện ích của chat head là cho phép người dùng có thể vừa làm việc và vừa chat cùng một lúc. Ví dụ nếu bạn đang đọc email thì nhận được tin nhắn từ Facebook, bạn có thể trả lời tin nhắn mà không phải chuyển app.

Tutorial này sẽ hướng dẫn bạn một cách chi tiết cách tạo một chat head đơn giản và cho phép người dùng có thể di chuyển vị trí của chat head khắp màn hình.

1. Kiến thức cơ bản

  • Chat head là một cửa sổ nổi đè lên giao diện của các app khác. Hệ thống Android cho phép các app đè lên một app khác nếu có permission: android.permission.SYSTEM_ALERT_WINDOW. Chúng ta dùng background service để đè một widget lên trên RootView của ứng dụng đang mở. Do đó, chat head luôn hiện lên trên các app khác.
  • Để có thể kéo chat head khắp màn hình, chúng ta phải override OnTouchListener() để chat head tương tác với hành động của người dùng và thay đổi vị trí của chat head trên màn hình.

2. Tiến hành viết code nào

Các bạn có thể download toàn bộ source code của bài hướng dẫn này tại đây nhé

Bước 1: Thêm permission:  android.permission.SYSTEM_ALERT_WINDOW vào file AndroidManifest.xml. Permission này cho phép app tạo các cửa sổ mới hiện đè lên các app khác.

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

Bước 2: Tạo layout cho chat head của bạn.

<?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="65dp"
    android:id="@+id/chat_head_root"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <!--Profile image for the chat head.-->
    <ImageView
        android:id="@+id/chat_head_profile_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="26dp"
        android:layout_height="26dp"
        android:layout_marginLeft="40dp"
        android:src="@drawable/ic_close"
        tools:ignore="ContentDescription"/>
</RelativeLayout>

Bước 3: Thêm View vào WindowManager và xử lý drag

  • Bắt đầu tạo một service: ChatHeadService.java. Khi nào bạn muốn hiển thị chat head thì khởi động service này bằng hàm startService(). Trong phần onCreate() của service, chúng ta sẽ thêm layout của chat head vào góc trái phía trên của cửa sổ.
public class ChatHeadService extends Service {
   private WindowManager mWindowManager;
   private View mChatHeadView;


   public ChatHeadService() {
   }


   @Override
   public IBinder onBind(Intent intent) {
       return null;
   }


   @Override
   public void onCreate() {
       super.onCreate();
       //Inflate the chat head layout we created
       mChatHeadView = LayoutInflater.from(this).inflate(R.layout.layout_chat_head, 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 chat head position
//Initially view will be added to top-left corner
       params.gravity = Gravity.TOP | Gravity.LEFT;
       params.x = 0;          
       params.y = 100;
      
       //Add the view to the window
       mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
       mWindowManager.addView(mChatHeadView, params);


//….
//….
   }


   @Override
   public void onDestroy() {
       super.onDestroy();
       if (mChatHeadView != null) mWindowManager.removeView(mChatHeadView);
   }
}
  • Để người dùng có thể kéo chat head khắp màn hình, chúng ta sẽ override OnTouchListener(). Mỗi khi người dùng chạm vào chat head, chúng ta sẽ ghi lại tọa độ (x, y )ban đầu và khi người dùng di chuyển ngón tay, app sẽ tính toán tọa độ (x, y) mới và di chuyển chat head.
  • Khởi động click listener để đóng chat head bằng cách dừng service khi người dùng click vào biểu tượng [x] ở góc phải bên trên của chat head.
//Set the close button.
ImageView closeButton = (ImageView) mChatHeadView.findViewById(R.id.close_btn);
closeButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        //close the service and remove the chat head from the window
        stopSelf();
    }
});

//Drag and move chat head using user's touch action.
final ImageView chatHeadImage = (ImageView) mChatHeadView.findViewById(R.id.chat_head_profile_iv);
chatHeadImage.setOnTouchListener(new View.OnTouchListener() {
    private int lastAction;
    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();

                lastAction = event.getAction();
                return true;
            case MotionEvent.ACTION_UP:
                //As we implemented on touch listener with ACTION_MOVE,
                //we have to check if the previous action was ACTION_DOWN
                //to identify if the user clicked the view or not.
                if (lastAction == MotionEvent.ACTION_DOWN) {
                    //Open the chat conversation click.
                    Intent intent = new Intent(ChatHeadService.this, ChatActivity.class);
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    startActivity(intent);

                    //close the service and remove the chat heads
                    stopSelf();
                }
                lastAction = event.getAction();
                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(mChatHeadView, params);
                lastAction = event.getAction();
                return true;
        }
        return false;
    }
});

Bước 4: Handle Overdraw permission

  • Bước cuối cùng. Để hiện thị chat head, bạn phải khởi động ChatService.java.
  • Trước đó, chúng ta phải kiểm tra xem app có permission android.permission.SYSTEM_ALERT_WINDOW hay không. Các Android từ API level 22 trở về trước, permission này được bật tự động. Từ android sau API 22, chúng ta phải bật permission thủ công khi được sự cho phép của người dùng. Để bật permission,ứng dụng cần phải mở màn hình quản lý permission để người dùng có thể thay đổi (chấp nhận hoặc không) permission qua action: Settings.ACTION_MANAGE_OVERLAY_PERMISSION. Lệnh này sẽ mở ra màn hình như dưới đây

android persmission

  • Dưới đây là một phần code MainActivity để hiển thị chat head khi SYSTEM_ALERT_WINDOW permission được cho phép.
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, ChatHeadService.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);
        }
    }
}

Xong rồi. Hãy thử chạy app để kiểm tra kết quả. Dưới đây là một mẫu giao diện của app.

Demo chat head

Nếu vẫn còn băn khoăn chỗ nào thì comment bên dưới nhé!!! Good luck!

5
Bình luận. Đặt câu hỏi cũng là một cách học

avatar
  Theo dõi bình luận  
Mới nhất Cũ nhất Nhiều voted nhất
Thông báo
Lê
Guest

Sao lúc chạy bị lỗi nhỉ. lần đầu chạy thử android studio nên cũng không rành lắm.

TVD
Guest

WOW, thì ra tạo cái này cũng dễ nhỉ!

Tú
Guest

AD ơi. Sao down được file code về vậy ạ? Chỉ e vs. em đaq muốn tạo 1 cái chathead