javafx ComboBox dengan cellfactory tidak menampilkan item yang dipilih

Saya memiliki aplikasi bukti konsep kecil yang berisi 6 Labels a ComboBox dan Button, semuanya dibuat menggunakan SceneBuilder.

Dengan mengklik Button, aplikasi membuat panggilan api Json untuk mengembalikan daftar negara dan detail terkaitnya (apla2code, apla3code, nama, dll). Saya membuat objek CountryDetails yang menampung 3 String elemen. Saya menggunakannya untuk mengembalikan array CountryDetails yang kemudian saya muat ke dalam array ObserbavleList. Saya kemudian menerapkannya ke ComboBox dan saya memuat elemen CountryDetails ke dalam 3 label setiap kali item dipilih di ComboBox. Semua ini berfungsi dengan baik (walaupun mungkin ada cara yang lebih baik untuk melakukan ini).

Masalah yang saya alami adalah ComboBox tidak menampilkan item yang dipilih dan saya tidak tahu cara memperbaikinya. Gambar di bawah menunjukkan apa masalahnya.

Kode yang membuat panggilan api adalah sebagai berikut:

import com.google.gson.Gson;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

public class GetCountries {

    public CountryDetails[] getDetails() {

        String inputLine            = "";
        StringBuilder jsonString    = new StringBuilder();

        HttpURLConnection urlConnection;

        try {
            URL urlObject = new URL("https://restcountries.eu/rest/v2/all");

            urlConnection = (HttpURLConnection) urlObject.openConnection();
            urlConnection.setRequestMethod("GET");

            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
            while ((inputLine = bufferedReader.readLine()) != null) {
                jsonString.append(inputLine);
            }
            urlConnection.getInputStream().close();

        } catch(IOException ioe) {
            System.out.println(ioe.getMessage());
        }

        Countries[] countries = new Gson().fromJson(jsonString.toString(), Countries[].class);

        CountryDetails[] countryDetails = new CountryDetails[countries.length];

        for(int i = 0; i < countries.length; i++){

            countryDetails[i] = new CountryDetails(
                    countries[i].getAlpha2Code(),
                    countries[i].getAlpha3Code(),
                    countries[i].getName()
            );
        }
        return countryDetails;
    }
} 

Kode untuk objek CountryDetails adalah sebagai berikut:

public class CountryDetails {

    private String alpha2Code;
    private String alpha3Code;
    private String name;

    public CountryDetails(String strAlpha2Code, String strAlpha3Code, String strName) {
        this.alpha2Code = strAlpha2Code;
        this.alpha3Code = strAlpha3Code;
        this.name = strName;
    }

    public String getAlpha2Code() { return alpha2Code; }

    public void setAlpha2Code(String alpha2Code) { this.alpha2Code = alpha2Code; }

    public String getAlpha3Code() { return alpha3Code; }

    public void setAlpha3Code(String alpha3Code) { this.alpha3Code = alpha3Code; }

    public String getName() { return name; }

    public void setName(String name) {this.name = name; }
}

Kode yang memuat ObservableList adalah sebagai berikut:

GetCountries countries = new GetCountries();

        CountryDetails[] countryDetails = countries.getDetails();

        for (CountryDetails countryDetail : countryDetails) {
            countriesObservableList.add(new CountryDetails(
                    countryDetail.getAlpha2Code(),
                    countryDetail.getAlpha3Code(),
                    countryDetail.getName())
            );
        }

Kode yang memuat ComboBox dan menampilkan elemen di Labels adalah sebagai berikut:

    cbCountryList.setCellFactory(new Callback<ListView<CountryDetails>, ListCell<CountryDetails>>() {
            @Override public ListCell<CountryDetails> call(ListView<CountryDetails> p) {
                return new ListCell<CountryDetails>() {
                    @Override
                    protected void updateItem(CountryDetails item, boolean empty) {
                        super.updateItem(item, empty);
                        if (empty || (item == null) || (item.getName() == null)) {
                            setText(null);
                        } else {
                            setText(item.getName());
                        }
                    }
                };
            }
        });

    public void comboAction(ActionEvent event) {
        lblAlpha2Code.setText(cbCountryList.getValue().getAlpha2Code());
        lblAlpha3Code.setText(cbCountryList.getValue().getAlpha3Code());
        lblCountryName.setText(cbCountryList.getValue().getName());
    }

Di bawah ini adalah gambar aplikasinya:

masukkan deskripsi gambar di sini


person Yastafari    schedule 07.01.2020    source sumber
comment
Setel ComboBox#buttonCell ke sebuah instance dikembalikan oleh pabrik sel khusus Anda.   -  person Slaw    schedule 07.01.2020


Jawaban (1)


Masalah yang saya alami adalah ComboBox tidak menampilkan item yang dipilih dan saya tidak tahu cara memperbaikinya.

Anda perlu menyetel StringConverter untuk Anda cbCountryList.

cbCountryList.setConverter(new StringConverter<CountryDetails>() {
    @Override
    public String toString(CountryDetails object) {
        return object.getName();
    }

    @Override
    public CountryDetails fromString(String string) {
        return null;
    }
});

Semua ini berfungsi dengan baik (walaupun mungkin ada cara yang lebih baik untuk melakukan ini).

Anda dapat mempertimbangkan untuk memperbarui hal-hal berikut,

  • Panggilan asinkron untuk permintaan HTTP dan memuat item
  • Anda dapat menyimpan daftar fetched-country Anda dalam cache saat Anda menelepon setiap kali tombol dipicu. Anda dapat membuat objek Singleton untuk GetCountries.

Kelas GetCountries yang dimodifikasi,

Ini menyimpan daftar negara dalam cache dan menggunakan data cache untuk beberapa permintaan,

 public static class GetCountries {

    private static final String API_URL = "https://restcountries.eu/rest/v2/all";
    private static CountryDetails[] countryDetails;

    public static CountryDetails[] getDetails() {

        //uses cached countryDetails once it gets loaded
        if (countryDetails != null) {
            return countryDetails;
        }

        StringBuilder jsonString = new StringBuilder();
        HttpURLConnection urlConnection;
        try {
            URL urlObject = new URL(API_URL);

            urlConnection = (HttpURLConnection) urlObject.openConnection();
            urlConnection.setRequestMethod(HttpMethod.GET.name());

            String inputLine = "";

            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
            while ((inputLine = bufferedReader.readLine()) != null) {
                jsonString.append(inputLine);
            }
            urlConnection.getInputStream().close();

        } catch (IOException ioe) {
            System.out.println(ioe.getMessage());
        }

        Countries[] countries = new Gson().fromJson(jsonString.toString(), Countries[].class);

        countryDetails = new CountryDetails[countries.length];
        for (int i = 0; i < countries.length; i++) {
            countryDetails[i] = new CountryDetails(
                    countries[i].getAlpha2Code(),
                    countries[i].getAlpha3Code(),
                    countries[i].getName()
            );
        }
        return countryDetails;
    }
}

Gunakan Tugas untuk mengambil negara Anda secara asinkron,

Task<CountryDetails[]> fetchCountryTask = new Task<CountryDetails[]>() {
    @Override
    protected CountryDetails[] call() throws Exception {
        return GetCountries.getDetails();
    }
};

fetchButton.setOnAction(event -> new Thread(fetchCountryTask).start());

fetchCountryTask.setOnRunning(event -> cbCountryList.setDisable(true));

fetchCountryTask.setOnSucceeded(e -> {
    cbCountryList.getItems().addAll(fetchCountryTask.getValue());
    cbCountryList.setDisable(false);
});
person Shekhar Rai    schedule 07.01.2020
comment
Sekhar Rai: Terima kasih untuk itu. StringConverter berhasil. Sedangkan untuk panggilan Asynchronous dan caching, Ini hanyalah bukti konsep dan pada akhirnya akan menjadi bagian dari wizard konfigurasi untuk aplikasi yang lebih besar. Daftar hanya akan dibuat satu kali saat wizard dimuat dan tidak akan dipanggil lagi. Namun, ini pasti akan berguna untuk bagian lain dari aplikasi yang perlu mengakses data Json serupa yang dikembalikan lebih sering dan dalam sesi yang sama. - person Yastafari; 08.01.2020