46

Tôi có một DialogFragment để hiển thị View như màn hình bật lên. Cửa sổ xuất hiện luôn ở giữa màn hình. Có cách nào để đặt vị trí của cửa sổ DialogFragment không? Tôi đã xem xét source code nhưng chưa thể tìm thấy bất kỳ nội dung gì.Vị trí của DialogFragment trong Android

Trả lời

77

Hãy thử một cái gì đó như thế này:

@Override 
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 
{ 
    getDialog().getWindow().setGravity(Gravity.CENTER_HORIZONTAL | Gravity.TOP); 
    WindowManager.LayoutParams p = getDialog().getWindow().getAttributes(); 
    p.width = ViewGroup.LayoutParams.MATCH_PARENT; 
    p.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE; 
    p.x = 200; 
    ... 
    getDialog().getWindow().setAttributes(p); 
    ... 

hoặc các phương pháp khác cho getDialog().getWindow().

đảm bảo đặt vị trí sau khi gọi nội dung được đặt.

+0

nhờ Steelight Tôi upvoted câu trả lời của bạn. Tôi có một câu hỏi mặc dù.Tại sao bạn thay đổi WindowManager.LayoutParam p? setGravity() dường như làm thủ thuật để thiết lập cửa sổ ở trên cùng. Có vẻ như không có bất kỳ tác dụng phụ nào trên LayoutParams khi tôi kiểm tra nó hoặc khi tôi đọc tài liệu về setGravity. – flobacca

+0

Câu hỏi đặt ra là "cách đặt vị trí", tôi không cho rằng anh ta có nghĩa là 'hàng đầu', vì vậy tôi đã đưa ra ví dụ về việc thiết lập nhiều thuộc tính để có được hiệu ứng mong muốn. – Steelight

+0

+1 vì điều này cho tôi thấy đúng hướng. Điều cuối cùng làm việc cho tôi: http://stackoverflow.com/a/20419231/56285 – Jonik

3

Tôi đang sử dụng lớp PopupWindow cho mục đích này vì bạn có thể chỉ định vị trí và kích thước của chế độ xem.

+13

PopupWindow không phải là một mảnh, vì vậy đây là một cơn đau đầu. –

4

getDialog().getWindow() sẽ không hoạt động đối với DialogFragment dưới dạng getWindow() sẽ trả về null nếu hoạt động lưu trữ không hiển thị, không phải là bạn đang viết một ứng dụng dựa trên phân đoạn. Bạn sẽ nhận được NullPointerException khi bạn thử getAttributes().

Tôi khuyên bạn nên trả lời của Mobistry. Nếu bạn đã có một lớp DialogFragment, nó không quá khó để chuyển đổi. Chỉ cần thay thế phương thức onCreateDialog bằng phương thức xây dựng và trả về một PopupWindow. Sau đó, bạn sẽ có thể sử dụng lại Chế độ xem bạn cung cấp cho AlertDialog.builder.setView() và gọi (PopupWindow object).showAtLocation().

67

Phải, tôi đập đầu vào tường trong một hoặc hai giờ với điều này, trước khi cuối cùng nhận được DialogFragment vị trí như tôi muốn.

Tôi đang xây dựng trên Steelight's answer tại đây. Đây là phương pháp đơn giản nhất, đáng tin cậy nhất mà tôi tìm thấy.

@Override 
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle b) { 
    Window window = getDialog().getWindow(); 

    // set "origin" to top left corner, so to speak 
    window.setGravity(Gravity.TOP|Gravity.LEFT); 

    // after that, setting values for x and y works "naturally" 
    WindowManager.LayoutParams params = window.getAttributes(); 
    params.x = 300; 
    params.y = 100; 
    window.setAttributes(params); 

    Log.d(TAG, String.format("Positioning DialogFragment to: x %d; y %d", params.x, params.y)); 
} 

Lưu ý rằng params.widthparams.softInputMode (được sử dụng trong câu trả lời của Steelight) không liên quan cho việc này.


Dưới đây là ví dụ hoàn chỉnh hơn. Điều tôi thực sự cần là sắp xếp hộp thoại "xác nhận" DialogFragment bên cạnh chế độ xem "nguồn" hoặc "cha mẹ", trong trường hợp của tôi là ImageButton.

Tôi đã chọn sử dụng DialogFragment, thay vì bất kỳ Fragment tùy chỉnh nào, vì nó cung cấp cho bạn tính năng "hộp thoại" miễn phí (hộp thoại đóng khi người dùng nhấp vào bên ngoài nó, v.v.).

Example ConfirmBox
Ví dụ ConfirmBox trên "nguồn" ImageButton của nó (thùng rác)

/** 
* A custom DialogFragment that is positioned above given "source" component. 
* 
* @author Jonik, https://stackoverflow.com/a/20419231/56285 
*/ 
public class ConfirmBox extends DialogFragment { 
    private View source; 

    public ConfirmBox() { 
    } 

    public ConfirmBox(View source) { 
     this.source = source;    
    } 

    public static ConfirmBox newInstance(View source) { 
     return new ConfirmBox(source); 
    } 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 

     setStyle(STYLE_NO_FRAME, R.style.Dialog); 
    } 


    @Override 
    public void onStart() { 
     super.onStart(); 

     // Less dimmed background; see https://stackoverflow.com/q/13822842/56285 
     Window window = getDialog().getWindow(); 
     WindowManager.LayoutParams params = window.getAttributes(); 
     params.dimAmount = 0.2f; // dim only a little bit 
     window.setAttributes(params); 

     // Transparent background; see https://stackoverflow.com/q/15007272/56285 
     // (Needed to make dialog's alpha shadow look good) 
     window.setBackgroundDrawableResource(android.R.color.transparent); 
    } 

    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
     // Put your dialog layout in R.layout.view_confirm_box 
     View view = inflater.inflate(R.layout.view_confirm_box, container, false); 

     // Initialise what you need; set e.g. button texts and listeners, etc. 

     // ... 

     setDialogPosition(); 

     return view; 
    } 

    /** 
    * Try to position this dialog next to "source" view 
    */ 
    private void setDialogPosition() { 
     if (source == null) { 
      return; // Leave the dialog in default position 
     } 

     // Find out location of source component on screen 
     // see https://stackoverflow.com/a/6798093/56285 
     int[] location = new int[2]; 
     source.getLocationOnScreen(location); 
     int sourceX = location[0]; 
     int sourceY = location[1]; 

     Window window = getDialog().getWindow(); 

     // set "origin" to top left corner 
     window.setGravity(Gravity.TOP|Gravity.LEFT); 

     WindowManager.LayoutParams params = window.getAttributes(); 

     // Just an example; edit to suit your needs. 
     params.x = sourceX - dpToPx(110); // about half of confirm button size left of source view 
     params.y = sourceY - dpToPx(80); // above source view 

     window.setAttributes(params); 
    } 

    public int dpToPx(float valueInDp) { 
     DisplayMetrics metrics = getActivity().getResources().getDisplayMetrics(); 
     return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, valueInDp, metrics); 
    } 
} 

Đó là khá dễ dàng để làm cho tổng quát sử dụng trên bằng cách thêm các thông số constructor hoặc setters khi cần thiết. (My thức ConfirmBox có một nút theo kiểu (bên trong một số biên giới vv) có văn bản và View.OnClickListener thể được tùy chỉnh trong mã.)

+0

Tôi đã tìm thấy câu trả lời hay nhất tuyệt vời. –

+0

Các bạn rock - cảm ơn - Tôi rất tò mò, ** tại sao tôi không thể chỉ thiết lập điều này trong XML của phân đoạn? ** Tôi không hiểu gì về một dev dev iOS? cảm ơn các ông! :) – Fattie

+0

Câu trả lời của Grt !! Tôi gặp bất kỳ vấn đề nào, trong kịch bản nút nguồn của tôi (dưới đây là đoạn hộp thoại sẽ được hiển thị) thay đổi vị trí trên thay đổi hướng màn hình, do đó, việc thay đổi hướng hộp thoại không cập nhật vị trí theo vị trí của ' nguồn'. Bạn có thể vui lòng cho tôi một số gợi ý về cách thực hiện điều này không. – Dory

3

Bạn cần phải ghi đè phương pháp onResume() trong DialogFragment của bạn như sau:

@Override 
public void onResume() { 
    final Window dialogWindow = getDialog().getWindow(); 
    WindowManager.LayoutParams lp = dialogWindow.getAttributes(); 
    lp.x = 100;  // set your X position here 
    lp.y = 200;  // set your Y position here 
    dialogWindow.setAttributes(lp); 

    super.onResume(); 
} 
Các vấn đề liên quan