BottomSheetDialogFragment의 상태를 확장 됨으로 설정


92

Android 지원 디자인 라이브러리 (v23.2.1)를 BottomSheetDialogFragment사용 BottomSheetBehavior#setState(STATE_EXPANDED)하여 확장 으로 확장 되는 조각의 상태를 어떻게 설정 합니까?

https://code.google.com/p/android/issues/detail?id=202396 말한다 :

하단 시트는 처음에 STATE_COLLAPSED로 설정됩니다. 확장하려면 BottomSheetBehavior # setState (STATE_EXPANDED)를 호출합니다. 뷰 레이아웃 전에는 메서드를 호출 할 수 없습니다.

제안 연습 첫번째 팽창 할 뷰를 필요로하지만, 나는 확실히 내가 조각 위에 BottomSheetBehaviour을 설정합니다 방법을 모르겠어요 ( BottomSheetDialogFragment).

View bottomSheet = coordinatorLayout.findViewById(R.id.bottom_sheet);  
BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);  

답변:


221

"뷰 레이아웃 전에는 메소드를 호출 할 수 없습니다."

위의 텍스트가 단서입니다.

대화 상자에는 대화 상자가 표시 되면 시작되는 리스너가 있습니다. 배치되어 있지 않으면 대화 상자를 표시 할 수 없습니다.

따라서 onCreateDialog()모달 하단 시트 ( BottomSheetFragment)에서 대화 상자를 반환하기 직전에 (또는 대화 상자에 대한 참조가 있으면 어디에서나) 다음을 호출하십시오.

// This listener's onShow is fired when the dialog is shown
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
    @Override
    public void onShow(DialogInterface dialog) {

        // In a previous life I used this method to get handles to the positive and negative buttons
        // of a dialog in order to change their Typeface. Good ol' days.

        BottomSheetDialog d = (BottomSheetDialog) dialog;

        // This is gotten directly from the source of BottomSheetDialog
        // in the wrapInBottomSheet() method
        FrameLayout bottomSheet = (FrameLayout) d.findViewById(android.support.design.R.id.design_bottom_sheet);

        // Right here!
        BottomSheetBehavior.from(bottomSheet)
            .setState(BottomSheetBehavior.STATE_EXPANDED);
    }
});

제 경우에는 제 관습 BottomSheet은 다음과 같습니다.

@SuppressWarnings("ConstantConditions")
public class ShareBottomSheetFragment extends AppCompatDialogFragment {

    @NonNull @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {

        BottomSheetDialog dialog =
                new BottomSheetDialog(getActivity(), R.style.Haute_Dialog_ShareImage);

        dialog.setContentView(R.layout.dialog_share_image);

        dialog.findViewById(R.id.cancel).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dismiss();
            }
        });

        dialog.setOnShowListener(new DialogInterface.OnShowListener() {
            @Override
            public void onShow(DialogInterface dialog) {
                BottomSheetDialog d = (BottomSheetDialog) dialog;

                FrameLayout bottomSheet = (FrameLayout) d.findViewById(android.support.design.R.id.design_bottom_sheet);
                BottomSheetBehavior.from(bottomSheet).setState(BottomSheetBehavior.STATE_EXPANDED);
            }
        });

        SwitchCompat switchview = (SwitchCompat) dialog.findViewById(R.id.switchview);
        switchview.setTypeface(FontCache.get(dialog.getContext(), lookup(muli, NORMAL)));

        return dialog;
    }
}

이것이 도움이되는지 알려주세요.

최신 정보

다음 BottomSheetDialogFragment과 같이 재정의 할 수도 있습니다 .

public class SimpleInitiallyExpandedBottomSheetFragment extends BottomSheetDialogFragment {

    @NonNull @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {

        BottomSheetDialog dialog = (BottomSheetDialog) super.onCreateDialog(savedInstanceState);

        dialog.setOnShowListener(new DialogInterface.OnShowListener() {
            @Override
            public void onShow(DialogInterface dialog) {
                BottomSheetDialog d = (BottomSheetDialog) dialog;

                FrameLayout bottomSheet = (FrameLayout) d.findViewById(android.support.design.R.id.design_bottom_sheet);
                BottomSheetBehavior.from(bottomSheet).setState(BottomSheetBehavior.STATE_EXPANDED);
            }
        });

        // Do something with your dialog like setContentView() or whatever
        return dialog;
    }
}

그러나 나는 기본 BottomSheetFragment이 반환하는 것 외에는 아무것도하지 않기 때문에 왜 누군가가 그렇게하고 싶어하는지 알지 못합니다 BottomSheetDialog.

ANDROIDX 업데이트

AndroidX를 사용하는 경우 이전에에서 찾은 리소스 android.support.design.R.id.design_bottom_sheet는 이제에서 찾을 수 있습니다 com.google.android.material.R.id.design_bottom_sheet.


감사합니다.이 방법을 시도했습니다. BottomSheetDialogFragment축소 된 동작에서 확장 된 동작으로 이동할 때 버벅 거림 (시작 애니메이션에서 프레임을 건너 뛰는 것처럼 보임)을 만듭니다 . 편집 : Android Marshmallow 및 KitKat 기기에서 테스트
user2560886

1
그것은 나를 위해 완벽하게 작동합니다. 건너 뛰지 않습니다. 대화를 반환하는 것 외에 다른 일을하고 있습니까? 더 나은 아이디어를 얻을 수 있도록 코드로 게시물을 업데이트 해 주시면 감사하겠습니다.
efemoney

5
android.support.design.R지원 라이브러리를 업데이트 한 후 패키지 를 찾을 수 없습니다 .
natario

2
나는 또한 android.support.design.R@natario처럼 해결하는 데 문제가 있습니다. 나는 implementation "com.google.android.material:material:1.0.0". 나는 또한 프로젝트에서 AndroidX를 사용하고 있습니다.
hsson

21
AndroidX를 사용하는 경우 리소스를 찾을 수 있습니다.com.google.android.material.R.id.design_bottom_sheet
urgentx

45

당신이 사용하려는 경우 efeturi의 대답은, 그러나, 중대하다 onCreateView을 () 로가는 반대로, 당신의 BottomSheet을 만들 이 onCreateDialog () , 여기 당신이 아래에 추가해야합니다 코드입니다 onCreateView () 메소드는 :

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    getDialog().setOnShowListener(new DialogInterface.OnShowListener() {
        @Override
        public void onShow(DialogInterface dialog) {
            BottomSheetDialog d = (BottomSheetDialog) dialog;
            View bottomSheetInternal = d.findViewById(android.support.design.R.id.design_bottom_sheet);
            BottomSheetBehavior.from(bottomSheetInternal).setState(BottomSheetBehavior.STATE_EXPANDED);
        }
    });
    return inflater.inflate(R.layout.your_bottomsheet_content_layout, container, false);
}

3
또는 getDialog를 전혀 호출 할 필요가 없습니다. 가장 깨끗한 방법은 onCreateView와 onCreateDialog를 모두 재정의하는 것입니다. onCreateView에서 뷰를 빌드하고 (다른 프래그먼트와 마찬가지로) onCreateDialog에서 대화 상자 별 코드를 수행합니다 (인스턴스를 가져 오려면
super.onCreateDialog

2
이것은 내 하루를 구합니다. 감사.
AndroidRuntimeException

onCreateDialog를 사용하는 경우 @Stimsoni onCreateView가 호출되지 않습니다. developer.android.com/reference/android/support/v4/app/…
Vincent_Paing

1
@Vincent_Paing, 그렇습니다. 첨부 된 링크에는 'onCreateView를 구현할 필요가 없습니다'라고 표시되어 있습니다. 그것은 호출되지 않을 것이라고 말하지 않습니다. 여기 github.com/material-components/material-components-android/blob/… 소스 코드를 살펴보십시오 . 기본 구현은 onCreateDialog를 호출하여 하단 시트를 만들고 위의 모든 솔루션은 여전히 ​​onCreateView를 사용하여 둘 다 항상 호출됨을 의미합니다. 재정의하는 경우 super.onCreateDialog ()를 호출해야합니다.
Stimsoni

BottomSheetDialogFragment에서 그것은 onCreateView ()에서 나를 충돌시킵니다. onViewCreated ()에 넣었고 완벽합니다! 감사합니다
avisper

22

단순하고 우아한 솔루션 :

BottomSheetDialogFragment 이를 해결하기 위해 하위 분류 될 수 있습니다.

class NonCollapsableBottomSheetDialogFragment extends BottomSheetDialogFragment {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        final BottomSheetDialog bottomSheetDialog = (BottomSheetDialog) super.onCreateDialog(savedInstanceState);

        bottomSheetDialog.setOnShowListener(new DialogInterface.OnShowListener() {
            @Override
            public void onShow(DialogInterface dialog) {
                FrameLayout bottomSheet = bottomSheetDialog.findViewById(com.google.android.material.R.id.design_bottom_sheet);

                BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);
                behavior.setSkipCollapsed(true);
                behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
            }
        });
        return bottomSheetDialog;
    }
}

따라서 BottomSheetDialogFragment자신의 하단 시트를 만드는 대신이 클래스를 확장하십시오 .

노트

프로젝트에서 이전 Android 지원 라이브러리를 사용 com.google.android.material.R.id.design_bottom_sheet하는 android.support.design.R.id.design_bottom_sheet경우로 변경 합니다 .


1
것 같다 com.google.android.material.R지금은 대신 android.support.design.R.
EpicPandaForce

@EpicPandaForce 물론입니다. Google의 Android 팀은 최근 이전 지원 라이브러리를 다시 패키징했습니다.
DYS

4

위의 것이 더 좋다고 생각합니다. 슬프게도 나는 해결하기 전에 그러한 해결책을 찾지 못했습니다. 하지만 내 솔루션을 작성하십시오. 모두 비슷합니다.

================================================ ===============================

나는 같은 문제에 직면 해있다. 이것이 제가 해결 한 것입니다. 동작은 동작을 얻을 수있는 BottomSheetDialog에 숨겨져 있습니다. 부모 레이아웃을 CooridateLayout으로 변경하지 않으려면 이것을 시도 할 수 있습니다.

1 단계 : BottomSheetDialogFragment 사용자 지정

open class CBottomSheetDialogFragment : BottomSheetDialogFragment() {
   //wanna get the bottomSheetDialog
   protected lateinit var dialog : BottomSheetDialog 
   override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
      dialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog
      return dialog
   }

   //set the behavior here
   fun setFullScreen(){
      dialog.behavior.state = STATE_EXPANDED
   }
}

2 단계 : 조각이이 사용자 지정 조각을 확장하도록합니다.

class YourBottomSheetFragment : CBottomSheetDialogFragment(){

   //make sure invoke this method after view is built
   //such as after OnActivityCreated(savedInstanceState: Bundle?)
   override fun onStart() {
      super.onStart()
      setFullScreen()//initiated at onActivityCreated(), onStart()
   }
}

3
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
            @Override
            public void onShow(DialogInterface dialog) {
                BottomSheetDialog d = (BottomSheetDialog) dialog;

                FrameLayout bottomSheet = (FrameLayout) d.findViewById(android.support.design.R.id.design_bottom_sheet);
                BottomSheetBehavior.from(bottomSheet).setState(BottomSheetBehavior.STATE_EXPANDED);
            }
        });

null을 반환 BottomSheetBehavior.from(bottomSheet)하기 때문에 NullPointException을 만났습니다 d.findViewById(android.support.design.R.id.design_bottom_sheet).

이상 해요. 이 코드 줄을 DEBUG 모드에서 Android Monitor의 Watches에 추가하고 정상적으로 Framelayout을 반환합니다.

wrapInBottomSheetBottomSheetDialog 의 코드는 다음과 같습니다 .

private View wrapInBottomSheet(int layoutResId, View view, ViewGroup.LayoutParams params) {
        final CoordinatorLayout coordinator = (CoordinatorLayout) View.inflate(getContext(),
                R.layout.design_bottom_sheet_dialog, null);
        if (layoutResId != 0 && view == null) {
            view = getLayoutInflater().inflate(layoutResId, coordinator, false);
        }
        FrameLayout bottomSheet = (FrameLayout) coordinator.findViewById(R.id.design_bottom_sheet);
        BottomSheetBehavior.from(bottomSheet).setBottomSheetCallback(mBottomSheetCallback);
        if (params == null) {
            bottomSheet.addView(view);
        } else {
            bottomSheet.addView(view, params);
        }
        // We treat the CoordinatorLayout as outside the dialog though it is technically inside
        if (shouldWindowCloseOnTouchOutside()) {
            coordinator.findViewById(R.id.touch_outside).setOnClickListener(
                    new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            if (isShowing()) {
                                cancel();
                            }
                        }
                    });
        }
        return coordinator;
    }

때때로, 나는 발견 R.id.design_bottom_sheet동일하지 않습니다 android.support.design.R.id.design_bottom_sheet. 그들은 다른 R.java에서 다른 가치를 가지고 있습니다.

내가 변경 그래서 android.support.design.R.id.design_bottom_sheetR.id.design_bottom_sheet.

dialog.setOnShowListener(new DialogInterface.OnShowListener() {
            @Override
            public void onShow(DialogInterface dialog) {
                BottomSheetDialog d = (BottomSheetDialog) dialog;

                FrameLayout bottomSheet = (FrameLayout) d.findViewById(R.id.design_bottom_sheet); // use R.java of current project
                BottomSheetBehavior.from(bottomSheet).setState(BottomSheetBehavior.STATE_EXPANDED);
            }
        });

이제 더 이상 NullPointException이 없습니다.


3

BottomsheetDialogFragment상태를 적용 onResume하면이 문제가 해결됩니다.

@Override
public void onResume() {
    super.onResume();
    if(mBehavior!=null)
       mBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}

onShow(DialogInterface dialog)postDelayed애니메이션 결함이 발생할 수 있습니다


2

onShow ()를 사용한 모든 결과는 소프트 키보드가 표시 될 때 임의의 렌더링 버그를 유발합니다. 아래 스크린 샷 참조-BottomSheet 대화 상자는 화면 하단에 있지 않지만 키보드가 표시된 것처럼 배치됩니다. 이 문제는 항상 발생하는 것은 아니지만 자주 발생합니다.

여기에 이미지 설명 입력

최신 정보

개인 회원을 반영한 나의 솔루션은 불필요합니다. 소프트 키보드 숨기기 후 대화 상자를 만들고 표시하기 위해 postDelayed (약 100ms)를 사용하는 것이 더 나은 솔루션입니다. 그런 다음 onShow ()를 사용한 위의 솔루션은 괜찮습니다.

Utils.hideSoftKeyboard(this);
mView.postDelayed(new Runnable() {
    @Override
    public void run() {
        MyBottomSheetDialog dialog = new MyBottomSheetDialog();
        dialog.setListener(MyActivity.this);
        dialog.show(getSupportFragmentManager(), TAG_BOTTOM_SHEET_DLG);
    }
}, 100);

그래서 다른 솔루션을 구현하지만 BottomSheetDialog에는 모든 멤버가 개인용으로 있으므로 리플렉션을 사용해야합니다. 그러나 렌더링 버그를 해결합니다. BottomSheetDialogFragment 클래스는 BottomSheetDialog를 생성하는 onCreateDialog 메서드가있는 AppCompatDialogFragment뿐입니다. 내 클래스를 만드는 AppCompatDialogFragment의 자체 자식을 만들어 BottomSheetDialog를 확장하고 개인 동작 멤버에 대한 액세스를 해결하고 onStart 메서드에서 STATE_EXPANDED 상태로 설정합니다.

public class ExpandedBottomSheetDialog extends BottomSheetDialog {

    protected BottomSheetBehavior<FrameLayout> mBehavior;

    public ExpandedBottomSheetDialog(@NonNull Context context, @StyleRes int theme) {
        super(context, theme);
    }

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

        try {
            Field privateField = BottomSheetDialog.class.getDeclaredField("mBehavior");
            privateField.setAccessible(true);
            mBehavior = (BottomSheetBehavior<FrameLayout>) privateField.get(this);
        } catch (NoSuchFieldException e) {
            // do nothing
        } catch (IllegalAccessException e) {
            // do nothing
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        if (mBehavior != null) {
            mBehavior.setSkipCollapsed(true);
            mBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
        }
    }
}


public class AddAttachmentBottomSheetDialog extends AppCompatDialogFragment {

    ....

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return new ExpandedBottomSheetDialog(getContext(), getTheme());
    }

    ....
}

1

내가 구현 한 가장 쉬운 방법은 다음과 같습니다. 여기에서 android.support.design.R.id.design_bottom_sheet를 찾고 하단 시트 상태를 EXPANDED 로 설정합니다 .

이것이 없으면 뷰 높이가 화면 높이의 0.5 이상이면 하단 시트 가 항상 COLLAPSED 상태로 고정되어 전체 하단 시트를 보려면 수동으로 스크롤해야합니다.

class BottomSheetDialogExpanded(context: Context) : BottomSheetDialog(context) {

    private lateinit var mBehavior: BottomSheetBehavior<FrameLayout>

    override fun setContentView(view: View) {
        super.setContentView(view)
        val bottomSheet = window.decorView.findViewById<View>(android.support.design.R.id.design_bottom_sheet) as FrameLayout
        mBehavior = BottomSheetBehavior.from(bottomSheet)
        mBehavior.state = BottomSheetBehavior.STATE_EXPANDED
    }

    override fun onStart() {
        super.onStart()
        mBehavior.state = BottomSheetBehavior.STATE_EXPANDED
    }
}

1

유사 uregentx의 대답에 코 틀린 , 당신은에서 확장하는 조각 클래스를 선언 할 수 있습니다 BottomSheetDialogFragment, 그리고 뷰가 생성 될 때 대화 상자가 표시됩니다 후에는 대화 리스너의 기본 상태를 설정할 수 있습니다.

STATE_COLLAPSED : 하단 시트가 표시되지만보기 높이 만 표시됩니다.

STATE_EXPANDED : 하단 시트가 표시되고 최대 높이가 표시됩니다.

STATE_HALF_EXPANDED : 하단 시트가 표시되지만 절반 높이 만 표시됩니다.

class FragmentCreateGroup : BottomSheetDialogFragment() {
      ...

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View? {
        // Set dialog initial state when shown
        dialog?.setOnShowListener {
            val bottomSheetDialog = it as BottomSheetDialog
            val sheetInternal: View = bottomSheetDialog.findViewById(com.google.android.material.R.id.design_bottom_sheet)!!
            BottomSheetBehavior.from(sheetInternal).state = BottomSheetBehavior.STATE_COLLAPSED
        }

        val view = inflater.inflate(R.layout.fragment_create_group, container, false)
        ...

        return view
    }
}

Gradle에서 머티리얼 디자인 구현을 사용하는 것을 기억하십시오.

implementation "com.google.android.material:material:$version"

머티리얼 디자인 참조 하단 시트도 살펴보십시오.


dialog?onCreateView에서 변수의 출처 는 어디 입니까?
Eric Smith

dialog클래스의 속성이며 DialogFragment실제로는 Getter입니다. 이 예제에서는 해당 getter를 사용하여 현재 DialogFragment 인스턴스를 가져 왔습니다 setOnShowListener. 예를 들어 활동과 같은 프로젝트에서 이러한 종류의 지침을 사용하여 작업 표시 줄에 액세스하는 actionBar게터가 사용되었으므로 해당 구성 요소를 수정할 수 있습니다 (예actionBar?.subtitle = "abcd"
FJCG

1

내 대답은 약간의 수정으로 위의 대부분의 답변과 다소 동일합니다. findViewById를 사용하여 맨 아래 시트보기를 먼저 찾는 대신, 향후 변경 될 수 있으므로 프레임 워크보기 리소스 ID를 하드 코딩하지 않는 것이 좋습니다.

setOnShowListener(dialog -> {
            BottomSheetBehavior bottomSheetBehavior = ((BottomSheetDialog)dialog).getBehavior();
            bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
        });

1
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    return super.onCreateDialog(savedInstanceState).apply {
        setOnShowListener {
            (this@TipsBottomDialogFragment.dialog as BottomSheetDialog).behavior.setState(
                BottomSheetBehavior.STATE_EXPANDED
            )
        }
    }
}

1

우리가 다른 솔루션을 사용할 수 있다고 생각하므로 미래의 독자에게 이것을 게시하십시오.

나는 당신이 BottomSheetDialog.

내부 Android ID를 사용하는 것이 싫고 내부에 BottomSheetDialog getBehavior사용할 수 있는 메서드가 있다는 것을 방금 발견했습니다 .

이것을 내부에서 사용할 수 있습니다 BottomSheetDialog.

behavior.state = BottomSheetBehavior.STATE_EXPANDED

을 사용 BottomSheetDialogFragment하면 해당 DialogFragment에서 BottomSheetDialog.


1

BottomSheetDialogFragment :

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    (dialog as? BottomSheetDialog)?.behavior?.state = STATE_EXPANDED
}

또는 표시 할 준비가되면 :

private fun onContentLoaded(items: List<Any>) {
    adapter.submitList(items)
    (dialog as? BottomSheetDialog)?.behavior?.state = STATE_EXPANDED
}

0

Kotlin BottomSheetDialogFragment 클래스에서 아래와 같이 onCreateDialog를 재정의합니다.

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        val bottomSheetDialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog
        bottomSheetDialog.setOnShowListener {
            val bottomSheet =
                bottomSheetDialog.findViewById<FrameLayout>(
                    com.google.android.material.R.id.design_bottom_sheet
                )
            val behavior = BottomSheetBehavior.from(bottomSheet!!)
            behavior.skipCollapsed = true
            behavior.state = BottomSheetBehavior.STATE_EXPANDED
        }
        return bottomSheetDialog
    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.