MójDroid.pl

Tworzymy czytnik kanałów RSS – Podstawowe Activity i szablony

2012-10-07
|
Damian P.

Dziś kolejna część z tworzenia prawdziwej aplikacji. Jako, że (prawie) nic nie zostało stworzone, czas na dodanie podstawowych części programu takich jak na przykład Activity. Sami wiecie, że to nic trudnego, bo wystarczy wyklikać kilka elementów w wyskakującym kreatorze i podstawy mamy gotowe. Zróbmy to więc, dodając od razu naszą własną listę z adapterem.
Informacja: Nie radzisz sobie z kodem? Coś Ci nie wychodzi lub nie działa? Zgubiłeś się? W tym miejscu zobaczysz pełen kod aplikacji.

1. Tworzymy projekt

Nie mamy nawet stworzonego projektu w Eclipse, więc czas na jego dodanie. Wybieramy kolejno zakładkę File na pasku narzędziowym, później New i Android Application Project. Pojawia się kreator. Wypełniamy go wedle własnego uznania, bo poradnik ma wam pokazywać tylko jak się tworzy ogólnie aplikację (a nie prowadzić was za rączkę). Moja aplikacja nazywać się będzie "Parser", a nazwa paczki - "pl.damianpiwowarski.parser". Ze wsparciem dla wersji Androida powinniśmy uważać. Chcę aby ten program działał na większości telefonów, więc wybieram obecnie jedną z najpowszechniejszych wersji Androida jako minimalną - w tym wypadku oznaczoną API numer 7 (Android 2.1). Build target ustawiam na Androida 4.1, choć w przyszłości może to się zmienić. Póki co niech zostanie. Ikon i innych dodatków nie mam jeszcze gotowych, więc odznaczę w tym miejscu ich tworzenie. Wybierzemy również stworzenie podstawowego Activity. Oszczędzi nam to trochę pracy, bo za chwilę i tak musielibyśmy to robić. Wybieramy BlankActivity i idziemy dalej. Nazewnictwo - ono jest ważne, bo zapewni nam szybkie przemieszczanie się pomiędzy poszczególnymi elementami w Eclipse. Ja będę stosował standard w Javie, który od razu jest proponowany i tutaj (rozpoczynamy od wielkiej litery, a kolejne człony nie mają odstępów tylko również rozpoczynają się od dużej). Moja główna klasa będzie się nazywać ViewMain (bo to główny widok aplikacji), a tworzony do niej szablon - view_main. Kreator proponuje nam również stworzenie nawigacji, ale my jej posiadać w aplikacji nie będziemy. Po chwili mamy już gotowe podstawy.

2. Hierarchia i pierwsza lista

Pamiętajcie na każdym kroku tworzenia aplikacji, że jest ona tworzona w pewny hierarchiczny, ułożony sposób. Mamy przede wszystkim projekt w Photoshopie, na którym widzimy, że w głównym Activity znajduje się lista z wiadomościami, przejście do ustawień, filtrowanie wpisów i sekcja ulubione. Te elementy (razem, wszystkie na raz) nie powtarzają się w kolejnych etapach, więc na przykład wczytywanie nowych wiadomości powinno być realizowane właśnie w tym głównym wątku. Kolejne uruchamiane Activity będą dodatkami, a nie podstawą programu. Zatem przejdźmy do kształtowania głównej części aplikacji. Na początku upewnijmy się, że aplikacja posiada dobre nazewnictwo (nie wiem czemu, ale nazwa programu w pierwszej części kreatora jest czasami zastępowana przez nazwę samego Activity), przechodząc do AndroidManifestu. Jeżeli w polu Activity widzimy atrybut label, to trzymając CTRL klikamy na jego wartość. Przechodzimy do pliku strings.xml, gdzie zmieniamy ewentualnie nazwę naszej aplikacji na "Parser". Gdy wszystko jest tak jak powinno, uruchamiamy nasz główny szablon dla programu - view_main (w /layout). W nim rzecz jasna tworzymy mniej więcej coś takiego: Oczywiście jest to już wygląd po dopieszczeniu szczegółów, my możemy póki co stworzyć jedynie ogólny zarys. Ja mam coś takiego: Natomiast sam kod XML prezentuje się tak:
<LinearLayout 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"
    android:background="#fff"
    android:orientation="vertical" >

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="100dp" >

        <ImageButton
            android:id="@+id/ImageButton_view_main_love"
            style="?android:attr/borderlessButtonStyle"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:src="@drawable/ic_love" />

        <View
            android:id="@+id/View_divider"
            android:layout_width="1dp"
            android:layout_height="match_parent"
            android:layout_marginBottom="10dp"
            android:layout_marginLeft="15dp"
            android:layout_marginTop="10dp"
            android:layout_toLeftOf="@+id/ImageButton_view_main_love"
            android:background="#b4b4b4" />

        <EditText
            android:id="@+id/editText1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_centerVertical="true"
            android:layout_marginLeft="15dp"
            android:layout_toLeftOf="@+id/View_divider"
            android:ems="10"
            android:hint="Filtruj kanały po tagach lub URL"
            android:textSize="14sp" >
        </EditText>
    </RelativeLayout>

    <ListView
        android:id="@+id/listView_view_main_parser"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1" >
    </ListView>

</LinearLayout>


Użyłem tutaj LinearLayoutu jako głównego z jednego powodu - nie muszę się martwić o wypełnienie wolnego obszaru. Górna część, czyli ta gdzie jest EditText i ImageButton, zajmuje mały kawałek szablonu, reszta (czyli tutaj ListView) wypełnia automatycznie resztę (czemu się tak dzieje?). Można oczywiście uzyskać to również na jednym RelativeLayoucie, ja jednak wolałem przygotować to w ten sposób.
Przy okazji zdecydowałem, że ta pierwsza część nie będzie się przesuwała z listą - będzie widoczna cały czas.

Teraz czas na wypełnienie listy. Na początku rzecz jasna tworzymy pojedynczy element. W photoshopie wygląda on tak:



Przepisując go na XML, stworzyłem coś takiego:



item_view_main_listview.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="wrap_content"
    android:background="#fff"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/textView_title_item_view_main_listview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="15dp"
        android:layout_marginRight="15dp"
        android:layout_marginTop="10dp"
        android:layout_toLeftOf="@+id/imageView_photo_item_view_main_listview"
        android:maxLines="2"
        android:text="Tytuł"
        android:textColor="#1e202c"
        android:textSize="18sp"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/textView_site_item_view_main_listview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/textView_title_item_view_main_listview"
        android:layout_marginLeft="15dp"
        android:layout_marginRight="15dp"
        android:layout_toLeftOf="@+id/imageView_photo_item_view_main_listview"
        android:lines="1"
        android:maxLines="1"
        android:paddingBottom="10dp"
        android:text="Strona"
        android:textColor="#c3c1c1"
        android:textSize="15sp" />

    <ImageView
        android:id="@+id/imageView_photo_item_view_main_listview"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_alignParentRight="true"
        android:adjustViewBounds="false"
        android:background="#000"
        android:scaleType="centerCrop"
        android:src="@drawable/image_example" />

</RelativeLayout>
Nie wygląda źle, choć wbudowany w SDK podgląd graficzny go nieco rozwala... Obrazek powinien dopasowywać się do wysokości elementu, rozszerzając się nieco, ale zachowując jednakową szerokość. To wymaga jeszcze dopracowania.

3. Uzupełnianie listy przykładowymi danymi

Jeszcze nie pobieramy elementów ze stron, więc aby przetestować działanie naszej listy wypełnijmy ją przykładowymi danymi. Stworzymy tutaj od razu customową-listę, korzystając z własnego Adaptera do wypełniania. Kto uważał przy poprzednich częściach ten wie, że do wykonania takiej operacji potrzebujemy dodatkowej klasy. Tworzymy więc ją: AdapterListViewMain:
package pl.damianpiwowarski.parser;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

public class AdapterListViewMain extends BaseAdapter {
    
    

    // Podstawowe zmienne do wykorzystania
    private Context listContext;
    private LayoutInflater layoutInflater;

    public AdapterListViewMain(Context context) {
	this.listContext = context;
	layoutInflater = LayoutInflater.from(listContext);
    }

    // Ta funkcja liczy nam ile elementów ma pojawić się na liście
    // Na testy dla tego przykładu damy ich 20
    public int getCount() {
	return 20;
    }

    // Pobranie danych dla jednego elementu
    // To zostawiamy puste
    public Object getItem(int position) {
	return null;
    }

    // Jak wyżej, tylko tutaj występuje identyfikator
    // To zostawiamy puste
    public long getItemId(int position) {
	return 0;
    }

    // Holder do cachowania elementów
    // Poprawia znacząco płynność
    private class CustomHolder {
	TextView tvTitle;
	TextView tvSite;
	ImageView ivImage;
    }

    // Pojedynczy element na liście
    public View getView(int position, View convertView, ViewGroup parent) {

	CustomHolder viewCache;

	// ConvertView - czy da się wykorzystać ponownie ostatnio usunięty
	// element na liście?
	if (convertView == null) {
	    // Jest pusty, więc definiujemy podstawę
	    convertView = layoutInflater.inflate(
		    R.layout.item_view_main_listview, null);

	    viewCache = new CustomHolder();

	    // Cachujemy kolejne elementy
	    viewCache.tvTitle = (TextView) convertView
		    .findViewById(R.id.textView_title_item_view_main_listview);
	    viewCache.tvSite = (TextView) convertView
		    .findViewById(R.id.textView_site_item_view_main_listview);
	    viewCache.ivImage = (ImageView) convertView
		    .findViewById(R.id.imageView_photo_item_view_main_listview);
	    
	    convertView.setTag(viewCache);
	} else {
	    viewCache = (CustomHolder) convertView.getTag();
	}
	
	// Przykładowe wypełnienie
	viewCache.tvTitle.setText("Przykład numer " + position);	

	return convertView;
    }

}

I łączymy adapter z naszą listą.

ViewMain:

package pl.damianpiwowarski.parser;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.widget.ListView;

public class ViewMain extends Activity {

    // Podstawowe wykorzystywane elementy
    private ListView listViewParser;
    private AdapterListViewMain adapterListViewMain;

    @Override
    public void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.view_main);

	// Przypisujemy każdy element View do zmiennej
	listViewParser = (ListView) findViewById(R.id.listView_view_main_parser);
	adapterListViewMain = new AdapterListViewMain(this);

	// Łaczymy dane z listą
	listViewParser.setAdapter(adapterListViewMain);
    }

    // Menu na ActionBarze
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
	getMenuInflater().inflate(R.menu.view_main, menu);
	return true;
    }
}
I to wszystko. Kolejne kroki w klasach opisałem bardzo prosto, bo poszczególne elementy tworzyliśmy i poznaliśmy już wcześniej. Jeżeli czegoś nie rozumiecie, tutaj znajdziecie poprzednie części poradnika. Przy okazji możecie zauważyć, że nasz obrazek jest dziwnie mały - to nie szkodzi, jako przykład dałem go w bardzo małej rozdzielczości. Przy pobieraniu swoich ten problem minie.