6

Hộp thư đến của Google hiển thị thanh đồ ăn bao gồm bàn phím như thế nào?

Làm cách nào để Hộp thư đến của Google hiển thị thanh trượt trên bàn phím (bao phủ, phân lớp)? Đây không phải là hành vi mặc định, mà tôi đã thử nghiệm với bố cục điều phối cơ bản, bàn phím mở và một thanh snack: hành vi mặc định hiển thị thanh snack phía sau bàn phím. Có rất nhiều câu trả lời cho SO cho câu hỏi: 'Làm cách nào để hiển thị thanh snack phía trên bàn phím', nhưng tôi chưa tìm thấy cách tạo lại hành vi của snack trong ứng dụng Inbox của riêng mình.

Bạn có thể chỉ cho tôi cách đạt được điều này không?

+0

Tôi đã thử đặt độ cao của chế độ xem của thanh snack, theo mặc định là 6, với số lượng lớn, nhưng điều này không giúp ích gì. Tôi đã thử độ cao lên tới 5000! – Erik

+1

Tôi cho rằng họ phải sử dụng cửa sổ lớp phủ (ví dụ: 'TYPE_SYSTEM_OVERLAY' trước 8.0,' TYPE_APPLICATION_OVERLAY' trên 8.0+). Tôi không sử dụng Hộp thư đến của Google nên tôi không thể kiểm tra kịch bản này. Tuy nhiên, nếu Inbox không có hành vi này trên 8.0+, nhưng từ 7.1 trở lên, thì rất có thể là lớp phủ, vì [lớp phủ không thể che phủ IME trên 8.0+] (https://developer.android.com /about/versions/oreo/android-8.0-changes.html#cwt). – CommonsWare

+0

'độ cao' chỉ ảnh hưởng đến bố cục ứng dụng của bạn. Tôi nghĩ rằng có thể có một số thủ thuật với việc tạo ra Snakebar với bối cảnh khác nhau. – sakiM

Trả lời

5

Hộp thư đến không sử dụng Snackbar tiêu chuẩn và cũng không sử dụng cửa sổ lớp phủ, nó sử dụng chế độ xem tùy chỉnh. Tôi đã giải mã APK để xác minh cách chúng đạt được hiệu ứng này và tôi đã cố gắng tái tạo một ví dụ tối thiểu.

Trước hết tạo ra một mới bố trí custom_snackbar.xml:

<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:background="#323232" 
    android:paddingLeft="10dp" 
    android:paddingRight="10dp"> 

    <TextView 
     android:id="@+id/text" 
     android:layout_width="0dp" 
     android:layout_height="match_parent" 
     android:layout_weight="1" 
     android:gravity="center_vertical" 
     android:text="Marked done" 
     android:textColor="@android:color/white"/> 

    <Button 
     android:id="@+id/button" 
     android:layout_width="wrap_content" 
     android:layout_height="match_parent" 
     android:background="@android:color/transparent" 
     android:gravity="center_vertical" 
     android:minWidth="48dp" 
     android:text="undo" 
     android:textAllCaps="true" 
     android:textColor="#a1c2fa"/> 

</LinearLayout> 

Sau đó, tạo ra những phương pháp để hiển thị và bỏ qua những tùy chỉnh Snackbar:

private View customSnackbar; 

private void showCustomSnackbar(final Context context) { 
    customSnackbar = LayoutInflater.from(context).inflate(R.layout.custom_snackbar, null, false); 
    customSnackbar.findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View v) { 
      //action implementation 
     } 
    }); 

    WindowManager.LayoutParams lp = new WindowManager.LayoutParams(); 
    lp.height = WindowManager.LayoutParams.WRAP_CONTENT; 
    lp.width = WindowManager.LayoutParams.MATCH_PARENT; 
    lp.format = PixelFormat.TRANSLUCENT; 
    lp.gravity = Gravity.BOTTOM; 
    lp.flags = WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | 
      WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | 
      WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; 
    customSnackbar.setLayoutParams(lp); 

    WindowManager windowManager = (WindowManager) context.getSystemService(WINDOW_SERVICE); 

    if (windowManager != null) { 
     windowManager.addView(customSnackbar, customSnackbar.getLayoutParams()); 

     Point m = new Point(); 
     windowManager.getDefaultDisplay().getSize(m); 
     int childMeasureSpecWidth = ViewGroup.getChildMeasureSpec(View.MeasureSpec.makeMeasureSpec(m.x, View.MeasureSpec.EXACTLY), 0, lp.width); 
     int childMeasureSpecHeight = ViewGroup.getChildMeasureSpec(View.MeasureSpec.makeMeasureSpec(m.y, View.MeasureSpec.EXACTLY), 0, lp.height); 
     customSnackbar.measure(childMeasureSpecWidth, childMeasureSpecHeight); 

     customSnackbar.setTranslationY(customSnackbar.getMeasuredHeight()); 
     customSnackbar.animate() 
       .setDuration(300) 
       .translationX(0.0f) 
       .translationY(0.0f); 
    } 
} 

private void dismissCustomSnackbar(final Context context) { 
    customSnackbar.animate() 
      .setDuration(300) 
      .translationX(0.0f) 
      .translationY(customSnackbar.getMeasuredHeight()) 
      .withEndAction(new Runnable() { 
       @Override 
       public void run() { 
        WindowManager windowManager = (WindowManager) context.getSystemService(WINDOW_SERVICE); 
        if (windowManager != null) { 
         windowManager.removeView(customSnackbar); 
        } 
       } 
      }); 
} 

Đây là kết quả thu được:

Screenshot

+1

Tôi không gây rối với 'WindowManager' nhiều ... nhưng tại sao điều này lại xuất hiện trên IME? Tôi nghĩ những thay đổi trong Android 8.0 liên quan đến các cửa sổ được thêm vào của WindowManager được cho là để ngăn chặn điều này. Bất kể, công việc tốt đẹp! – CommonsWare

+0

Giới hạn Android 8.0 dường như chỉ áp dụng cho loại 'TYPE_APPLICATION_OVERLAY'. Trong ví dụ này, khung nhìn có kiểu mặc định 'TYPE_APPLICATION'. –

+0

Cảm ơn, điều đó thật tuyệt vời. Tôi sẽ thử điều này sau trong các ứng dụng của tôi và báo cáo ở đây nếu có bất kỳ trường hợp quirks hoặc cạnh nào xảy ra. Tôi cũng đoán nó sẽ có thể xây dựng một 'Snackbar' theo cách thông thường và sau đó, nếu bàn phím được mở, thay vì hiển thị nó thường xuyên, sử dụng phương pháp này để hiển thị nó ở phía trước của bàn phím. Bằng cách này, hành vi snackbar tùy chỉnh này có thể được điều chỉnh theo các tình huống bàn phím khác nhau. – Erik

Các vấn đề liên quan