Jump to content

Opencart - Android - JSON - получаем список товаров в приложении

Всем привет!

 

Сегодня мы разберем получение товаров из категории 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();
        }
    }
}

Запускаем приложение и любуемся результатом:

nexus.thumb.jpg.364147d8815428c5240f877d26904e83.jpg

 

До новых встреч :)

 

  • +1 5


12 Comments


Recommended Comments

берём хром
перерабатываем под себя - меняем id, иконку запихиваем туда все стили сайта и т.д. всё что можно, при изменении подкачиваем нужное.остальное как у арбуза,  вырезаем лишняк.
Сё  -приложение вашего сайта в гуглплей и осёлплей.
 

Share this comment


Link to comment

@AWARO , это называется WebView. Не рекомендуется к использованию в таком ключе.

К примеру, если свернуть и развернуть такое приложение, получим загрузку главной страницы. А не текущую activity.

То же самое произойдет и при обычной смене ориентации экрана. Непрофессионально ;)

Не говоря о кэшах, SharedPreferences и т.п.

 

Но в целом да. Можно сделать в 5 строк :-D

Edited by ocdroid

Share this comment


Link to comment

@AWARO я об этом и говорю. Но это неверный подход.

Ведь в опенкарте тоже можно запросы к базе прям из файла шаблона делать. Вы так делаете?

Надеюсь, нет. :-D Но можно ведь!

Share this comment


Link to comment

@ocdroid а браузер как это делает? точно так же, только втыкаем ему насильно постоянно обращение к одному сайту без адресной строки
и всё.
 

Share this comment


Link to comment

@ocdroid речь не идет об встраивании сайта в какое нить приложение, речь идет о браузере для одного сайта
 

Share this comment


Link to comment

@AWARO Советую попробовать сделать хоть одно такое "приложение". Тогда и обсудим ;)

А то всякие алиэкспрессы и амазоны то и не знали.. наделали приложений нативных, а можно было просто браузер перекрасить :)

Напоминает новый скайп. Стабильный, правда? Зачем С, если можно по быстрому на Electron склепать?

  • +1 1

Share this comment


Link to comment

Нет, они всё на свете знают, откуда нам бедалагам мозги)

Share this comment


Link to comment

Поддержу, тема интересная.

Единственное Открываем catalog/controller/product/category.php мне кажется лучше отдельный контроллер для этого запилить, а то получается много лишнего обрабатывается, а фактически на вывод ерунда нужна.

Share this comment


Link to comment

@pimur Полностью согласен. К полноценному API мы еще подойдем. Сейчас скорее показан сам принцип взаимодействия.

  • +1 1

Share this comment


Link to comment

Да ни кто не спорит, норм тема.
но судя по тому что 90% это ( 500р? блин а че так дорого вот симпла и та по 450р  и бегут качать на варезе)
Я предложил выше дешманский вариант, да и не у всех магазы под копирку деланы, много всякого доп. чем не дыра с баблом?
а там разбогатеют придут просить запилить им норм приложуху под пыхтелки каждого.
Мало подводных камне разве, .

вот чувак делал,

http://priparade.ru/android.apk

 

вот ещё
https://play.google.com/store/apps/details?id=com.ocx.androidstore&amp;hl=ru

 

https://www.opencart.com/index.php?extension_id=25090&amp;route=marketplace/extension/info

 

 

 

Share this comment


Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
You are posting as a guest. If you have an account, please sign in.
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Recently Browsing   0 members

    No registered users viewing this page.

×

Important Information

On our site, cookies are used and personal data is processed to improve the user interface. To find out what and what personal data we are processing, please go to the link. If you click "I agree," it means that you understand and accept all the conditions specified in this Privacy Notice.