Přejít k navigační liště

Zdroják » Mobilní vývoj » AQuery – šikovná knihovna pro Android

AQuery – šikovná knihovna pro Android

V dnešním článku si představíme knihovnu Android Query, která se po vzoru jQuery snaží zjednodušit některé úkoly na Androidu. Podíváme se na její koncepci a pak si naprogramujeme prohlížeč obrázků z Flickru.

Android Query (dále AQuery) je šikovná knihovna pro Android. Jak lze vidět už z názvu, AQuery se inspirovalo jQuery. Snaží se mít podobnou úspornou syntaxi a přináší do Androidu jednoduché a přímočaré „ajax“ requesty (vím, že je to termín nepřesný, přesto ho však budu používat, neboť to tak dělá i knihovna). A to samozřejmě není všechno.

Jak AQuery přidat

V Eclipse (jinými prostředími se zabývat nebudu) jako jakoukoli jinou knihovnu. Nejprve si stáhněte příslušný .jar soubor ze stránek AQuery (v tuto chvíli je to soubor android-query-0.21.7.jar) a uložte ho do složky libs ve vašem projektu (pokud složka libs neexistuje, vytvořte ji). Potom klikněte pravým tlačítkem na váš projekt v Eclipse, zvolte Build Path → Configure Build Path..., v okně, které vyskočilo, vyberte záložku Libraries, klikněte na Add JARs..., proklikejte se ke složce libs v příslušném projektu, vyberte .jar soubor AQuery, stiskněte OK a pak ještě jednou OK.

Základní koncepce

Nejdůležitější třídou v AQuery je (překvapivě) AQuery, která umí manipulovat s View objekty, provádět ajax požadavky i stahovat obrázky z internetu do nějakého ImageView . Odteď bude AQuery znamenat knihovnu, zatímco AQuery bude značit třídu AQuery nebo její instanci.

Abychom pochopili, jak AQuery funguje, musíme se na chvilku podívat dovnitř. AQuery má čtyři konstruktory, každý z nich se hodí v jiné situaci:

AQuery aq;
aq = new AQuery(Activity act); // Uvnitř Activity
aq = new AQuery(Activity act, View root); // Uvnitř Fragmentu
aq = new AQuery(Context context); // Například v BroadcastReceiveru, lze použít pouze ajax
aq = new AQuery(View root); // Třeba v nějakém Adapteru

Aby mohlo AQuery používat internet, musí být v manifestu  <uses-permission android:name="android.permission.INTERNET" />.

AQuery si také udržuje odkazy na dvě View, a to View root, což je View předané v konstruktoru (případně null) a aktivní View view  – to View, na němž se budou provádět všechny operace. Předáme-li nějaké View konstruktoru, hned ze začátku bude i aktivní. Pokud ne (tzn. konstruktor AQuery(Activity act), musíme nějaké vybrat, například pomocí metody AQuery.id(int id) (detailněji níže). AQuery si udržuje odkaz na aktivní View do té doby, než vybereme jiné.

Samotné AQuery je velmi levné vytvořit a nezabere prakticky žádné místo, ale udržuje si odkazy na poměrně veliké objekty. Proto pokud chcete předejít problémům s pamětí, neuchovávejte AQuery globálně (jako nějakou static složku)!

AQuery se občas nechová příliš javovsky. Jedním z takových případů (ale tady si myslím, že je to šikovné) je volání metod na AQuery. Zavolám-li například aq.text("Text") a aktivní View žádný text zobrazit neumí, AQuery takové volání ignoruje. Ignoruje ho dokonce i tehdy, je-li aktivní View null. To hodně zkrátí kód, neboť člověk nemusí ošetřovat, zda View, s nímž pracuje, vlastně existuje. Ale zase na druhou stranu to přidá trochu práce, když člověk potřebuje, aby program zahlásil chybu v případě, že požadované View neexistuje. (Na stránkách AQuery tvrdí, že je to výborné pro tabletové layouty, ale dle mého je to špatný přístup a takové layouty by se měly řešit pomocí Fragmentů , které spolu komunikují skrze vlastní API, viz můj předchozí článek na Zdrojáku.)

// Z takového kódu
TextView nameView = (TextView) view.findViewById(R.id.name);
if (nameView != null) {
    nameView.setText(content.getPname());
}
// se s AQuery stane takovýto.
aq.id(R.id.name).text(content.getPname());
// (převzato ze stránek AQuery)

A jak tedy AQuery používat?

Využití si rozdělíme na čtyři skupiny: základ, ajax, stahování obrázků a ostatní.

Základ

API AQuery je poměrně malé a názvy metod jsou ve většině případů samovysvětlující. (Asi jste si všimli, že odkazuji na Javadoc třídy AbstractAQuery. Samotná třída AQuery od ní dědí a nic vlastního nepřidává.)

Jak AQuery objekt vytvoříme, jsme si řekli už dříve. Pokud jsme mu předali nějaké View, můžeme metody, jež s ním pracují, používat ihned. Pokud ne, anebo pokud chceme manipulovat s jiným než kořenovým View, musíme ho najít. K tomu slouží metody id a find. Metoda find(int id) jako argument přebírá id požadovaného View a vrací nový AQuery objekt s oním View nastaveným jako root (a i jako aktivní).

Metoda id má dvě varianty. Poněkud nelogická id(View view), jež nastaví view jako aktivní a potom id(int... id), která se postupně prochází předané pole id a vždy na předtím nalezeném View zavolá findViewById a poslední nalezené nastaví jako aktivní. Je to tedy obdoba jQuery CSS selektoru. Nenapadá mě, kdy bych předal více než jedno id. Na rozdíl od find upraví id aktuální instanci. Většinou to není problém, pokud člověk nepotřebuje pracovat s více View  naráz.

Jak metoda id, tak metoda find volají findViewById na View root popřípadě v Activity act, nikoli na View view (aktivní)!

Když máme AQuery s nastaveným aktivním View, můžeme s ním provádět různé věci. Můžeme mu nastavovat některé běžné vlastnosti, posluchače událostí atd. Syntaxe je poměrně intuitivní a podobná jQuery (v tom, že nastavovací metody nemají prefix set  – gettery prefix get však mají, neboť settery podporují tzv. chaining, což znamená, že vrací this  – AQuery objekt –, aby se mohla rovnou zavolat jiná metoda, a v Javě není možné, aby se lišil návratový typ metody v závislosti na počtu nebo typu argumentů). K dispozici jsou například metody adapter pro nastavení Adapteru nějakému ListView nebo  GridView , backgroundColor pro nastavení barvy pozadí, clear které u TextView odstraní text, u ImageView odstraní obrázek a u  WebView   „otevře prázdnou stránku“ a dále třeba metody enabled, gone, height, margin, text, textColor, visible nebo width, jejichž názvy mluví za vše. Pro popis parametrů a další metody se podívejte na Javadoc třídy AbstractAQuery . A dovolím si znovu upozornit na to, že nepodporuje-li aktivní View (nebo není-li žádné) volanou metodu, v tichosti je ignorována.

Trochu zvláštně pojmenovaná, ale užitečná je metoda isExist, která zjistí, existuje-li aktivní  View.

Neumí-li AQuery nějakou operaci s aktuálním View, lze to View získat. Buď pomocí metody getView, která vrátí aktuální View nebo null, není-li žádné, anebo (šikovněji) pomocí metod typu getEditText, getListView, getSpinner atd. (pro kompletní seznam viz dokumentaci). Vězte však, že zatímco jinde AQuery možná až nevhodně zamlčuje problémy, u těchto metod jednoduše hloupě vezme aktuální View a pokusí se ho přetypovat na instanci požadované třídy. Nelze-li přetypovat anebo je aktuální View null, skončí to výjimkou. (A zrovna tady si myslím, že vrátit null by bylo šikovnější.)

Poslední základní manipulací, o které jsme ještě nemluvili, je práce s událostmi. AQuery má metody pro většinu nejčastěji používaných událostí. Metody mají vždy tvar minulého času (koncovka -ed) a každá má dvě varianty: fooed(View.OnFooListener listener) a fooed(Object handler, String method). První varianta je klasicky androidí, druhá se mi osvědčila jako velmi šikovná – jako listener se zavolá metoda method objektu handler, která přebírá stejné argumenty jako posluchačská metoda ve speciálním posluchačském rozhraní ( View.OnFooListener). Problém je ale ten, že se název metody předává jako řetězec, a IDE tudíž může tvrdit, že daná metoda je nevyužitá. U větších projektů bych se tomu z tohoto důvodu vyhl. Nepodporuje-li aktuální View danou událost, jíž chceme přiřadit posluchače, vše se samozřejmě v tichosti ignoruje.

AQuery aq = new AQuery(this); // Uvnitř nějaké Activity
// Následující dvě přiřazení posluchače jsou ekvivalentní.
aq.id(R.id.button).clicked(new View.OnClickListener() {
    public void onClick(View v) {
        // ...
    }
});
aq.id(R.id.button).clicked(this, "onButtonClicked");

// ...

public void onButtonClicked(View v) {
    // ...
}

Ajax

Ajax v AQuery neuvěřitelně zjednoduší stahování dat z internetu a umožní soustředit se jen na samotné zpracování dat. A má jednu záludnost, o níž si řekneme později.

Než se ale vrhneme o ajaxu, představíme si jinou šikovnou metodu, která se však ajaxu týká, a to metodu progress. Tato metoda (musí být zavolána před voláním ajaxu) přijímá tři typy argumentů. progress(Dialog dialog) při startu ajaxu zobrazí a po skončení zase skryje předaný Dialog (většinou asi ProgressDialog ). progress(View view) respektive progress(int id) zobrazí a po skončení skryjí určité View (většinou asi ProgressBar ). O pro mě důležitou variantu, a to vestavěný progressbar v titulkové liště jsem si zažádal a bude v příští verzi (mimochodem, rychlost reakce ukazuje na to, že AQuery je stále vyvíjené, podporované a má aktivní vývojáře).

Možnosti ajaxu v AQuery jsou poměrně rozsáhlé, že by vydaly na vlastní článek, a my si tudíž ukážeme jen něco. Velmi obšírně je vše napsané v dokumentaci ajaxu na stránkáh AQuery.

Metodu ajax můžeme volat na jakékoli instanci AQuery za předpokladu, že má aplikace permission android.permission.INTERNET. Předáme-li konstruktoru AQuery nějakou Activity , která bude při dokončení ajaxu už stopnutá, callback se nezavolá, aby se předešlo chybám (jako například zobrazení dialogu v neaktivní Activity). Je-li to nechtěné chování, musíme vytvořit AQuery objekt, kterému předáme Context ( new AQuery(getApplicationContext())). A jak metoda ajax vypadá? Jako první argument přebírá url, jako druhý třídu výsledku, aby věděla, jak odpověď serveru zpracovat a jako třetí přebírá callback. A jak je u metod nastavujících callback v AQuery zvykem, lze jako callback předat jak callbackový objekt, tak libovolný objekt a název jeho metody, která se jako callback zavolá.

String url = //...
q.ajax(url, JSONObject.class, new AjaxCallback<JSONObject>() {

    @Override
    public void callback(String url, JSONObject json, AjaxStatus status) {

        if (json != null) {
            // Úspěch
        } else {
            // Chyba
            int errorCode = status.getCode();
        }
    }
});

q.ajax(url, JSONObject.class, this, "onDataFetched");

//...

public void onDataFetched(String url, JSONObject json, AjaxStatus status) {
    if (json != null) {
        // Úspěch
    } else {
        // Chyba
        int errorCode = status.getCode();
    }
}

Tato dvě volání ajaxu jsou ekvivalentní.

Místo JSONObject.class lze předat například String.class pro surový řetězec navrácených dat, XmlDom.class a mnoho dalšího.

Cachováním, POST požadavky, stahováním souborů, vlastními hlavičkami, cookies ani ničím dalším se tu zabývat nebudeme a já vás místo toho odkážu na dokumentaci. Ajax v AQuery toho každopádně ale umí hodně a v mnoha situacích dokáže být velmi šikovným pomocníkem.

Všechno má své ale…

…a to má i ajax. Ale možná není přesný výraz, třeba je to tak správně, ale mně se to rozhodně nelíbí a přijde mi to neintuitivní. Řeč je o zachytávání chyb v ajaxových callbacích. AQuery zastává názor, že v různých listenerech může nastat nečekaná běhová chyba (což je pravda) a že uživatel by o tom neměl vědět (s čímž já nesouhlasím), a tak všechny nezachycené chyby, které se vyskytnou v ajaxových callbacích i event listenerech, zachytává. Přesněji řečeno, zachytává je tehdy, předali-li jsme callback, resp. listener jako Object handler, String method. A jediné, co my s tím můžeme udělat, je zavolat metodu AQUtility.setExceptionHandler(UncaughtExceptionHandler handler) a vytvořit funkci, která se při chybě zavolá. Popsané to je v dokumentaci zachytávání chyb. Pokud použijeme callbackovou třídu, je všechno v pořádku.

Na tuhle vlastnost každopádně dávejte obrovský pozor, komplikuje to debugování. Já například řešil, proč se mi nespouští ShowPhotosActivity po kliknutí na tlačítko a stažení JSON souboru. Pořád jsem zjišťoval, proč se JSON nestáhne nebo proč obslužná funkce končí v polovině a teprve pak jsem zjistil, že jsem zase zapomněl přidat ShowPhotosActivity do manifestu. Určitě zavolejte funkci AQUtility.setDebug(true), díky které se chyba alespoň vypíše do LogCatu, když už tedy nezabije aplikaci. Ale nezapomeňte řádek s tímto příkazem před zabalením aplikace a rozšířením mezi uživatele odstranit.

Stahování obrázků

Potřebujete do ImageView umístit obrázek z internetu? Nebo snad potřebujete zobrazit obrázek tak, aby šel přibližovat? Tohle a mnohem více AQuery vyřeší. Navíc k tomu nabídne šikovnou podporu zobrazení progressbaru, cachování, změnu velikosti obrázku, fallback obrázek, callback po stažení obrázku a ještě více. Opět se budeme věnovat jen tomu základnímu a pro větší detaily vás odkážu na dokumentaci.

Stažení obrázku do ImageView

Není nic jednoduššího.

aq.id(R.id.imageView).progress(R.id.progressBar).image("https://www.google.com/images/srpr/logo3w.png");

Funkce image samozřejmě přijímá i další argumenty, viz dokumentace. Předat ji lze i objekt Bitmap, Drawable nebo File a tehdy nic nestahuje a použije lokální obrázek.

Chci stáhnout obrázek a aby se dal zvětšovat a zmenšovat.

Není problém, jen místo ImageView potřebujeme prázdné WebView

aq.id(R.id.webView).progress(R.id.progressBar).webImage("https://www.google.com/images/srpr/logo3w.png");

Metoda webImage nemá zdaleka tolik možností jako image, kromě url lze nastavit už jen, bude-li obrázek zoomovatelný, jestli bude zobrazeno zoomovací ovládání a barva pozadí.

Ostatní

Kromě výše zmíněných věcí má AQuery plno dalších šikovných (maličkostí), které programátorovi usnadní žívot.

Nastavení dostupná až ve vyšších API

Některé volby jsou dostupné až od vyšších API. Například hardwarová akcelerace lze zapnout až od API 11. Místo toho, aby člověk musel zjišťovat verzi API a podle toho volat nebo nevolat funkci, nabízí AQuery zkratku. V případě hardwarové akcelerace se zkratka jmenuje hardwareAccelerated11(). Metody mají vždy na konci číslo značící, od kterého API fungují. Je-li API nižší, nestane se nic. Dalšími takovými metodami jsou třeba overridePendingTransition5(int enterAnim, int exitAnim) nebo setOverScrollMode9(int mode). Všechny najdete v Javadocu třídy  AbstractAQuery .

Kontrola nové verze na Marketu Play Store

AQuery tvrdí, že umí zkontrolovat, je-li na Play Store dostupná nová verze aplikace, a pokud ano, zobrazí uživateli dialog nabízející update (podporují plno jazyků včetně češtiny). Mně se to zatím nepodařilo rozchodit. Ale jejich server, na němž služba běží, vrací správné výsledky, takže tam problém nebude.

Autentizace

AQuery zjednoduší autentizaci přes Facebook, Twitter a snad i Google. Nikdy jsem to nepoužil a ani moc nestudoval, jen odkážu na dokumentaci.

A jdeme programovat!

Vytvoříme si jednoduchý „zobrazovač obrázků z Flickru“ (budeme mu říkat Zobrázkovač). Ten se bude skládat ze tří Activit. V první ( ZobrazkovacActivity) bude EditText , do něhož uživatel napíše jméno nějakého Flickrového uživatele, a Button , jímž odešle požadavek. Ta Activity zašle požadavek na Flickrové API (konkrétně na  flickr.people.findByUsername ), zjistí id onoho uživatele a spustí druhou, ShowPhotosActivity. Ta si na Flickru zažádá o flickr.people.getPublicPhotos pro daného uživatele a zobrazí GridView s thumbnaily těch fotek. Po kliknutí na některý z thumbnailů se spustí SinglePhotoActivity, která stáhne a zobrazí fotku v plné velikosti s možností přibližování. Kompletní zdrojové kódy (kromě API klíče Flickru) jsou ke stáhnutí na konci článku, zkompilovaná, zabalená a podepsaná aplikace tamtéž.

Takhle bude vypadat hlavní obrazovka hotové aplikace.

Takhle bude vypadat hlavní obrazovka hotové aplikace.

Po odeslání existujícího uživatelského jména se zobrazí galerie obrázků.

Po odeslání existujícího uživatelského jména se zobrazí galerie obrázků.

Obrázek se dá přibližovat.

Obrázek se dá přibližovat.

V layoutu třídy ZobrazkovacActivity je EditText s id nick, do nějž uživatel vepíše jméno Flickr uživatele. Dále je tam ještě Button s id submit, jímž se odešle požadavek na Flickr API, a ProgressBar s id  progressbar.

Samotný kód ZobrazkovacActivity je poměrně jednoduchý. V onCreate  zapneme debug režim v AQuery ( AQUtility.setDebug(true)), vytvoříme AQuery objekt a tlačítku nastavíme OnClickListener. Pro něj jsem použil anonymní třídu, neboť chci, aby případné výjimky nebyly zachyceny. Při klepnutí na tlačítko se zjistí obsah EditText u, vytvoří se url požadavku na Flickr API a odešle se. Jako ajaxový callback jsem sice zase použil anonymní třídu, aby výjimky zabily aplikaci, ale jediné, co se v callbacku děje, je zavolání metody onUserFetched (protože je už hodně odsazený).

public void onCreate(Bundle savedInstanceState) {
    AQUtility.setDebug(true); // !! ODSTRANIT PŘED ZABALENÍM A PODEPSÁNÍM !!
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    aq = new AQuery(this);

    // Přidáme tlačítku OnClickListener
    aq.id(R.id.submit).clicked(new View.OnClickListener() {

        public void onClick(View v) {
            // Zjistíme obsah EditTextu
            String nick = aq.id(R.id.nick).getText().toString();
            try {
                // Url už musí být zakódované a v nicku mohou být nepovolené
                // znaky, proto musíme url vytvořít takhle
                URI url = new URI("http", "api.flickr.com",
                        "/services/rest",
                        "method=flickr.people.findByUsername&api_key="
                                + API_KEY
                                + "&format=json&nojsoncallback=1&username="
                                + nick, null);

                // Místo ajax(url.toString(), this, "onUserFetched") jsem
                // použil toto řešení, neboť v případě výjimky vyskočí
                // oznámení a zabije to aplikaci. Pro mě je to chtěné
                // chování.
                // Pozor ovšem na to, že AQuery stejně odchytává chyby ve
                // své ajaxové funkci (například
                // org.apache.http.conn.ConnectTimeoutException)
                aq.progress(R.id.progressbar).ajax(url.toString(),
                        JSONObject.class, new AjaxCallback() {

                            @Override
                            public void callback(String url,
                                    JSONObject json, AjaxStatus status) {
                                onUserFetched(url, json, status);
                            }
                        });
            } catch (URISyntaxException e) {
                Toast.makeText(ZobrazkovacActivity.this,
                        "Nepovedlo se vytvořit URI", Toast.LENGTH_LONG);
            }
        }
    });
}

onUserFetched potom zjistí, byl-li požadavek úspěšný a existuje-li uživatel s požadovaným jménem, a pokud ano, zjistí jeho id a spustí ShowPhotosActivity, jejímuž Intentu toto id předá jako extra data.

public void onUserFetched(String url, JSONObject json, AjaxStatus status) {
    if (json != null) {
        try {
            // Pro dokumentaci JSON tříd viz
            // http://www.json.org/javadoc/org/json/JSONObject.html
            String stat = json.getString("stat");
            if (!stat.equals(STATUS_OK)) {
                // Chyba při volání API - špatný API klíč, neexistující
                // uživatel, ...
                signalizeError();
                return;
            }
            JSONObject user = json.getJSONObject("user");
            String id = user.getString("nsid");

            Intent i = new Intent(this, ShowPhotosActivity.class);
            i.putExtra(ShowPhotosActivity.EXTRA_USER_ID, id);
            startActivity(i);

        } catch (JSONException e) {
            signalizeError();
        }
    } else {
        signalizeError();
    }
}

V layoutu ShowPhotosActivity je ProgressBar s id progressbar a GridView s id grid, v němž budou zobrazeny thumbnaily obrázků.

V metodě onCreate ShowPhotosActivity hlavně stáhne seznam obrázků a nastaví OnItemClickListener svému GridView. Tentokrát jsem v obou případech použil callbacky objekt-metoda, takže musím počítat s odchytávanými výjimkami.

public void onCreate(Bundle savedInstanceState) {

    // ...

    URI url = new URI("http", "api.flickr.com", "/services/rest",
            "method=flickr.people.getPublicPhotos&api_key=" + API_KEY
                    + "&user_id="
                    + getIntent().getStringExtra(EXTRA_USER_ID)
                    + "&format=json&nojsoncallback=1", null);

    // Zde si musím dát pozor na to, že nezachycené výjimky v
    // onPhotosFetched jsou odchytávány
    aq.progress(R.id.progressbar).ajax(url.toString(),
            JSONObject.class, this, "onPhotosFetched");

    // ...

    // Nastavení posluchače při kliknutí na jednotlivé thumbnaily. Opět si
    // musím dát pozor, že nezachycené výjimky jsou odchytávány.
    aq.id(R.id.grid).itemClicked(this, "onPhotoClick");
}

Metoda onPhotosFetched zjistí, je-li vrácené JSON správné, a pokud ano, vytáhne z něj pole informací o fotografiích, které předá nově vytvořenému AsyncImageAdapter u (vlastní třída, podíváme se na ni za chvíli). Ten si jednak uloží jako instanční proměnnou a jednak ho nastaví GridView jako adaptér.

public void onPhotosFetched(String url, JSONObject json, AjaxStatus status) {

    // ...

    String stat = json.getString("stat");

    if (!stat.equals(STATUS_OK)) {
        // ...
    }

    // Vrácený JSON vypadá zhruba takto:
    // { "photos": { "photo": [{...}, ...], ... }}
    JSONObject photosObj = json.getJSONObject("photos");
    JSONArray photos = photosObj.getJSONArray("photo");

    adapter = new AsyncImageAdapter(this, photos);

    // Po úspěšném stažení seznamu obrázků nastavíme GridView
    // Adapter.
    aq.id(R.id.grid).adapter(adapter);

    // ...

}

A metoda onPhotoClick získá z  AsyncImageAdapter u url velkého obrázku (viz níže) a tu předá jako Intent-extra spouštěné  SinglePhotoActivity.

public void onPhotoClick(AdapterView parent, View v, int position,
        long id) {
    Intent i = new Intent(this, SinglePhotoActivity.class);
    // Neřešíme NullPointerException, kdyby adapter.getItem(position)
    // vrátilo null
    i.putExtra(SinglePhotoActivity.PHOTO_URL,
            AsyncImageAdapter.getLargeImgUrl(adapter.getItem(position)));
    startActivity(i);
}

AsyncImageAdapter v konstruktoru přebírá ContextJSONArray obsahující informace o jednotlivých fotkách. Kvůli dědění od BaseAdapteru musí implementovat několik důležitých metod, které ale nejsou z hlediska AQuery tak zajímavé, takže vás odkážu na zdrojové kódy. Zde si ukážeme metodu getView, která vytvoří ImageView a pomocí AQuery do něj stáhne příslušný Flickr obrázek. Dále se ještě hodí vědět, že getItem(int position) vrátí JSONObject z příslušné pozice, anebo null, pokud objekt neexistuje. getThumbUrl(JSONObject photo) vrátí řetězec s url adresou thumbnailu obrázku a getLargeImgUrl(JSONObject photo) vrátí url velkého obrázku. Obě posledně jmenované metody jsou statické.

public View getView(int position, View convertView, ViewGroup parent) {
    try {
        JSONObject photo = photos.getJSONObject(position);
        ImageView imageView;

        // Tady buď zkonvertujeme convertView nebo vytvoříme nové ImageView

        // Díky šikovnému AQuery (mimochodem konstruktor
        // new AQuery(View root);) do ImageView stáhneme obrázek jednoduše.
        // Z počátku to bude jen prázdné ImageView, obrázek se tam stáhne
        // asynchronně.
        new AQuery(imageView).image(getThumbUrl(photo));
        return imageView;
    } catch (JSONException e) {
        return null;
    }
}

SinglePhotoActivity nemá žádný XML layout, pouze v onCreate  vytvoříme WebView, které předáme do setContentView, vytvoříme ProgressDialog, abychom uživatele informovali o stahování (a jinak než už ohraným ProgressBar em) a nakonec použijeme  AQuery.

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    WebView webview = new WebView(this);
    setContentView(webview);

    // Pro změnu použijeme ProgressDialog
    ProgressDialog dialog = new ProgressDialog(this);
    dialog.setIndeterminate(true);
    dialog.setCancelable(true);
    dialog.setInverseBackgroundForced(false);
    dialog.setCanceledOnTouchOutside(false);
    dialog.setTitle("Loading...");

    // Ve webview zobrazí obrázek, jehož url získá z Intentu
    (new AQuery(webview)).progress(dialog).webImage(
            getIntent().getStringExtra(PHOTO_URL));
}

A to je vše. Pokud je v pořádku Manifest (zaregistrované všechny Activity a požádáno o android.permission.INTERNET  a pokud jste nezapomněli přidat AQuery .jar soubor, máte funkční Flickr zobrázkovač. S Android Query brnkačka, ne?

Ke stažení

Stáhnout si můžete zdrojové kódy aplikace a zkompilovanou, zabalenou a podepsanou aplikaci.

Závěr

Dnes jsme si představili knihovnu AQuery, která se snaží do Androidu přinést stručnost jQuery. Na mnoho věcí je opravdu velmi šikovná a značně usnadní práci. Někteří javovští puristé tvrdí, že taková syntaxe do Javy nepatří a že navíc už z názvu plyne, že je stejně o  horší než jQuery, ale já bych se tím moc netrápil a klidně ji použil.

Odkazy

Komentáře

Subscribe
Upozornit na
guest
5 Komentářů
Nejstarší
Nejnovější Most Voted
Inline Feedbacks
View all comments
https://jooke.mojeid.cz/#WyznI9umv9

Diky za skvely clanek, doufam, ze v tom budete pokracovat ;-)

Renda

Clanku takovehle kvality posledni dobou spise ubyva nez pribyva. Ode mne palec nahoru za skvelou praci ;-) Diky

Martin Hassman

My děkujeme za chválu! Ale bereme to s odstupem, ono „kvalitních článků ubývá“ se říká minimálně 10 let a možná i déle. A kdyby to byla opravdu, do puntíku a celou dobu pravda, tak bychom dnes už asi neměli nic 8-)

Jiří Prokop

Chtěl bych se zeptat jak je to s podporou nativních funkcí androidu, usnadňuje to např. focení obrázků nativním foťákem nebo přístup do fotogalerie v androidu nebo je to jen pro jakési „android-webaplikace“ (za použití ajaxu)?

Enum a statická analýza kódu

Mám jednu univerzální radu pro začínající programátorty. V učení sice neexistují rychlé zkratky, ovšem tuhle radu můžete snadno začít používat a zrychlit tak tempo učení. Tou tajemnou ingrediencí je statická analýza kódu. Ukážeme si to na příkladu enum.

Pocta C64

Za prvopočátek své programátorské kariéry vděčím počítači Commodore 64. Tehdy jsem genialitu návrhu nemohl docenit. Dnes dokážu lehce nahlédnout pod pokličku. Chtěl bych se o to s vámi podělit a vzdát mu hold.