Soru Bir diyalogdan değerleri döndüren Android “En İyi Uygulama”


Değeri karmaşık bir özel iletişim kutusundan (örneğin, metin alanları, tarih veya zaman seçici, bir dizi radyo düğmesi vb.) Artı bir "Kaydet" ve "İptal" düğmesine basarak arama etkinliğine döndürmenin "doğru" yolu nedir?

Bazı Web'de gördüğüm tekniklerin arasında şunlar yer alıyor:

  • Aktivite tarafından okunabilen Diyalog-türevli sınıftaki kamu veri üyeleri

  • kamu "get" erişimciler. . . "." . "

  • Diyaloğun bir niyetle başlatılması (aksine göstermek() ) Dialog sınıfındaki çeşitli işleyicilerin girdisini alan ve işleyiciye geri iletilecek şekilde işleyicilere iletilen artı işleyicileri, böylece dinleyicinin "Kaydet" düğmesine bastığında paket geri gönderilir. ReturnIntent ()

  • Örneğin, TimePicker veya DatePicker'in dinleyicileri gerçekten Aktivitede olduğundan, diyalogda bulunan kontrollerden gelen girdileri işleyen Aktivitedeki dinleyiciler. Bu şemada pratik olarak tüm çalışmalar Faaliyette yapılır.

  • "Kaydet" düğmesi için Aktivitedeki Tek Dinleyici ve ardından Etkinlik doğrudan diyalogdaki kontrolleri sorgular; Etkinlik iletişim kutusunu kapatır.

... artı daha çok unutmuşum.

Kanonik olarak doğru veya "en iyi uygulama" yöntemini dikkate alan özel bir teknik var mı?


52
2017-12-17 19:07


Menşei




Cevaplar:


Aşağıdaki yolu kullanıyorum:

  1. Tüm faaliyetlerim bir ve aynı ana Etkinliğe sahip (ControlActivity diyelim). ControlActivity var private volatile Bundle controlBundle; uygun alıcı / ayarlayıcı ile
  2. Diyaloğu başlattığımda, kendi yöntemimle diyaloğu çağırırdım:

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

Yani her zaman iletişim kutusuna gönderilen parametreleri biliyorum

  1. Diyalog tamamlanmak üzereyken, başka bir diyalog kuruyorum Bundle gerekli değerlerle ve sonra benim Activity paket ayarlayıcı:

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

Sonunda diyalog bittiğinde diyalogdan "geri döndüğüm" değeri biliyorum. Biliyorum ki öyle değil int retCode=this.showMyDialog(); biraz daha karmaşık, ama uygulanabilir.


4
2017-12-17 20:29



Barmaley'in örneği, sadece bir int'den daha karmaşık verileri ele almanın bir yoluna olan ihtiyacım daha yakın. Ama aynı zamanda başka bir stratejiyi de gösteriyor. Daha önce de belirttiğim gibi, çok çeşitli stratejiler görüyorum ve bir Android uzmanı olarak hangi stratejilerin “en iyi uygulama” ile uyumlu olduğunu anlamaya çalışıyorum. - Peter Nelson


Belki sorunuzu yanlış anlıyorum, ancak neden sadece yerleşik dinleyici sistemini kullanmıyorsunuz?

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
    }
})

Bu, her zaman iletişim kutularından gelen verileri ele aldım.

DÜZENLEME: Size sorunuzu daha iyi cevaplayabilecek daha somut bir örnek vereyim. Bu sayfadan okuyacağınız bazı örnek kodları çalacağım:

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)
}

19
2017-12-17 19:12



Sorum şu: Değerlerin İADE edilmesi için en iyi uygulama yönteminin olup olmadığı. Tanımladığınız şey, Etkinlik'in kullanıcının nasıl yapıldığını bildiğini gösterir. Kullanıcı bittiğinde, değerleri geri getirmenin birçok yolu var ama bunlardan birinin en iyi uygulama olup olmadığını bilmiyorum. - Peter Nelson
Diyaloğunuzu nasıl oluşturduğunuza, "geri döndüğünüz" hangi değerleri "döndürdüğünüze" bağlıdır. Sorunuza nasıl cevap vermeliyiz? - Falmarri
Sorun terminolojidir. Bir diyalogdan "geri dönmez". Bu bir yöntem ya da işlev değil. bir dönüş değeri yok. Kullanıcı iletişim kutusundaki bir şeyi tıkladığında bazı kodları çalıştırmak istersiniz. Bu kod iletişim kutusunu da kapatabilir, ancak bu bir değer döndürdüğü anlamına gelmez. Bilgisayardaki gibi yayınlanmış, bunu yapmanın yolu, diyaloğunuzdaki "bitmiş" ve "iptal et" tuşlarına bir onclick işleyicisini atamaktır. diğer şeylerin yanı sıra <aktivite> .dismissDialog (...) adını alırlar ve elbette etkinliklerinizdeki yöntemleri çağırırlar. - Jeffrey Blattman
Cevabımı güncelledim. - Computerish
Yanlıştır, onClick () 'de hangi kodu çalıştıramazsınız - orada yalnızca ebeveyn etkinliğinin üyelerini kullanabilirsiniz, stack / auto değişkenleri onClick () öğesine geçirilemez; - barmaley


MIDI uygulamasında, evet / hayır / iptal onay iletişim kutularına ihtiyacım vardı, bu yüzden ilk önce standart bir StandardDialog sınıfı yaptım:

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();
    }
}

Daha sonra, ana faaliyetimde, diğer iş parçacıklarından UI güncellemeleri için bir ileti işleyicisi zaten vardı, bu yüzden iletişim kutularından gelen iletileri işlemek için kod ekledim. Çeşitli program işlevleri için StandardDialog'u başlattığımda farklı bir dialogId parametresi kullanarak, farklı sorulara evet / hayır / yanıt yanıtlarını işlemek için uygun kodu çalıştırabilirim. Bu fikir, basit bir tam sayı mesajından çok daha yavaş olmasına rağmen, bir veri Bundle göndererek karmaşık özel diyaloglar için genişletilebilir.

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();
            }
        }
    }
};

Sonra tek yapmam gereken, aktivitede do {Whatever} () yöntemlerini tanımlamak. Bir iletişim kutusu açmak için, bir örnek olarak "kaydedilmiş bir MIDI olayları" düğmesine yanıt veren bir yönteme sahibiz ve aşağıdaki gibi onaylayın:

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

clearDlgId başka bir yerde benzersiz bir tam sayı olarak tanımlanır. Bu yöntem, etkinliğin önünde bir Evet / Hayır iletişim kutusu açar; bu, iletişim kutusu kapanana kadar odağı kaybeder, bu durumda etkinlik iletişim kutusuyla bir ileti alır. Ardından mesaj işleyici doClearDlgYesClicked() "Evet" düğmesine tıklandığında (Bu durumda hiçbir işlem gerekmediğinden "Hayır" düğmesi için bir mesaja ihtiyacım yoktu).

Her neyse, bu yöntem benim için çalışır ve sonuçları bir diyalogdan geri almayı kolaylaştırır.


7
2017-12-01 21:22



Bu benim için iyi çalışıyor. Ancak Android stüdyosunda Handler sınıfı statik olmalı veya bellek sızıntısı riski (sarı metin) var. Statik dönüştürün, çalışmasını sağlayan büyük sorunlar var. Herhangi bir yorum? - Jan Bergström
Çok teşekkür ederim! - Michal


Bunu kendim için bir süre düşünmüştüm ve sonunda bunu yapmanın en uygun yolu, faaliyetimi her bir kontrol akışını temsil eden çeşitli yöntemlere ayırmaktır. Örneğin, benim faaliyetlerimdeki aktiviteler deyince: değişkenleri yükten yükleyin, bazı verileri kontrol edin, varsa işlem yapın ve devam edin, arka plan araması yapmazsanız, kullanıcı etkileşimini bekleyin, başka bir etkinlik başlatın.

Genelde ortak olan parçaları alıyorum, bu durumda ilk iki ve sonuncu. İlk olanları saracağım onCreate() ve son için ayrı bir tane yap startAnotherActivity(Data). Orta kısımları düzenleyebilirsiniz, böylece bir checkData(Data) (muhtemelen onCreate()) ya da processAvailableData(Data) veya performBackgroundTask(Data). Arka plan görevi arka planda bir işlem gerçekleştirir ve onBackgroundTaskCompleted(OtherData).

Şimdi ikisi processAvailableData(Data) ve onBackgroundTaskCompleted(OtherData) ara getUserResponse() sırayla hangi yöntem arayabilir startAnotherActivity(Data) veya işlevlerini kendi ile birleştirin.

Bu yaklaşımın birtakım faydalar sağladığını hissediyorum.

  1. Verilerinizi iade etmek yerine "ileriye doğru hareket etmek" ile sorularınızın cevap vermesine yardımcı olur.
  2. Yeni işlevsellik daha kolay eklenmesini sağlar. Örneğin, kullanıcıya daha fazla seçenek sunmak istiyorsak, uygun yöntemi yalnızca getUserResponse() En sonunda bir sonraki etkinliğe geçirilen verileri etkileyebilir.
  3. Aşırı akış problemlerinden kaçınmaya yardımcı olur (ilgili soruları kontrol edin. finish() ve return SO), sezgisel varsayımımız belirli bir akış olduğunda ve başka biri olduğunda ortaya çıkar.
  4. Değişkenleri daha iyi yönetmenize yardımcı olur, böylece anonim iç sınıflarda (onClick (), doInBackground (), vb.) Değişken erişim sorunlarından kaçınmak için çok fazla sınıf düzeyi alanınız olmasın.

Eminim ki, daha fazla yönteme sahip olmanın bazı ek yükleri eklediğini, ancak muhtemelen akış, yeniden kullanım ve kolaylık avantajlarıyla dengelendiğini görüyorsunuz (bir derleme uzmanının bu konudaki görüşlerini duymayı çok isterim).


2
2018-06-04 18:40





İki parçanın örneği ile bir çözümü açıklayacağım. Orada bir düşünün SimpleFragment Bir tarih oluşturmak için sadece bir metin alanı vardır. Sonra bir DatePickerFragment hangi belirli bir tarih seçmek için izin verir. İstediğim şey şu ki DatePickerFragment tarih değerini tekrar çağrıya geçirir SimpleFragmentKullanıcı seçimini onayladığında.

SimpleFragment

Yani, her şeyden önce DatePickerFragment içinden 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

İletişim parçasında, kullanıcı pozitif tuşa bastığında seçilen tarihi geri almayı planlıyoruz:

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

Talep eden parçaya geri dönersek, diyalog tarafından geriye aktarılanları tüketiriz:

@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);
    }
}

Referanslar mattpic kim verdi mükemmel cevap önce.


1
2018-01-12 11:49





Oldukça fazla araştırmadan sonra geri arama arayüzüne yerleştim. Benim kodum şu şekildedir:

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();
}

}

1
2018-06-10 18:02