Отображение данных из JSON в ListView(курс валют) - Android

355
23 января 2018, 11:53

Почему-то не отображаются актуальные данные из JSON файла которые тяну с сайта RBC. Создал класс в MainActivity:

 private class GetRbcExchangeRates extends AsyncTask<String, String, String> {
    public ArrayList<CurrencyRateModel> listOfCurrency = new ArrayList<>();
    private final String httpAddress = "https://www.cbr-xml-daily.ru/daily_json.js";
    @Override
    protected String doInBackground(String... params) {
        HttpURLConnection connection = null;
        BufferedReader bufferedReader = null;
        StringBuilder jSonResult = new StringBuilder();
        //listOfCurrency = new ArrayList<>();
        try {
            URL url = new URL(httpAddress);
            connection = (HttpURLConnection)url.openConnection();
            connection.connect();
            bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            String line = "";
            while((line = bufferedReader.readLine()) != null) {
                jSonResult.append(line);
            }

        }
        catch(Exception e) {
            e.printStackTrace();
            publishProgress("MESSAGE", "Отсутствует подключение к интернет!");
        }
        finally {
            if(connection != null)
                connection.disconnect();
            try {
                if(bufferedReader !=null)
                    bufferedReader.close();
            }
            catch(IOException e) {
                e.printStackTrace();
            }
        }
        try {
            JSONObject rbcGETRates = new JSONObject(jSonResult.toString());
            JSONObject jSonValute = rbcGETRates.getJSONObject("Valute");
            Iterator<String> arrayKey = jSonValute.keys();
            while(arrayKey.hasNext()) {
                String key = arrayKey.next();
                JSONObject jSonItem = jSonValute.getJSONObject(key);
                CurrencyRateModel currencyitems = new CurrencyRateModel();
                currencyitems.id = jSonItem.getString("ID");
                currencyitems.nameCode = jSonItem.getString("NumCode");
                currencyitems.charCode = jSonItem.getString("CharCode");
                currencyitems.nominal = jSonItem.getInt("Nominal");
                currencyitems.name = jSonItem.getString("Name");
                currencyitems.value = jSonItem.getDouble("Value");
                currencyitems.previous = jSonItem.getDouble("Previous");
                listOfCurrency.add(currencyitems);
            }
        }
        catch (JSONException e) {
            e.printStackTrace();
        }
        return null;
    }
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        ListView lsView = (ListView)findViewById(R.id.currencyView);
        MyAdapter myAdapter = new MyAdapter(MainActivity.this, listOfCurrency);
        lsView.setAdapter(myAdapter);
    }
    @Override
    protected void onProgressUpdate(String... values) {
        super.onProgressUpdate(values);
        if(values[0].equals("MESSAGE")) {
            Toast.makeText(MainActivity.this, values[1], Toast.LENGTH_LONG).show();
        }
    }
    @Override
    protected void onPostExecute(String s) {
        super.onPostExecute(s);
    }
}

Класс - Модель с полями:

public class CurrencyRateModel {
public String id;
public String nameCode;
public String charCode;
public int nominal;
public String name;
double value;
double previous;

}

И адаптер:

public class MyAdapter extends BaseAdapter {
private Context ctx;
private ArrayList<CurrencyRateModel> arrayList;
private LayoutInflater inflater;
public MyAdapter(Context ctx, ArrayList<CurrencyRateModel> arrayList) {
    this.ctx = ctx;
    this.arrayList = arrayList;
    inflater = ((Activity)ctx).getLayoutInflater();
}
@Override
public int getCount() {
    return arrayList.size();
}
@Override
public Object getItem(int position) {
    return null;
}
@Override
public long getItemId(int position) {
    return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {

    ViewHolder vh;
    if(convertView == null) {
        vh = new ViewHolder();
        convertView = inflater.inflate(R.layout.itemcurrency, null);
        vh.tvTitle = (TextView)convertView.findViewById(R.id.title);
        vh.tvDescription = (TextView)convertView.findViewById(R.id.description);
        convertView.setTag(vh);
    }
    else {
        vh = (ViewHolder) convertView.getTag();
        vh.tvTitle.setText("1");
        vh.tvDescription.setText("2");
    }
    return convertView;
}
static class ViewHolder {
    TextView tvTitle, tvDescription;
}

}

В методе onCreate делаю так:

 @Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    // async TASK Run
    GetRbcExchangeRates async = new GetRbcExchangeRates();
    async.execute();
}

Так же создан отдельный ресурс файл c 2 TextView:

 <TextView
    android:id="@+id/title"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textSize="22sp"/>
<TextView
    android:id="@+id/description"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textSize="18sp"
    android:textColor="#555"/>

Отладку произвожу на планшете с Lollipop 5.1. При запуске пустота, если свернешь приложение, а после снова разворачиваешь, в ListView записываются цифры 1 и 2 (что в адаптере в блоке else); При отладке видно, что в структуру данных записываются валидные распарсенные данные из сайта, какая то проблема в отображении данных. Я вывел в лог:

Что я делаю не так. Буду благодарен за любую помощь, так как всего пару недель щупаю эту платформу.

Answer 1

Код по обработке данных (вывод на экран), полученных в асинхронном потоке, должен выполнятся в методе onPostExecute() - выполнить после завршения потока, то есть, когда данные сформированы.

В адаптере вместо

vh.tvTitle.setText("1");

логично предположить, что нужно выводить данные для этой позиции, а не хардкорный текст - 1.

 vh.tvTitle.setText(arrayList.get(position).name);

целиком метод getView()(не знаю, что нужно выводить в description, пусть будет значение value) + комментарии, что именно там происходит и исправленное условие (закрывающая скобка не в том месте была):

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder vh;
    if(convertView == null) {
        // создаем объект-холдер для кэширования ссылок на виджеты
        vh = new ViewHolder();
        // получаем объект айтема списка, который парсим из xml-разметки
        convertView = inflater.inflate(R.layout.itemcurrency, null);
        // получаем ссылки на виджеты в айтеме
        vh.tvTitle = (TextView)convertView.findViewById(R.id.title);
        vh.tvDescription = (TextView)convertView.findViewById(R.id.description);
        // сохраняем холдер для переиспользования
        convertView.setTag(vh);
    }
    else {
        // получаем холдер с кэшированными ссылками на виджеты
        vh = (ViewHolder) convertView.getTag();
    }
    // в виджеты айтема выводим данные согласно текущей позиции в списке
    vh.tvTitle.setText(arrayList.get(position).name);
    vh.tvDescription.setText(Double.toString(arrayList.get(position).value));
    return convertView;
}

Числовой тип должен быть преобразован в String перед передачей в метод setText().

Подробнее о устройстве адаптера с холдером

Метод getItem() не должен возвращать null, он должен возвращать содержимое айтема (данные для него):

@Override
public CurrencyRateModel getItem(int position) {
   return arrayList.get(position);
}

Так же в модели данных стоит подумать о геттерах/сеттерах, в Java это считается хорошим тоном.

READ ALSO
Вопрос про валидность данных

Вопрос про валидность данных

Странный вопрос, но в голову больше ничего не лезетЧто ещё можно сделать если id меньше 0 или имя фамилия пустые?

345
Перебрать элементы Deque/Queue

Перебрать элементы Deque/Queue

Привет всем! Как можно перебрать все элементы Deque и Queue как в массиве?

237
AsynkTask. Как завершить преждевременно. Break?

AsynkTask. Как завершить преждевременно. Break?

ЭкспериментируюЗапустил Активити, а через него перехожу на второе активити, в нем запускаю AsyncTask

276
Стоит ли учить RxJava и Java 8 если я перешел на Kotlin?

Стоит ли учить RxJava и Java 8 если я перешел на Kotlin?

Вопрос такойЯ Android разработчик, пишу на котлине

270