Slide 1

Slide 1 text

Android School Warsztat 04 Adam Jodłowski

Slide 2

Slide 2 text

2 Domyślny katalog aplikacji ● aplikacja może manipulować swoimi plikami z katalogu /data/data//files za pomocą metod kontekstu getFilesDir(); getCacheDir(); openFileInput(); openFileOutput(); deleteFile(); fileList(); ● przykłady zapisu, dopisywania i odczytu FileOutputStream fos; String strFileContents = "Początek."; fos = openFileOutput("tekst.txt", MODE_PRIVATE); fos.write(strFileContents.getBytes()); fos.close(); strFileContents = "Koniec."; fos = openFileOutput("tekst.txt", MODE_APPEND); fos.write(strFileContents.getBytes()); fos.close(); String strFileName = "tekst.txt"; FileInputStream fis = openFileInput(strFileName); ...

Slide 3

Slide 3 text

3 Pamięć zewnętrzna ● używamy jej do składowania dużych plików, niekluczowych składników aplikacji ● Android pozwala na dostęp do zawartości karty SD jeśli urządzenie ją posiada, system plików to przeważnie FAT i nie mają w nim zastosowania uprawnienia linuksowe ● karta SD może zostać w każdej chwili odmontowana – nasza aplikacja musi być na to gotowa ● binarne zasoby statyczne możemy też przechowywać w katalogach /assets i /res/raw

Slide 4

Slide 4 text

4 Praca z pamięcią zewnętrzną ● utworzenie nowego katalogu i zapisanie w nim pliku File sdDirectory = Environment.getExternalStorageDirectory(); if (sdDirectory.exists() && sdDirectory.canWrite()) { File myDirectory = new File(sdDirectory.getAbsolutePath() + "/school"); myDirectory.mkdir(); if (myDirectory.exists() && myDirectory.canWrite()) { File file = new File(myDirectory.getAbsolutePath() + "/" + "as.txt"); file.createNewFile(); if (file.exists() && file.canWrite()) { FileOutputStream fos = new FileOutputStream(file); fos.write("Lubie placki".getBytes()); if (fos != null) { fos.flush(); fos.close(); } } } }

Slide 5

Slide 5 text

5 Praca z pamięcią zewnętrzną ● odczytanie pliku z pamięci zewnętrznej File readFile = new File(Environment.getExternalStorageDirectory() + "/school/" + "as.txt"); if (readFile.exists() && readFile.canRead()) { FileInputStream fis = new FileInputStream(readFile); byte[] reader = new byte[fis.available()]; while (fis.read(reader) != -1) {} String fileContent = new String(reader); if (fis != null) fis.close(); }

Slide 6

Slide 6 text

6 Praca z zasobami statycznymi ● zasoby z /res/raw Resources resources = getResources(); InputStream is = resources.openRawResource(R.raw.plik); byte[] reader = new byte[is.available()]; while (is.read(reader) != -1) {} String data = new String(reader)); is.close(); ● zasoby z /assets AssetManager am = getAssets(); InputStream is = am.open("pliki/plik.txt"); ... is.close(); ● czy dostrzegasz różnicę?

Slide 7

Slide 7 text

7 Wypróbuj mechanizmy zapisu i odczytu danych z domyślnego katalogu, karty pamięci i zasobów statycznych.

Slide 8

Slide 8 text

8 Baza danych ● SQLite to silnik bazodanowy wbudowany w system ● cechuje go słaba współbieżność, ograniczenia integralnościowe nie są zawsze wymuszane, brak zaawansowanych możliwości znanych z pełnoprawnych baz relacyjnych ● każda aplikacja może mieć wiele prywatnych baz w postaci pojedynczych plików ● do bezpośredniego zarządzania bazą danych możemy użyć odpowiednich metod kontekstu, ale lepiej jest wykorzystać klasę pomocniczą SQLiteOpenHelper

Slide 9

Slide 9 text

9 SQLiteOpenHelper ● klasa pomocnika class MyDatabaseHelper extends SQLiteOpenHelper { MyDatabaseHelper(Context context) { super(context, "my_database.db", null, 1); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE " + "EMPLOYEES" + " (_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, surname TEXT NOT NULL, age INTEGER);"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("DROP TABLE IF EXISTS " + "EMPLOYEES"); onCreate(db); } } ● korzystanie z pomocnika MyDatabaseHelper dh = new MyDatabaseHelper(ctx); SQLiteDatabase db = dh.getWritableDatabase(); // SQLiteDatabase db = dh.getReadableDatabase(); // wykonanie operacji na bazie dh.close();

Slide 10

Slide 10 text

10 Podstawowe zapytania ● INSERT i SELECT String INSERT = "INSERT INTO " + "EMPLOYEES" + " (name, surname, age) VALUES (?, ?, ?)"; SQLiteStatement insertStatement = db.compileStatement(INSERT); insertStatement.bindString(1, "scott"); insertStatement.bindString(2, "tiger"); insertStatement.bindLong(3, 666); insertStatement.executeInsert(); Cursor cursor = db.query("EMPLOYEES", new String[] {"name", "surname", "age"}, "name = ?", new String[] {"scott"}, null, null, "age"); if (cursor.moveToFirst()) { do { Log.d("AS", cursor.getString(0) + " " + cursor.getString(1) + ", " + cursor.getLong(2)); } while (cursor.moveToNext()); } if (cursor != null && !cursor.isClosed()) cursor.close();

Slide 11

Slide 11 text

11 Podstawowe zapytania ● SELECT, DELETE i UPDATE db.rawQuery("SELECT * FROM EMPLOYEES WHERE name = ?", new String[] {"scott"}); db.delete("EMPLOYEES", "name = ?", new String[] {"scott"}); ContentValues values = new ContentValues(); values.put("age", 333); db.update("EMPLOYEES", values, "name = ?", new String[] {"scott"});

Slide 12

Slide 12 text

12 Przetestuj mechanizm działania SQLiteOpenHelper, stwórz własną tabelę, dodaj do niej dane i wypisz je wykonując odpowiednie zapytania. Zapoznaj się z programem SQLite Database Browser.

Slide 13

Slide 13 text

13 ListView ● obok GridView i GalleryView stanowi kontrolkę typu AdapterView i zawiera elementy View ● łączy je ze źródłem danych dzięki ListAdapter, np. BaseAdapter, ArrayAdapter, CursorAdapter, które stanowi model i manipuluje widokami ● wydajność operacji adapterów jest kluczowa ● poznamy podejście trzymające listę danych w pamięci, często używamy też kursorów i tzw. Loaderów poprawiających ich wydajność

Slide 14

Slide 14 text

14 ArrayAdapter ● przykładowa klasa adaptera class OrderAdapter extends ArrayAdapter { private ArrayList items; private Context ctx; LayoutInflater inflater = null; public OrderAdapter(Context context, int textViewResourceId, ArrayList items) { super(context, textViewResourceId, items); this.items = items; ctx = context; inflater = (LayoutInflater) ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE); } @Override public View getView(int position, View convertView, ViewGroup parent) { View v = convertView; if (v == null) { // optymalizacja v = inflater.inflate(R.layout.row, null); } Order o = items.get(position); if (o != null) { // odswiezamy tresc wiersza TextView tt = (TextView) v.findViewById(R.id.toptext); tt.setText("Name: " + o.getOrderName()); TextView bt = (TextView) v.findViewById(R.id.bottomtext); bt.setText("Status: " + o.getOrderStatus()); } return v; } }

Slide 15

Slide 15 text

15 Lista z adapterem i optymalizacja ● podpięcie adaptera do listy ListView lv = (ListView) findViewById(R.id.listView); ArrayList orders = new ArrayList(); // wypelniamy liste "orders"... OrderAdapter adapter = new OrderAdapter(ctx, R.layout.row, orders); lv.setAdapter(adapter); // lv.setOnItemClickListener(...); ● dodatkowa optymalizacja adaptera list poprzez ViewHolder pattern static class ViewHolder { public ImageView imageView; public TextView textView; } // cialo metody getView() ViewHolder holder; View rowView = convertView; if (rowView == null) { rowView = inflater.inflate(R.layout.rowlayout, null, true); holder = new ViewHolder(); holder.textView = (TextView) rowView.findViewById(R.id.label); holder.imageView = (ImageView) rowView.findViewById(R.id.icon); rowView.setTag(holder); } else { holder = (ViewHolder) rowView.getTag(); } // odswiezamy tresc wiersza holder.textView.setText(names[position]); holder.imageView.setImageResource(R.drawable.icon); return rowView;

Slide 16

Slide 16 text

16 Metoda adaptera notifyDataSetChanged() powoduje przeładowanie modeli widoków. Stwórz ArrayAdapter i wypełnij za jego pomocą własną listę obiektów, obsłuż kliknięcie na dany element.

Slide 17

Slide 17 text

17 Zadanie domowe: Stwórz aplikację do robienia notatek tekstowych używając ListView oraz bazy danych lub systemu plików. Dla chętnych: stwórz listę z nagłówkami sekcji, korzystając z przesłaniania metod getViewTypeCount(), getItemViewType() i getCount().