Всем привет!
Сегодня мы разберем получение товаров из категории Opencart в Android-приложение через JSON. Для начала не будем использовать сторонние библиотеки, а сделаем все нативным образом, чтобы ознакомиться с базовыми принципами обмена данными.
Итак, поехали
Сперва нужно определить выдачу массива товаров в JSON-объект из магазина.
Открываем catalog/controller/product/category.php
И в цикле выдачи данных для товаров добавляем свои запросы. Перед
$data['products'][] = array(
Добавляем
// изображение для списка, размер 100х100
if ($result['image']) {
$json_image = $this->model_tool_image->resize($result['image'], 100, 100);
} else {
$json_image = $this->model_tool_image->resize('placeholder.png', 100, 100);
}
// создаем массив данных для каждого товара
// получаем имя, путь изображения, описание и цену
$data['json-products'][] = array(
'name' => $result['name'],
'thumb' => $json_image,
'description' => utf8_substr(trim(strip_tags(html_entity_decode($result['description'], ENT_QUOTES, 'UTF-8'))), 0, $this->config->get('theme_' . $this->config->get('config_theme') . '_product_description_length')) . '..',
'price' => $price,
);
Дальше ищем response
$this->response->setOutput($this->load->view('product/category', $data));
И заменяем на
// формируем массив products
$data['json_products'] = array(
'products' => $data['json-products'],
);
// и отдаем его в json при запросе в адресной строке &json_products
// для этого поставим условие запроса
if (isset( $this->request->get['json_products'])) {
$this->response->setOutput(json_encode($data['json_products']));
} else {
$this->response->setOutput($this->load->view('product/category', $data));
}
Таким образом, теперь при запросе к сайту по адресу мой-сайт/index.php?route=product/category&path=20&json_products мы будем получать массив данных вида:
{"products":[
{"name":"Apple Cinema30'",
"thumb":"http:\/\/store.url\/image\/cache\/catalog\/demo\/apple_cinema_30-100x100.jpg",
"description":"The 30-inch Apple Cinema HD Display delivers an amazing 2560 x 1600 pixel resolution. Designed speci..",
"price":"$100.00"},
{"name":"Canon EOS 5D",
"thumb":"http:\/\/store.url\/image\/cache\/catalog\/demo\/canon_eos_5d_1-100x100.jpg",
"description":"Canon's press material for the EOS 5D states that it 'defines (a) new D-SLR category', while we're n..",
"price":"$100.00"},
. . . ,
. . . ,
]}
С серверной частью закончили, переходим к программной части приложения.
Что мы будем делать?
Получим JSON данные из url
Разберем эти данные и актуализируем с listView(textView)
Скачаем картинки, кешируем их в приложении и актуализируем по позициям в listView(imageView)
Для начала не забудьте дать приложению разрешение на использование сети (в манифесте):
<uses-permission android:name="android.permission.INTERNET"/>
Также в папку res/drawable мы поместим заглушку для изображений(до того, как они спарсятся)
blank.png (100x100px)
Теперь разметка.
Для простоты будем работать с activity_main и стандартным listView
activity_main.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ListView
android:id="@+id/lv_products"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:context=".MainActivity" />
</RelativeLayout>
Здесь все просто - в релятивную разметку мы поместили listView c id=lv_products
Теперь создадим кастомную разметку для этого listView
lv_layout.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="@+id/tv_product"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginTop="5dp"
android:textColor="#195F74"
android:textSize="20sp"
android:textStyle="bold" />
<ImageView
android:id="@+id/iv_thumb"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_below="@id/tv_product"
android:layout_centerVertical="true"
android:contentDescription="@string/str_iv_thumb"
android:padding="5dp"
android:scaleType="fitXY" />
<TextView
android:id="@+id/tv_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tv_product"
android:layout_toRightOf="@id/iv_thumb"
android:textSize="16sp" />
<TextView
android:id="@+id/tv_price"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/tv_description"
android:layout_marginBottom="10dp"
android:gravity="right"
android:layout_marginRight="5dp"
android:textColor="#cc3333"
android:textSize="20sp"
android:textStyle="bold"/>
</RelativeLayout>
С разметкой закончили — приступаем к коду
Создаем файл ProductsJSONParser.java (это и будет класс парсера)
package com.opencart.ocproducstlist;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.res.Resources;
/** класс парсера JSON данных */
public class ProductsJSONParser {
// Получаем JSONObject и возвращаем List
public List<HashMap<String, Object>> parse(JSONObject jObject) {
JSONArray jProducts = null;
try {
// Здесь элементы из массива 'products', который мы получаем из контроллера
jProducts = jObject.getJSONArray("products");
} catch (JSONException e) {
e.printStackTrace();
}
// применяем getProducts к массиву объекта JSON
// теперь каждый объект - это товар
return getProducts(jProducts);
}
private List<HashMap<String, Object>> getProducts(JSONArray jProducts) {
int productCount = jProducts.length();
List<HashMap<String, Object>> productList = new ArrayList<HashMap<String, Object>>();
HashMap<String, Object> product = null;
// разбираем товары по одному и добавляем к объекту List
for (int i = 0; i < productCount; i++) {
try {
// вызываем getProduct и парсим, добавляем
product = getProduct((JSONObject) jProducts.get(i));
productList.add(product);
} catch (JSONException e) {
e.printStackTrace();
}
}
return productList;
}
// Разбираем JSON-объект product
private HashMap<String, Object> getProduct(JSONObject jProduct) {
HashMap<String, Object> product = new HashMap<String, Object>();
String name = "";
String thumb = "";
String description = "";
String price = "";
try {
// обратите внимание на метод .replaceAll
// без него разные "непечатные" символы будут отображаться неправильно
name = jProduct.getString("name").replaceAll(""", "\"");
thumb = jProduct.getString("thumb");
description = jProduct.getString("description").replaceAll(""", "\"").replaceAll("’", "\'");
price = jProduct.getString("price");
product.put("product", name);
// здесь сперва ставим заглушку
product.put("thumb", R.drawable.blank);
product.put("thumb_path", thumb);
product.put("description", description);
product.put("price", price);
} catch (JSONException e) {
e.printStackTrace();
}
return product;
}
}
Ну а теперь MainActivity
package com.opencart.ocproducstlist;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import org.json.JSONObject;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.widget.ListView;
import android.widget.SimpleAdapter;
public class MainActivity extends Activity {
ListView mListView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// URL с нашими JSON-данными
String strUrl = "http://мойсайт.com/index.php?route=product/category&path=20&json_products";
// определяем задачу по загрузке
// и запускаем ее с нашим url
DownloadTask downloadTask = new DownloadTask();
downloadTask.execute(strUrl);
// ссылаемся на ListView в activity_main
mListView = (ListView) findViewById(R.id.lv_products);
}
/** метод загрузки данных из url */
private String downloadUrl(String strUrl) throws IOException {
String data = "";
InputStream iStream = null;
try {
URL url = new URL(strUrl);
// Создаем http соединение, соединяемся и считываем данные
HttpURLConnection urlConnection = (HttpURLConnection) url
.openConnection();
urlConnection.connect();
iStream = urlConnection.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(
iStream));
StringBuffer sb = new StringBuffer();
String line = "";
while ((line = br.readLine()) != null) {
sb.append(line);
}
data = sb.toString();
br.close();
} catch (Exception e) {
Log.d("Exception while downloading url", e.toString());
} finally {
iStream.close();
}
return data;
}
/** Асинхронно скачиваем json */
private class DownloadTask extends AsyncTask<String, Integer, String> {
String data = null;
@Override
protected String doInBackground(String... url) {
try {
data = downloadUrl(url[0]);
} catch (Exception e) {
Log.d("Background Task", e.toString());
}
return data;
}
@Override
protected void onPostExecute(String result) {
// закончили в non-ui
ListViewLoaderTask listViewLoaderTask = new ListViewLoaderTask();
// начинаем парсить
listViewLoaderTask.execute(result);
}
}
/** Асинхронно парсим данные и кидаем в listView */
private class ListViewLoaderTask extends
AsyncTask<String, Void, SimpleAdapter> {
JSONObject jObject;
// парсим в non-ui
@Override
protected SimpleAdapter doInBackground(String... strJson) {
try {
jObject = new JSONObject(strJson[0]);
ProductsJSONParser productsJsonParser = new ProductsJSONParser();
productsJsonParser.parse(jObject);
} catch (Exception e) {
Log.d("JSON Exception1", e.toString());
}
// Инстанцируем класс парсера
ProductsJSONParser productsJsonParser = new ProductsJSONParser();
// Список для сохранения
List<HashMap<String, Object>> products = null;
try {
// Получаем спарсеные данные в List (наш список)
products = productsJsonParser.parse(jObject);
} catch (Exception e) {
Log.d("Exception", e.toString());
}
// Ключи, которые используем в hashMap
String[] from = { "product", "thumb", "description", "price" };
// и айдишники, используемые в listView
int[] to = { R.id.tv_product, R.id.iv_thumb, R.id.tv_description, R.id.tv_price };
// задаем адаптер
// и закидываем ключи в айдишники
SimpleAdapter adapter = new SimpleAdapter(getBaseContext(),
products, R.layout.lv_layout, from, to);
return adapter;
}
/** doInBackground выполнен - займемся картинками */
@Override
protected void onPostExecute(SimpleAdapter adapter) {
// Задаем адаптер для listview
mListView.setAdapter(adapter);
for (int i = 0; i < adapter.getCount(); i++) {
HashMap<String, Object> hm = (HashMap<String, Object>) adapter
.getItem(i);
String imgUrl = (String) hm.get("thumb_path");
ImageLoaderTask imageLoaderTask = new ImageLoaderTask();
HashMap<String, Object> hmDownload = new HashMap<String, Object>();
hm.put("thumb_path", imgUrl);
hm.put("position", i);
// запускаем ImageLoaderTask для скачивания
// и актуализации картинок в listview
imageLoaderTask.execute(hm);
}
}
}
/** Асинхронно качаем картинки и помещаем в listView */
private class ImageLoaderTask extends
AsyncTask<HashMap<String, Object>, Void, HashMap<String, Object>> {
@Override
protected HashMap<String, Object> doInBackground(
HashMap<String, Object>... hm) {
InputStream iStream = null;
String imgUrl = (String) hm[0].get("thumb_path");
int position = (Integer) hm[0].get("position");
URL url;
try {
url = new URL(imgUrl);
// создаем соединение и подключаемся
HttpURLConnection urlConnection = (HttpURLConnection) url
.openConnection();
urlConnection.connect();
// считываем данные
iStream = urlConnection.getInputStream();
// директория кеширования
File cacheDirectory = getBaseContext().getCacheDir();
// временно сохраняем картинку в кеш-дир
File tmpFile = new File(cacheDirectory.getPath() + "/ocpl_"
+ position + ".png");
// поток в кеш-файл
FileOutputStream fOutStream = new FileOutputStream(tmpFile);
// из потока в картинку
Bitmap b = BitmapFactory.decodeStream(iStream);
// пишем файл в темп (png)
b.compress(Bitmap.CompressFormat.PNG, 100, fOutStream);
// сбрасываем и закрываем поток
fOutStream.flush();
fOutStream.close();
// создаем hashMap для передачи картинки
// в listview, в соответствии с позицией
HashMap<String, Object> hmBitmap = new HashMap<String, Object>();
// сохраняем путь к картинке
// и позицию картинки в listview
hmBitmap.put("thumb", tmpFile.getPath());
hmBitmap.put("position", position);
// возвращаем объект с картинкой и позицией
return hmBitmap;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(HashMap<String, Object> result) {
// теперь получаем путь и позицию
String path = (String) result.get("thumb");
int position = (Integer) result.get("position");
// задаем адаптер
SimpleAdapter adapter = (SimpleAdapter) mListView.getAdapter();
// забираем объекты из hashMap
// с соответствующей позицией в listview
HashMap<String, Object> hm = (HashMap<String, Object>) adapter
.getItem(position);
// заменяем текущий путь (сейчас "заглушка" - res/drawable/blank.png)
hm.put("thumb", path);
// и сообщаем listView об изенении содержимого
adapter.notifyDataSetChanged();
}
}
}
Запускаем приложение и любуемся результатом:
До новых встреч