Development Tip

사용자 정의보기가있는 AlertDialog :보기의 내용을 래핑하도록 크기 조정

yourdevel 2020. 12. 29. 08:03
반응형

사용자 정의보기가있는 AlertDialog :보기의 내용을 래핑하도록 크기 조정


내가 만드는 응용 프로그램에서이 문제가 발생했습니다. 모든 설계 단점과 모범 사례 접근 방식의 부족을 무시하십시오. 이것은 순전히 해결할 수없는 예를 보여주기위한 것입니다.

사용하여 사용자 정의 세트 DialogFragment로 기본을 반환합니다 . 이 경우 특정 크기 요구 사항을 가지고, 내가 어떻게 얻을 않는 올바르게 정의의 모든 콘텐츠를 표시 할 자체의 크기를 조정 ?AlertDialogViewAlertDialog.Builder.setView()ViewDialogView

이것은 내가 사용한 예제 코드입니다.

package com.test.test;

import android.os.Bundle;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;

public class MainActivity extends Activity {

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

        // Use a button for launching
        Button b = new Button(this);
        b.setText("Launch");
        b.setOnClickListener(new OnClickListener() {    
            @Override
            public void onClick(View v) {
                // Launch the dialog
                myDialog d = new myDialog();
                d.show(getFragmentManager(), null);
            }
        });

        setContentView(b);
    }

    public static class myDialog extends DialogFragment {

        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {           
            // Create the dialog
            AlertDialog.Builder db = new AlertDialog.Builder(getActivity());
            db.setTitle("Test Alert Dialog:");
            db.setView(new myView(getActivity()));

            return db.create();
        }

        protected class myView extends View {
            Paint p = null;

            public myView(Context ct) {
                super(ct);

                // Setup paint for the drawing
                p = new Paint();
                p.setColor(Color.MAGENTA);
                p.setStyle(Style.STROKE);
                p.setStrokeWidth(10);
            }

            @Override
            protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
                setMeasuredDimension(800, 300);
            }

            @Override
            protected void onDraw(Canvas canvas) {
                // Draw a rectangle showing the bounds of the view
                canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), p);
            }
        }
    }
}

A Button가 생성되고 DialogFragment클릭 하면 열립니다 . 사용자 정의 View( myView)는의 재정의에 올바르게 설정된 너비 800과 높이 300을 가져야합니다 onMeasure(). 이것은 View디버깅 목적으로 측정 된 경계를 자홍색으로 그립니다.

800 너비는 Dialog내 장치 의 기본 크기 보다 넓지 만 올바르게 늘어나지 않고 잘립니다.

다음 솔루션을 살펴 보았습니다.

다음 두 가지 코딩 접근 방식을 추론했습니다.

  1. 가져 오기 WindowManager.LayoutParams의를 Dialog하고 사용하여 오버라이드 (override)myDialog.getDialog().getWindow().get/setAttributes()
  2. setLayout(w, h)방법 사용myDialog.getDialog().getWindow().setLayout()

나는 내가 생각할 수있는 모든 곳에서 시도해 보았고 (재정의 onStart(), a onShowListener, Dialog생성 및 표시 후 등) 일반적으로 특정 값LayoutParams 이 제공 되면 두 가지 방법이 올바르게 작동하도록 할 수 있습니다 . 그러나 공급 될 때마다 아무 일도 일어나지 않습니다.WRAP_CONTENT

어떤 제안?

편집하다:

상황 스크린 샷 : 여기에 이미지 설명 입력

이유는 이유 -. 다른 필요한 경우 - 특정 값의 스크린 샷 (주 900 850 조정되고있는 전체 창 주어진 의미가보기의 전체 폭을 커버하지 않는, 여기에 제공 그래서를 입력 WRAP_CONTENT/ 필수 고정 값이 적절하지 않음) :여기에 이미지 설명 입력


솔직히 말해서 그런 간단한 결과를 얻기에는 너무 깊이 파고 드는 작업 솔루션이 있습니다. 하지만 여기 있습니다 :

정확히 무슨 일이 일어나고 있는지 :

Hierarchy Viewer로Dialog 레이아웃 을 열면의 전체 레이아웃 과 정확히 무슨 일이 벌어지고 있는지 조사 할 수있었습니다 .AlertDialog여기에 이미지 설명 입력

블루 하이라이트 (하이 레벨의 모든 부분이다 Window위한 프레임 Dialog시각적 스타일, 등)과의 단부로부터 청색 다운 여기서위한 요소 AlertDialog이다 ( 적색 = 제목, 황색 어쩌면리스트 용 = A있는 ScrollView 스터브 AlertDialog의 , 녹색 = Dialog콘텐츠, 즉 사용자 정의보기, 주황색 = 버튼).

여기에서 7- 뷰 경로 ( 파란색 의 시작 에서 녹색 의 끝까지 )가 올바르게 실패한 것이 분명했습니다 WRAP_CONTENT. LayoutParams.width각각을 살펴보면 View모든 것이 주어 LayoutParams.width = MATCH_PARENT지고 어딘가에 크기가 설정되어 있음을 알 수 있습니다. 당신이 그 나무를 따르는 경우에 따라서는 사용자 정의 분명하다 View트리의 하단에이 것 결코 의 크기에 영향을 미칠 수 없습니다 Dialog.

그렇다면 기존 솔루션은 무엇을하고 있었습니까?

  • 의 두 가지 코딩 방법 내 질문에 언급 단순히 상단을지고 있었다 View및 수정 LayoutParams. 분명히, View트리의 모든 개체가 부모와 일치하는 경우 최상위 수준이 정적 크기로 설정되면 전체 Dialog크기가 변경됩니다. 그러나 최상위 수준이로 설정되어 있으면 트리 WRAP_CONTENT의 모든 나머지 View개체 는 트리 를 "WRAP their CONTENT" 보는 것과는 반대로 여전히 트리 에서 "MATCH their PARENT"를 찾습니다 .

문제 해결 방법 :

무뚝뚝하게 영향을 미치는 경로 LayoutParams.width의 모든 View개체를 WRAP_CONTENT.

onStart라이프 사이클 단계 DialogFragment가 호출 된 후에 만이 작업을 수행 할 수 있음을 발견했습니다 . 따라서 다음 onStart과 같이 구현됩니다.

@Override
public void onStart() { 
    // This MUST be called first! Otherwise the view tweaking will not be present in the displayed Dialog (most likely overriden)
    super.onStart();

    forceWrapContent(myCustomView);
}

그런 다음 View계층 구조 를 적절하게 수정하는 기능 LayoutParams:

protected void forceWrapContent(View v) {
    // Start with the provided view
    View current = v;

    // Travel up the tree until fail, modifying the LayoutParams
    do {
        // Get the parent
        ViewParent parent = current.getParent();    

        // Check if the parent exists
        if (parent != null) {
            // Get the view
            try {
                current = (View) parent;
            } catch (ClassCastException e) {
                // This will happen when at the top view, it cannot be cast to a View
                break;
            }

            // Modify the layout
            current.getLayoutParams().width = LayoutParams.WRAP_CONTENT;
        }
    } while (current.getParent() != null);

    // Request a layout to be re-done
    current.requestLayout();
}

그리고 다음은 작업 결과입니다. 여기에 이미지 설명 입력

왜 전체 Dialog기본 크기에 맞는 모든 케이스를 처리하기 위해 WRAP_CONTENT명시적인 minWidth세트를 원하지 않는지 혼란 스럽지만 그럴만 한 이유가 있다고 확신합니다 (듣고 싶을 것입니다) .


dialog.show();

그냥 사용

dialog.getWindow().setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, yourHeight);

매우 간단한 솔루션이지만 저에게 효과적입니다. 그래도 대화 상자를 확장하고 있지만 이것이 DialogFragment에서도 작동한다고 가정합니다.


AlertDialog는이 두 개의 창 속성을 사용하여 가능한 가장 작은 크기를 정의하여 태블릿에서 화면 중앙에 적절한 너비로 떠 있습니다.

http://developer.android.com/reference/android/R.attr.html#windowMinWidthMajor http://developer.android.com/reference/android/R.attr.html#windowMinWidthMinor

선택한 기본 대화 상자 스타일을 확장하고 값을 변경하여 고유 한 논리를 적용 할 수 있습니다.


이것은 나를 위해 잘 작동했습니다.

WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
lp.copyFrom(dialog.getWindow().getAttributes());
lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
dialog.show();
dialog.getWindow().setAttributes(lp);

BT의 답변 에서 한 가지 문제를 발견했습니다 . 대화 상자가 할당 된 상태로 남았습니다 (화면 중앙이 아님). 이 문제를 해결하기 위해 부모 레이아웃의 중력 변경을 추가했습니다. 업데이트 된 forceWrapContent () 메서드를 참조하세요.

protected void forceWrapContent(View v) {
    // Start with the provided view
    View current = v;

    // Travel up the tree until fail, modifying the LayoutParams
    do {
        // Get the parent
        ViewParent parent = current.getParent();

        // Check if the parent exists
        if (parent != null) {
            // Get the view
            try {
                current = (View) parent;
                ViewGroup.LayoutParams layoutParams = current.getLayoutParams();
                if (layoutParams instanceof FrameLayout.LayoutParams) {
                    ((FrameLayout.LayoutParams) layoutParams).
                            gravity = Gravity.CENTER_HORIZONTAL;
                } else if (layoutParams instanceof WindowManager.LayoutParams) {
                    ((WindowManager.LayoutParams) layoutParams).
                            gravity = Gravity.CENTER_HORIZONTAL;
                }
            } catch (ClassCastException e) {
                // This will happen when at the top view, it cannot be cast to a View
                break;
            }

            // Modify the layout
            current.getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT;
        }
    } while (current.getParent() != null);

    // Request a layout to be re-done
    current.requestLayout();
}

대화 상자는 내용을 감싸 야합니다.

투명한 배경무게 중심이 있는 match_parent사용하여 부모 레이아웃만들 수 있습니다 . 기본 레이아웃을 그 아래에 놓습니다. 그래서 것 보면 같은 중심 위치 대화 상자를 표시합니다.

이 방법을 사용하면 Scrollview, RecyclerView 및 대화 상자에서 모든 유형의 레이아웃을 사용할 수 있습니다.

public void showCustomDialog(Context context) {
    Dialog dialog = new Dialog(context);
    dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
    LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    view = inflater.inflate(R.layout.dialog_layout, null, false); 
    findByIds(view);  /*find your views by ids*/
    ((Activity) context).getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
    dialog.setContentView(view);
    final Window window = dialog.getWindow();
    window.setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT);
    window.setBackgroundDrawableResource(R.color.colorTransparent);
    window.setGravity(Gravity.CENTER);
    dialog.show();
}

사용하다 setStyle(STYLE_NORMAL, android.R.style.Theme_Holo_Light_Dialog);


AppCompatDialog를 사용하십시오.

import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.AppCompatDialog;
import android.view.Window;
import android.widget.ProgressBar;

public class ProgressDialogCompat extends AppCompatDialog {

    public ProgressDialogCompat(Context context) {
        super(context);
        supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Context context = getContext();
        int padding = context.getResources().getDimensionPixelSize(R.dimen.x_medium);
        ProgressBar progressBar = new ProgressBar(context);
        progressBar.setPadding(padding, padding, padding, padding);
        setContentView(progressBar);
        setCancelable(false);
        super.onCreate(savedInstanceState);
    }
}

참조 URL : https://stackoverflow.com/questions/14907104/alertdialog-with-custom-view-resize-to-wrap-the-views-content

반응형