javafx ComboBox พร้อม Cellfactory ไม่แสดงรายการที่เลือก

ฉันมีแอปพิสูจน์แนวคิดเล็กน้อยซึ่งประกอบด้วย 6 Labels a ComboBox และ Button ซึ่งทั้งหมดสร้างขึ้นโดยใช้ SceneBuilder

เมื่อคลิก Button แอปจะทำการเรียก Json api เพื่อส่งคืนรายชื่อประเทศและรายละเอียดที่เกี่ยวข้อง (apla2code, apla3code, ชื่อ ฯลฯ) ฉันสร้างวัตถุ CountryDetails ซึ่งมี 3 String องค์ประกอบ ฉันใช้สิ่งนั้นเพื่อส่งคืนอาร์เรย์ของ CountryDetails ซึ่งฉันโหลดลงในอาร์เรย์ของ ObserbavleList จากนั้นฉันใช้สิ่งนั้นกับ ComboBox และฉันโหลดองค์ประกอบ CountryDetails ลงใน 3 ป้ายกำกับแต่ละครั้งที่มีการเลือกรายการใน ComboBox ทั้งหมดนี้ใช้งานได้ดี (แม้ว่าอาจมีวิธีที่ดีกว่านี้มากก็ตาม)

ปัญหาที่ฉันพบคือ ComboBox ไม่แสดงรายการที่เลือก และฉันไม่สามารถหาวิธีแก้ไขปัญหานี้ได้ ภาพด้านล่างแสดงให้เห็นว่าปัญหาคืออะไร

รหัสที่ทำให้การเรียก api เป็นดังนี้:

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

รหัสสำหรับวัตถุ CountryDetails เป็นดังนี้:

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

รหัสที่โหลด ObservableList เป็นดังนี้:

GetCountries countries = new GetCountries();

        CountryDetails[] countryDetails = countries.getDetails();

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

รหัสที่โหลด ComboBox และแสดงองค์ประกอบใน Labels มีดังนี้:

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

ด้านล่างเป็นภาพของแอป:

ป้อนคำอธิบายรูปภาพที่นี่


person Yastafari    schedule 07.01.2020    source แหล่งที่มา
comment
ตั้งค่า ComboBox#buttonCell เป็นอินสแตนซ์ ส่งกลับโดยโรงงานเซลล์ที่คุณกำหนดเอง   -  person Slaw    schedule 07.01.2020


คำตอบ (1)


ปัญหาที่ฉันมีคือ ComboBox ไม่แสดงรายการที่เลือก และฉันไม่สามารถทราบวิธีแก้ไขปัญหานี้ได้

คุณต้องตั้งค่า StringConverter สำหรับ cbCountryList.

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

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

ทั้งหมดนี้ใช้งานได้ดี (แม้ว่าอาจมีวิธีที่ดีกว่านี้มากก็ตาม)

คุณอาจพิจารณาอัปเดตสิ่งต่อไปนี้

  • การเรียกแบบอะซิงโครนัสสำหรับคำขอ HTTP และรายการโหลด
  • คุณสามารถแคชรายการ fetched-country ของคุณในขณะที่คุณกำลังโทรทุกครั้งที่ปุ่มถูกทริกเกอร์ คุณสามารถสร้างออบเจ็กต์ Singleton สำหรับ GetCountries

แก้ไข GetCountries คลาส,

มันแคชรายชื่อประเทศและใช้ข้อมูลที่แคชไว้สำหรับคำขอหลายรายการ

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

ใช้ งาน เพื่อดึงข้อมูลประเทศของคุณแบบอะซิงโครนัส

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: ขอบคุณสำหรับสิ่งนั้น StringConverter ใช้งานได้ดี สำหรับการเรียกแบบอะซิงโครนัสและการแคช นี่เป็นเพียงการพิสูจน์แนวคิดและท้ายที่สุดจะเป็นส่วนหนึ่งของวิซาร์ดการกำหนดค่าสำหรับแอปพลิเคชันขนาดใหญ่ รายการจะถูกสร้างขึ้นเพียงครั้งเดียวเมื่อตัวช่วยสร้างโหลด และจะไม่ถูกเรียกอีก อย่างไรก็ตาม สิ่งนี้จะมีประโยชน์อย่างแน่นอนสำหรับส่วนอื่นๆ ของแอปพลิเคชันซึ่งจะต้องเข้าถึงข้อมูล Json ที่ส่งคืนที่คล้ายกันบ่อยกว่าและในเซสชันเดียวกัน - person Yastafari; 08.01.2020