Bouw een ASCII Art Editor Database Creation & Querying

Het Android-platform biedt een breed scala aan opslagopties voor gebruik binnen uw apps. In deze tutorialserie gaan we een aantal van de gegevensopslagfaciliteiten van de Android SDK verkennen door een eenvoudig project te bouwen: een ASCII-kunsteditor.

Deze zelfstudiereeks over het maken van een eenvoudige ASCII Art Editor wordt gepresenteerd in vier delen:

  • De gebruikersinterface bouwen
  • Afbeelding exporteren en gebruikersconfiguratie
  • Database maken & opvragen
  • Opslaan en verwijderen van ASCII-afbeeldingen

Stap 1: Maak een databasehelperklasse aan

Om een ​​SQLite-database in Android-apps te beheren, breiden we de SQLiteOpenHelper-klasse uit. Deze klasse zal het aanmaken van de database verzorgen, dus we zullen de datastructuur erin definiëren. Maak een nieuwe klasse in uw Android-project en noem deze "ImageDataHelper" of een andere naam naar keuze. Verleng de openingszin van de klassenverklaring als volgt:

 public class ImageDataHelper breidt SQLiteOpenHelper uit

Voeg de volgende invoer toe aan de bovenkant van de klas:

 importeer android.content.Context; importeer android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; importeer android.provider.BaseColumns;

Stap 2: Definieer de database-eigenschappen

Maak binnen uw database-helperklasse de volgende variabelen om de database-eigenschappen te definiëren. Eerst de databaseversie:

 private static final int DATABASE_VERSION = 1;

Geef vervolgens de database een naam:

 private static final String DATABASE_NAME = "asciipics.db";

Om een ​​betrouwbaar databasemodel te maken, hebben we een ID-kolom nodig, dus voeg er een toe met behulp van de BaseColumns constante:

 public static final String ID_COL = BaseColumns._ID;

Dit geeft ons automatisch een primaire sleutelkolom die automatisch wordt verhoogd. De database zal een enkele tabel bevatten, dus geef hem hierna een naam:

 openbare statische finale String TABLE_NAME = "foto's";

De tabel bevat twee kolommen, één voor de inhoud van de ASCII-illustratie, een tekststring en een voor een naam, die in een lijst wordt weergegeven wanneer de gebruiker probeert opgeslagen kunstwerken te laden. Definieer deze kolommen nu:

 public static final String ASCII_COL = "ascii_text"; public static final String CREATED_COL = "pic_creation";

Nu kunnen we de tekenreeks voor het maken van databases definiëren:

 private static final String DATABASE_CREATE = "MAAK TABLE" + TABLE_NAME + "(" + ID_COL + "INTEGER" + "PRIMARY KEY AUTOINCREMENT," + ASCII_COL + "TEXT," + CREATED_COL + "TEXT);";

Zoals u ziet, heeft de syntaxis betrekking op standaard-SQL in dit geval.


Stap 3: Implementeer de database-aanmaak

We gaan een gebruiken eenling ontwerppatroon voor de databasehelperklasse, die u mogelijk niet tegenkomt, afhankelijk van uw Java-ervaring. Om de databasehelper in meer dan één activiteit te kunnen gebruiken en tegelijkertijd de efficiëntie te behouden, willen we de app beperken zodat deze slechts één exemplaar van de klasse kan maken. Voeg nog een paar instantievariabelen toe:

 privé statische ImageDataHelper dbInstance; private context dbContext;

In plaats van de constructormethode rechtstreeks te gebruiken, zullen onze activiteiten een fabrieksmethode oproepen die we definiëren om een ​​instantie van de klasse terug te geven. We zullen hier de eerste variabele gebruiken om het exemplaar op te slaan, zodat het maar één keer wordt gemaakt. De contextvariabele helpt ons om geheugenlekken te voorkomen, omdat we de toepassingscontext zullen gebruiken, in plaats van de context voor de activiteit die het databasehelperobject maakt.

Voeg na de constanten een constructormethode toe aan de databasehelperklasse:

 private ImageDatabase (context context) super (context, DATABASE_NAME, null, DATABASE_VERSION); 

Merk op dat de constructor privé is, dus externe code kan dit niet rechtstreeks aanroepen. Voeg nu een fabrieksmethode toe zodat uw activiteiten het enkele exemplaar van deze klasse kunnen maken en openen:

 public static ImageDataHelper getInstance (contextcontext) if (dbInstance == null) dbInstance = new ImageDataHelper (context.getApplicationContext ()); return dbInstance; 

Dit is een statische methode, dus we kunnen er toegang toe krijgen door naar de klasse zelf te verwijzen in plaats van een exemplaar ervan. We controleren of de database-helper-instance al is gemaakt en roept de constructor alleen aan als dit niet het geval is. We gebruiken de toepassingscontext voor efficiënt geheugengebruik en retourneren een instantie van de klasse. U zult zien hoe wij deze klas binnenkort van Activiteiten voorzien.

We zullen het aanmaken van de databasetabel uitvoeren in de onCreate methode, dus voeg het toe:

 public void onCreate (SQLiteDatabase db) db.execSQL (DATABASE_CREATE); 

Hier passeren we de tekenreeks voor het maken van databasetabellen. We moeten ook een methode bieden om uit te voeren wanneer de database wordt geüpgraded:

 public void onUpgrade (SQLiteDatabase db, int oldVersion, int newVersion) db.execSQL ("DROP TABLE IF EXISTS pics"); db.execSQL ( "vacuüm"); onCreate (db); 

We vernietigen de bestaande tabel en maken deze opnieuw. Als u de structuur van uw database op enig moment wilt wijzigen, wijzigt u de tekenreeks voor het maken van de database en verhoogt u het versienummer - de ONUPGRADE methode zal worden uitgevoerd.

Dat is onze databasehelperklasse compleet. Nu kunnen we vanuit de hoofdactiviteit een instantie van de databasehulp maken om deze te gebruiken wanneer de app wordt uitgevoerd. Voeg in uw hoofdactiviteitsklasse een nieuwe exemplaarvariabele toe aan de bovenkant:

 private ImageDataHelper imgData;

Binnen in de onCreate methode, maak een instantie van de nieuwe klasse:

 imgData = ImageDataHelper.getInstance (this);

Merk op dat we de klassenaam en de fabrieksmethode gebruiken om het exemplaar van de database-helperklasse te retourneren, op deze manier weten we dat de app als geheel maar hooguit één exemplaar van de klasse heeft.


Stap 4: klikken afhandelen op de knop Laden

Vergeet niet dat we een knop Laden hebben toegevoegd die gebruikers kunnen laden in eerder opgeslagen kunstwerken. In je hoofdactiviteit onCreate methode, luister voor klikken op de knop:

 Knop loadBtn = (Knop) findViewById (R.id.load_btn); loadBtn.setOnClickListener (deze);

Voeg nu een nieuw gedeelte toe aan de voorwaardelijke verklaring in uw bij klikken methode:

 else if (v.getId () == R.id.load_btn) 

We zullen later de verwerking aan dit voorwaardelijke blok toevoegen.


Stap 5: Maak een laadklasse

Wanneer de gebruiker op de knop Laden klikt, starten we een activiteit in de pop-upstijl die boven de hoofdactiviteit verschijnt. Deze nieuwe activiteit presenteert de lijst met opgeslagen kunstwerken in de database, zodat de gebruiker er een kan selecteren om te laden. Maak een nieuwe klasse in uw project en noem deze "PicChooser" of een andere naam als u dat wilt. Omdat de inhoud van deze activiteit een lijst met kunstwerken zal zijn, gebruiken we een ListActivity, dus verleng uw openingsverklaring:

 public class PicChooser breidt ListActivity uit

Voeg de volgende invoer toe aan de klas:

 import android.app.ListActivity; importeer android.content.Intent; import android.database.Cursor; importeer android.database.sqlite.SQLiteDatabase; import android.os.Bundle; import android.view.View; import android.widget.SimpleCursorAdapter; import android.widget.TextView;

We zullen de database gebruiken om de opgeslagen afbeeldingen weer te geven, dus voeg instantievariabelen voor de database, helper en een cursor toe om de gegevens te doorzoeken:

 privé ImageDataHelper picDataHelp; privé SQLiteDatabase savedPictures; private Cursor picCursor;

Voeg de toe onCreate methode:

 public void onCreate (Bundle savedInstanceState) super.onCreate (savedInstanceState); setContentView (R.layout.load); 

Stap 6: Ontwerp de lay-out van de laadactiviteit

Laten we de lay-out toevoegen die we zojuist hebben genoemd - voeg een nieuw bestand toe aan de map "res / layout" en noem het "load.xml" om overeen te komen met de bovenstaande code. Voeg een lineaire lay-out toe aan het nieuwe bestand:

  

Voeg in de lay-out een informatieve tekstweergave en een lijstweergave toe om de opgeslagen afbeeldingsnamen te laden in:

  

Met de lijstweergave-ID kunnen we gegevens erin laden in Java. Voeg de hier getoonde tekenreeks toe aan uw XML-bestand "res / values ​​/ strings":

 Kies uit deze opgeslagen afbeeldingen:

Stap 7: Ontwerp de lijstitems

We moeten een lay-out definiëren voor elk item dat in de lijstweergave wordt weergegeven. Voeg een nieuw lay-outbestand toe aan uw app met de naam "pic_item.xml" en een lineaire lay-out:

  

Merk op dat we een bij klikken attribuut, met vermelding van de naam van een methode die we willen uitvoeren wanneer gebruikers op het betreffende lijstitem klikken. Voeg binnen de Lineaire lay-out Tekstweergaven toe voor de ID en aanmaakstring voor de weergegeven afbeelding:

  

Met de ID's kunnen we gegevens uit de database toewijzen aan deze weergaven.


Stap 8: de opgeslagen afbeeldingen zoeken

Terug in je nieuwe fotokiezer Activiteit onCreate methode, maak na de regel waarin u de nieuwe lay-out hebt ingesteld instanties van de database en helper:

 picDataHelp = ImageDataHelper.getInstance (deze); savedPictures = picDataHelp.getReadableDatabase ();

We gebruiken de fabrieksmethode opnieuw om de database-helperinstantie te retourneren. We zullen een Cursor gebruiken om de records in de databasetabel te doorkruisen, dus maak deze nu aan en doorzoek alles in de tabel "pics":

 picCursor = savedPictures.query ("pics", null, null, null, null, null, null);

Tip: In deze tutorial gebruiken we een zeer eenvoudige database-implementatie om de basisprincipes van gegevensopslag op Android te introduceren. Voor complexere apps zou u echter moeten kijken naar het gebruik van Content Providers voor uw databasebewerkingen. Zie dit bericht over het gebruik van inhoudsproviders en deze over het laden van gegevens met Cursor-laders en fragmenten. Hiermee kunt u zich ontwikkelen voor efficiëntie door uw laadbewerkingen voor gegevens van de belangrijkste UI-thread van de app te verplaatsen, maar de mate van complexiteit die hiermee gemoeid is, is aanzienlijk verhoogd ten opzichte van het basisgebruik dat we hier onderzoeken en ligt daarom iets buiten het bereik van deze reeks.

Voor elke afbeelding in de database gaan we de ID- en aanmaakstring weergeven. We gebruiken een eenvoudige cursoradapter om deze toe te wijzen aan de items in de lijstweergave, zodat ze kunnen worden geselecteerd door gebruikers. We moeten de databasetabelkolommen definiëren die we willen weergeven en de weergaven waaraan we ze willen toewijzen in de lijstweergave:

 String [] kolommen = ImageDataHelper.ID_COL, ImageDataHelper.CREATED_COL; int [] views = R.id.picID, R.id.picName;

We kunnen verwijzen naar de kolomnamen met behulp van de openbare constanten die we in de databasehelperklasse hebben gemaakt. De lay-outitems zijn de twee tekstweergaven die we hebben opgenomen in de lay-out van het lijstitem. Nu kunnen we de Simple Cursor-adapter maken om gegevens aan de zichtbare items in de gebruikersinterface toe te wijzen:

 SimpleCursorAdapter picAdapter = new SimpleCursorAdapter (this, R.layout.pic_item, picCursor, columns, views, SimpleCursorAdapter.FLAG_AUTO_REQUERY);

We passeren de lay-out die we hebben gemaakt voor elk lijstitem, de cursor die we hebben gemaakt om de database-afbeeldingen, kolommen en weergaven te doorlopen die we in kaart hebben gebracht. Nu kunnen we dit instellen als de adapter voor de lijstactiviteit:

 setListAdapter (picAdapter);

Dit zorgt ervoor dat de namen en ID's van alle opgeslagen foto's in de weergave worden weergegeven - vervolgens implementeren we een uit de lijst om in het tekstveld te laden.


Stap 9: Voer een opgeslagen fotoselectie in

Vergeet niet dat toen we de lay-out "pic_item" maakten, we een bij klikken kenmerk voor elk item in de lijst. Wanneer gebruikers op een lijstitem klikken, wordt de opgegeven methode uitgevoerd - de methode moet worden opgenomen in de activiteit die als host voor de lay-out fungeert en de weergave Geklikt als een parameter ontvangen. Voeg de methode toe aan uw activiteit "PicChooser":

 public void picChosen (View view) 

De parameter Weergeven is de lay-out voor het lijstitem, dat twee tekstweergaven bevat, één voor de afbeeldings-ID en één voor de naam. We willen dat de ID van de afbeelding wordt geselecteerd, dus zorg ervoor dat binnen de methode de ID-tekstweergave van de aangeklikte weergave wordt weergegeven en vervolgens de tekstinhoud:

 TextView pickedView = (TextView) view.findViewById (R.id.picID); String selectedID = (String) pickedView.getText ();

Met de ID kunnen we de foto-inhoud uit de database ophalen. Nu gaan we de Picture Chooser Activity voltooien en de gekozen afbeelding-ID retourneren naar de hoofdactiviteit. Sluit eerst de databaseverbindingen:

 picDataHelp.close (); savedPictures.close (); picCursor.close ();

We gaan deze activiteit starten vanuit de hoofdactiviteitklasse, waarbij wordt opgegeven dat deze een resultaat moet opleveren. Wanneer deze activiteit eindigt, de onActivityResult methode zal daarom worden uitgevoerd in de hoofdklasse, zodat we de ID van de door de gebruiker gekozen afbeelding kunnen doorgeven. Maak een intentie en geef de gegevens door:

 Intent backIntent = new Intent (); backIntent.putExtra ("pickedImg", gekozenID);

Stel het resultaat in:

 setResult (RESULT_OK, backIntent);

Nu kunnen we deze activiteit beëindigen:

 af hebben();

Voordat we klaar zijn met de "PicChooser" -klasse, moeten we een beetje huishoudelijk werk doen. Als de gebruiker een afbeelding uit de lijst selecteert, hebben we ervoor gezorgd dat de databaseverbindingen zijn gesloten voordat de activiteit eindigt. De gebruiker kan echter op de knop Terug drukken om terug te keren naar de hoofdactiviteit in plaats van een afbeelding te kiezen. In dat geval kunnen we verbindingen sluiten onDestroy, voeg het gewoon toe aan de klas:

 @Override public void onDestroy () picCursor.close (); picDataHelp.close (); savedPictures.close (); super.onDestroy (); 

Stap 10: Laad de gekozen afbeelding

Nu hebben we de mogelijkheid voor gebruikers om te kiezen uit de afbeeldingen die zijn opgeslagen in de database, we hoeven alleen de gekozen afbeelding in het tekstveld te laden. Terug in uw hoofdactiviteit voegt u de volgende importinstructies toe:

 import android.database.Cursor; importeer android.database.sqlite.SQLiteDatabase;

In de anders als je hebt gemaakt voor klikken op de knop Laden van de methode Click listener, start de activiteit "PicChooser" voor een resultaat:

 Intent loadIntent = new Intent (this, PicChooser.class); this.startActivityForResult (loadIntent, LOAD_REQUEST);

Merk op dat dit vergelijkbaar is met de code die we gebruikten bij het starten van de kleurconfiguratieactiviteit, met een constante die een identifier vertegenwoordigt voor de onActivityResult methode - voeg de constante variabele toe aan de bovenkant van de klas:

 private finale int LOAD_REQUEST = 2;

Wanneer de gebruiker nu een afbeelding uit de weergegeven lijst heeft gekozen, wordt teruggekeerd naar de gekozen ID van de foto onActivityResult dus laten we aan die methode werken. Na de als verklaring waarin u gebruikers behandelde die terugkwamen van de kleurkiezer Activiteit, voeg een toe anders als met een vergelijkbare schets:

 else if (requestCode == LOAD_REQUEST) if (resultCode == RESULT_OK) 

In dit blok krijgt u de gegevens terug van de activiteit "PicChooser":

 String pickedID = data.getStringExtra ("pickedImg");

Wanneer de afbeelding die wordt weergegeven in het tekstveld wordt opgeslagen in de database, houden we de opgeslagen foto-ID bij. Voeg bovenaan aan de klas een variabele toe om dit te doen:

 private int currentPic = -1;

Door het te initialiseren naar een negatief, kunnen we controleren of het huidige beeld uit de database komt of niet. Terug in onActivityResult na het ophalen van de gegevens uit "PicChooser", update deze variabele:

 currentPic = Integer.parseInt (pickedID);

We zullen dit gebruiken wanneer de gebruiker een opgeslagen foto verwijdert of bewerkt. Krijg een exemplaar van de database van de helper:

 SQLiteDatabase savedPicsDB = imgData.getWritableDatabase ();

Zoek in de database naar de afbeelding met de gekozen ID:

 Cursor gekozenCursor = savedPicsDB.query ("pics", nieuwe String [] ImageDataHelper.ASCII_COL, ImageDataHelper.ID_COL + "=?", Nieuwe String [] "" + currentPic, null, null, null);

Neem even de tijd om hiernaar te kijken. De eerste parameter is de tabel, de tweede is een tekenreeksreeks die de gewenste kolommen vertegenwoordigt, in dit geval alleen de tekst waaruit de ASCII-afbeelding bestaat. De derde en vierde parameters zijn de selectie, in SQL zou dit typisch een zijn waar query, waarbij de door de gebruiker gekozen afbeeldings-ID wordt vergeleken in de ID-kolom, zodat we die specifieke afbeelding ophalen.

Er moet slechts één record zijn met de opgegeven ID, dus verplaats de cursor naar het eerste record dat is opgehaald:

 chosenCursor.moveToFirst ();

Verkrijg de tekst voor de foto:

 String savedChars = selectedCursor.getString (0);

Toon de afbeelding in het tekstveld:

 textArea.setText (savedChars);

Sluit de cursor, database en helper:

 chosenCursor.close (); savedPicsDB.close (); imgData.close ();

Conclusie

Dat is onze database die is ingesteld voor het opslaan en laden van afbeeldingen. Controleer de download van de broncode voor alles waar u niet zeker van bent. Op het moment dat u de app uitvoert, ziet u geen opgeslagen afbeeldingen waaruit u kunt kiezen. Dit komt omdat we nog geen foto's hebben opgeslagen. Dat doen we de volgende keer, in het laatste deel van de tutorialserie. We zullen ook omgaan met het verwijderen van foto's, het maken van nieuwe foto's en het bewerken van bestaande foto's. Dan zal onze ASCII-kunsteditor volledig functioneel zijn.