programing

대화 상자에서 값을 반환하는 Android 'Best Practice'

randomtip 2021. 1. 16. 09:32
반응형

대화 상자에서 값을 반환하는 Android 'Best Practice'


복잡한 사용자 정의 대화 상자 (예 : 텍스트 필드, 날짜 또는 시간 선택기, 라디오 단추 등)에서 "저장"및 "취소"단추와 같은 값을 호출 활동에 반환하는 "올바른"방법은 무엇입니까?

웹에서 본 기술 중 일부 는 다음과 같습니다.

  • 활동에서 읽을 수있는 Dialog 파생 클래스의 공용 데이터 멤버

  • 공개 "get"접근 자. . . "..". . "

  • Intent ( show () 와 반대 ) 및 Dialog 클래스의 핸들러를 사용하여 대화를 시작합니다.이 핸들러는 다양한 컨트롤에서 입력을 받아 활동으로 다시 전달되도록 번들로 묶어 리스너가 "저장"을 누르면 번들이 다시 전달됩니다. 사용 ) (ReturnIntent을

  • 대화 상자에있는 컨트롤의 입력을 처리하는 Activity의 리스너, 예를 들어 TimePicker 또는 DatePicker의 리스너는 실제로 Activity에 있습니다. 이 계획에서 사실상 모든 작업은 활동에서 수행됩니다.

  • "저장"버튼에 대한 활동의 ​​한 리스너와 활동은 대화 상자의 제어를 직접 조사합니다. 활동이 대화 상자를 닫습니다.

... 이미 잊은 것이 더 있습니다.

표준 적으로 올바른 방법 또는 "모범 사례"방법으로 간주되는 특정 기술이 있습니까?


다음과 같은 방법으로 사용하고 있습니다.

  1. 내 모든 활동에는 하나의 동일한 상위 활동이 있습니다 (ControlActivity라고 가정 해 보겠습니다). ControlActivity에는 private volatile Bundle controlBundle;적절한 getter / setter가 있습니다.
  2. 대화를 시작할 때 내 자신의 방법을 통해 대화를 호출했습니다.

    public void showMyDialog(int id, Bundle bundle)
    {
        this.controlBundle=bundle;
        this.showDialog(id, bundle);
    }
    

따라서 대화 상자에 전송 된 매개 변수를 알 때마다

  1. 대화 상자가 완료 되려고 할 때 Bundle필요한 값으로 다른 대화 상자를 구성한 다음 Activity번들 설정기를 통해 입력합니다 .

((ControlActivity )this.getOwnerActivity).setControlBundle(bundle);

그래서 결국 대화가 끝나면 대화에서 "반환 된"값을 알고 있습니다. int retCode=this.showMyDialog();좀 더 복잡한 것 같지는 않지만 실행 가능 하다는 것을 알고 있습니다.


아마도 귀하의 질문을 오해하고 있지만 왜 내장 리스너 시스템을 사용하지 않는 이유는 무엇입니까?

builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
    public void onClick(DialogInterface dialog, int id) {
        // run whatever code you want to run here
        // if you need to pass data back, just call a function in your
        // activity and pass it some parameters
    }
})

이것이 제가 항상 대화 상자의 데이터를 처리하는 방법입니다.

편집 : 귀하의 질문에 더 잘 대답 할 수있는보다 구체적인 예를 제공하겠습니다. 이 페이지에서 몇 가지 샘플 코드를 훔칠 것입니다.

http://developer.android.com/guide/topics/ui/dialogs.html

// Alert Dialog code (mostly copied from the Android docs
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Pick a color");
builder.setItems(items, new DialogInterface.OnClickListener() {
    public void onClick(DialogInterface dialog, int item) {
        myFunction(item);
    }
});
AlertDialog alert = builder.create();

...

// Now elsewhere in your Activity class, you would have this function
private void myFunction(int result){
    // Now the data has been "returned" (as pointed out, that's not
    // the right terminology)
}

내 MIDI 앱의 경우 예 / 아니오 / 취소 확인 대화 상자가 필요했기 때문에 먼저 일반 StandardDialog 클래스를 만들었습니다.

public class StandardDialog {

    import android.app.Activity;
    import android.app.AlertDialog;
    import android.content.DialogInterface;
    import android.os.Handler;

    public class StandardDialog {
    public static final int dlgResultOk         = 0;
    public static final int dlgResultYes        = 1;
    public static final int dlgResultNo         = 2;
    public static final int dlgResultCancel     = 3;

    public static final int dlgTypeOk           = 10;
    public static final int dlgTypeYesNo        = 11;
    public static final int dlgTypeYesNoCancel  = 12;

    private Handler mResponseHandler;
    private AlertDialog.Builder mDialogBuilder;
    private int mDialogId;

    public StandardDialog(Activity parent, 
                          Handler reponseHandler, 
                          String title, 
                          String message, 
                          int dialogType, 
                          int dialogId) {

        mResponseHandler = reponseHandler;
        mDialogId = dialogId;
        mDialogBuilder = new AlertDialog.Builder(parent);
        mDialogBuilder.setCancelable(false);
        mDialogBuilder.setTitle(title);
        mDialogBuilder.setIcon(android.R.drawable.ic_dialog_alert);
        mDialogBuilder.setMessage(message);
        switch (dialogType) {
        case dlgTypeOk:
            mDialogBuilder.setNeutralButton("Ok", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    mResponseHandler.sendEmptyMessage(mDialogId + dlgResultOk);
                }
            });         
            break;

        case dlgTypeYesNo:
        case dlgTypeYesNoCancel:
            mDialogBuilder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    mResponseHandler.sendEmptyMessage(mDialogId + dlgResultYes);
                }
            });         
            mDialogBuilder.setNegativeButton("No", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    mResponseHandler.sendEmptyMessage(mDialogId + dlgResultNo);
                }
            });         
            if (dialogType == dlgTypeYesNoCancel) {
                mDialogBuilder.setNeutralButton("Cancel", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        mResponseHandler.sendEmptyMessage(mDialogId + dlgResultCancel);
                    }
                });         
            }
            break;
        }
        mDialogBuilder.show();
    }
}

다음으로, 내 주요 활동에는 다른 스레드의 UI 업데이트에 대한 메시지 처리기가 이미 있으므로 대화 상자에서 메시지를 처리하기위한 코드를 추가했습니다. 다양한 프로그램 기능에 대해 StandardDialog를 인스턴스화 할 때 다른 dialogId 매개 변수를 사용하여 적절한 코드를 실행하여 다양한 질문에 대한 예 / 아니오 / 취소 응답을 처리 할 수 ​​있습니다. 이 아이디어는 단순한 정수 메시지보다 훨씬 느리지 만 데이터 번들을 전송하여 복잡한 사용자 정의 대화 상자에 대해 확장 할 수 있습니다.

private Handler uiMsgHandler = new Handler() {

    @Override
    public void handleMessage(Message msg) {
        if (msg != null) {

            // {Code to check for other UI messages here}

            // Check for dialog box responses
            if (msg.what == (clearDlgId + StandardDialog.dlgResultYes)) {
                doClearDlgYesClicked();
            }
            else if (msg.what == (recordDlgId + StandardDialog.dlgResultYes)) {
                doRecordDlgYesClicked();
            }
            else if (msg.what == (recordDlgId + StandardDialog.dlgResultNo)) {
                doRecordDlgNoClicked();
            }
        }
    }
};

그런 다음 활동에서 do {Whatever} () 메서드를 정의하기 만하면됩니다. 대화 상자를 불러 오려면 예를 들어 "Clear Recording MIDI events"버튼에 응답하고 다음과 같이 확인하는 방법이 있습니다.

public void onClearBtnClicked(View view) {
    new StandardDialog(this, uiMsgHandler, 
        getResources().getString(R.string.dlgTitleClear),
        getResources().getString(R.string.dlgMsgClear), 
        StandardDialog.dlgTypeYesNo, clearDlgId);
}

clearDlgId다른 곳에서는 고유 한 정수로 정의됩니다. 이 방법은 활동 앞에 예 / 아니오 대화 상자를 표시하여 대화 상자가 닫힐 때까지 초점을 잃고 활동이 대화 결과와 함께 메시지를 가져옵니다. 그런 다음 doClearDlgYesClicked()"예"버튼을 클릭하면 메시지 처리기가 메서드를 호출합니다 . (이 경우에는 조치가 필요하지 않았기 때문에 "아니오"버튼에 대한 메시지가 필요하지 않았습니다).

어쨌든,이 방법은 저에게 효과적이며 대화에서 결과를 쉽게 전달할 수 있습니다.


나는 이것에 대해 한동안 숙고 해왔고 결국 내가 찾은 가장 편리한 방법은 내 활동을 각 제어 흐름 단위를 나타내는 다양한 방법으로 나누는 것입니다. 예를 들어, 내 활동 내의 활동이 다음과 같은 경우 : 인 텐트에서 변수로드, 일부 데이터 확인, 가능한 경우 처리 및 진행, 백그라운드 호출이 아닌 경우 사용자 상호 작용 대기, 다른 활동 시작.

나는 일반적으로 공통적 인 부분,이 경우 처음 두 개와 마지막 부분을 픽업합니다. 나는 첫 번째 것을 포장 onCreate()하고 마지막을 위해 별도의 것을 만들 것입니다 startAnotherActivity(Data). 그들이 구성되어 있습니다, 그래서 당신은 중간 부분을 정렬 할 수 있습니다 checkData(Data)(아마도에 병합 onCreate()중 하나를 호출하는) processAvailableData(Data)performBackgroundTask(Data). 백그라운드 작업은 백그라운드에서 작업을 수행하고 제어를로 반환합니다 onBackgroundTaskCompleted(OtherData).

이제 둘 다 processAvailableData(Data)그리고 차례로 그 함수를 호출 하거나 병합 할 수 onBackgroundTaskCompleted(OtherData)있는 getUserResponse()메서드를 호출 startAnotherActivity(Data)합니다.

저는이 접근 방식이 많은 이점을 제공한다고 생각합니다.

  1. 데이터를 반환하는 대신 "앞으로 이동"하여 질문이 가리키는 데이터 반환 문제에 도움이됩니다.
  2. 새로운 기능을 더 쉽게 추가 할 수 있습니다. 예를 들어, 사용자에게 더 많은 옵션을 제공하려면 getUserResponse()결국 다음 활동으로 전달되는 데이터에 영향을 미칠 수 있는 적절한 메서드를 호출하면됩니다 .
  3. 직관적 인 가정이 특정 흐름이고 다른 흐름으로 판명 될 때 불필요한 흐름 문제를 피하는 데 도움이됩니다 ( SO finish()관련된 질문 확인 return).
  4. 익명의 내부 클래스 (onClick (), doInBackground () 등)에서 변수 액세스 문제를 방지하기 위해 많은 클래스 수준 필드를 가지지 않도록 변수를 더 잘 관리 할 수 ​​있습니다.

더 많은 메서드를 사용하면 오버 헤드가 추가되지만 흐름, 재사용 및 단순성 이점에 의해 상쇄 될 수 있습니다 (이에 대한 컴파일 전문가의 의견을 듣고 싶습니다).


두 조각의 예를 들어 하나의 솔루션을 설명하겠습니다. SimpleFragment날짜를 렌더링 할 텍스트 필드가 하나 뿐인가 있다고 상상해보십시오 . 그런 다음 DatePickerFragment특정 날짜를 선택할 수있는 것이 있습니다. 내가 원하는 것은 사용자가 선택을 확인할 때마다 DatePickerFragment날짜 값을 호출에 다시 전달하는 것 SimpleFragment입니다.

SimpleFragment

따라서 먼저 다음 DatePickerFragment에서 시작 합니다 SimpleFragment.

private DateTime mFavoriteDate; // Joda-Time date

private void launchDatePicker() {
    DatePickerFragment datePickerFragment = new DatePickerFragment();
    Bundle extras = new Bundle();
    // Pass an initial or the last value for the date picker
    long dateInMilliSeconds = mFavoriteDate.getMillis();
    extras.putLong(BundleKeys.LAST_KNOWN_DATE, dateInMilliSeconds);
    datePickerFragment.setArguments(extras);
    datePickerFragment.setTargetFragment(this, SIMPLE_FRAGMENT_REQUEST_CODE);
    datePickerFragment.show(getActivity().getSupportFragmentManager(),
            DatePickerFragment.FRAGMENT_TAG);
}

DatePickerFragment

대화 조각에서 사용자가 긍정적 인 버튼을 눌렀을 때 선택한 날짜를 다시 전달할 준비를합니다.

public static final String DATE_PICKED_INTENT_KEY = "DATE_PICKED_INTENT_KEY";
public static final int DATE_PICKED_RESULT_CODE = 123;

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    // ...
    Long dateInMilliSeconds = getArguments().getLong(BundleKeys.LAST_KNOWN_DATE);
    DateTime date = new DateTime(dateInMilliSeconds);
    initializePickerUiControl(date);

    AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(activity);
    dialogBuilder
        .setPositiveButton(R.string.date_picker_positive, (dialog, which) -> {
            // Pass date to caller
            passBackDate();
        })
        .setNegativeButton(R.string.date_picker_negative, (dialog, which) -> {
            // Nothing to do here
        });
    return dialogBuilder.create();
}

private void passBackDate() {
    DateTime dateTime = getDateTimeFromPickerControl();
    Intent intent = new Intent();
    intent.putExtra(DATE_PICKED_INTENT_KEY, dateTime.getMillis());
    getTargetFragment().onActivityResult(
            getTargetRequestCode(), DATE_PICKED_RESULT_CODE, intent);
}

SimpleFragment

요청 프래그먼트로 돌아가서 대화에 의해 전달 된 것을 소비합니다.

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == SIMPLE_FRAGMENT_REQUEST_CODE &&
            resultCode == DatePickerFragment.DATE_PICKED_RESULT_CODE) {
        long datePickedInMilliseconds = data.getLongExtra(
                DatePickerFragment.DATE_PICKED_INTENT_KEY, 0);
        mFavoriteDate = new DateTime(datePickedInMilliseconds);
        updateFavoriteDateTextView();
    }
    else {
        super.onActivityResult(requestCode, resultCode, data);
    }
}

이전에 훌륭한 답변 을 준 mattpic에 대한 언급 .


꽤 많은 연구 끝에 콜백 인터페이스를 결정했습니다. 내 코드는 다음과 같습니다.

MyFragment.java

public class MyFragment extends Fragment {

...

private void displayFilter() {

    FragmentManager fragmentManager = getFragmentManager();

    FilterDialogFragment filterDialogFragment = new FilterDialogFragment();
    Bundle bundle = new Bundle();
    bundle.putSerializable("listener", new FilterDialogFragment.OnFilterClickListener() {
        @Override
        public void onFilterClickListener() {
            System.out.println("LISTENER CLICKED");

        }
    });
    filterDialogFragment.setArguments(bundle);
    filterDialogFragment.show(fragmentManager, DIALOG_FILTER);

}

MyDialog.java

public class MyDialog extends DialogFragment {

private ImageButton mBtnTest;
private OnFilterClickListener mOnFilterClickListener;

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    // Get the layout inflater
    LayoutInflater inflater = getActivity().getLayoutInflater();
    View filterLayout = inflater.inflate(R.layout.filter_dialog, null);
    // Inflate and set the layout for the dialog
    // Pass null as the parent view because its going in the dialog layout
    builder.setView(filterLayout)
            .setTitle("Filter");

    Dialog dialog = builder.create();

    mOnFilterClickListener = (OnFilterClickListener) getArguments().getSerializable("listener");

    mBtnTest = (ImageButton)filterLayout.findViewById(R.id.fandb);
    mBtnTest.setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
            mOnFilterClickListener.onFilterClickListener();
            dismiss();
        }
    });

    return dialog;
}

public interface OnFilterClickListener extends Serializable {
    void onFilterClickListener();
}

}

ReferenceURL : https://stackoverflow.com/questions/4473940/android-best-practice-returning-values-from-a-dialog

반응형