Android: Multiple Dimensional array - java - java

I've got an array coming in from a cursor in a function from a sqlite database. I'd like to add multiple items to the array to make it easier to bring data in from other pages. I.e. I'd like to do mArray("ID"), mArray("Name") (i'll be populating a listview from an array of the function, and would like to have easy access to name from ID)
Here is my code at the moment which only saves the ID in the array:
public static Array GetRooms(String StationID) {
File dbfile = new File(Global.currentDBfull);
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(dbfile, null);
Cursor c = db.rawQuery("select * from StationObjects where ObjectID = 0 and StationID = " + StationID , null);
c.moveToFirst();
Log.e("TomDebug", c.getString(c.getColumnIndex("SubGroupName")));
ArrayList<String> mArrayList = new ArrayList<String>();
c.moveToFirst();
while(!c.isAfterLast()) {
mArrayList.add(c.getString(c.getColumnIndex("SubGroupName")));
c.moveToNext();
}
return null;
}
Edit: To give a bit of clarity;
In psudocode i'd like to be able to do:
while(!c.isAfterLast()) {
mArrayList.name.add(c.getString(c.getColumnIndex("Name")));
mArrayList.type.add(c.getString(c.getColumnIndex("Type")));
mArrayList.ID.add(c.getString(c.getColumnIndex("ID")));
c.moveToNext();
}
So that anywhere in the code i can do
Array curRooms = GetRooms("1234")
String name = curRooms.Name
String type = curRooms.Type
(i know the mix between array and string wont work there, but just an example)

It sounds like you want an actual data structure instead of a string. There are a few ways of doing this, with ORMs and whatnot, but a basic example would be something like this, that you can fill the fields in from the database.
public class StationObject
{
public String name;
public String type;
public String id;
}
//pseudocode
for each result in resultset
fill temp StationObject with fields
add temp to list

Related

Android get ArrayList from Room Database in adapter class

I have a Room Database table with multiple columns (PartsTable). I need to fetch only one column from the table that contains one word String and I'm using a subset of a table as per google docs (PartsTuple).
Now I need to create a function that will or something else that will return the ArrayList of fetched data, which I can access in my adapter class. I am able to return the data and see it in a console log (from the main fragment where I get data from ViewModel), but I just can't seem to make it work on a function that will return the said list of data which I could then access from a different class.
Code from DAO:
#Query("SELECT keyword FROM partsTable")
LiveData<List<PartsTuple>> getTuple();
Code from repo:
public LiveData<List<PartsTuple>> getPartsTuple() {
return partsKeyword;
}
Code from view model:
public LiveData<List<PartsTuple>> getPartsTuple() {
return partsKeyword;
}
Fragment class where I display data in a log:
mViewModel.getPartsTuple().observe(getViewLifecycleOwner(), new Observer<List<PartsTuple>>() {
#Override
public void onChanged(List<PartsTuple> partTuple) {
Log.d(TAG, "vraceno: " + partTuple.toString());
}
});
, and data from the log
D/PartsFragment: vraceno: [part1, parts3, part_2]
Code from adapter class where I compare strings and highlight them.
ArrayTEST arrayTEST = new ArrayTEST();
ArrayList<String> values = arrayTEST.getWordFromHardcodedList();
String text = note.getPartsSubtitle();
Spannable textSpannable = new SpannableString(text);
for (int j = 0; j < values.size(); j++) {
//word of list
String word = String.valueOf(values.get(j));
//find index of words
for (int i = -1; (i = text.indexOf(word, i + 1)) != -1; i++) {
//find the length of word for set color
int last = i + word.length();
textSpannable.setSpan(new BackgroundColorSpan(Color.parseColor("#1a0cab8f")),
i, last, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
textSpannable.setSpan(new ForegroundColorSpan(Color.RED),
i, last, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
if (note.getPartsSubtitle().trim().isEmpty()) {
tvTEXT.setVisibility(View.GONE);
} else {
tvTEXT.setText(textSpannable);
}
The part that I'm having trouble with is this, where I need to get a list of data from database and not hardCoded like this
arrayTEST.getWordFromHardcodedList();
Now I need to access this list of data from my adapter class since if there is a match I wanna highlight the parts from the list of parts in my main recycler view where all the data is shown. I can do this when I type the list manually but it needs to be dynamic based on user input.
Thanks in advance
In your adapter class add a field for this list - I'll call it highlightedParts. Observe getPartsTuple() as you do, and set the data you get to highlightedParts. Then you need to create a custom setter for highlightedParts and every time it gets called, update elements of the RecyclerView to highlight the desired items. For updating, you can use notifyDataSetChanged() method. There are other, more optimized variations for only updating a specific item or item range, but you're going to have to update entire dataset.
Ended up using shared preferences with Gson.
In app gradle add
implementation 'com.google.code.gson:gson:2.8.6'
Save the data to SP in a fragment:
SharedPreferences sharedPreferences = requireActivity().getSharedPreferences("shared_preferences", MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
Gson gson = new Gson();
String json = gson.toJson(myListOfData);
editor.putString("partsKEY", json);
editor.apply();
Load the array in the adapter class:
SharedPreferences sharedPreferences = context.getSharedPreferences("shared_preferences", MODE_PRIVATE);
Gson gson = new Gson();
String json = sharedPreferences.getString("partsKEY", null);
Type type = new TypeToken<ArrayList<NoteTupleTest>>() {
}.getType();
partsArrayList= gson.fromJson(json, type);
if (partsArrayList== null) {
partsArrayList= new ArrayList<>();
}

How can i sort albums from songs?

So my problem is that when i try to sort the albums, the album title and album art are wrong.
I tried sorting the album ids but that doesn't fix it because album id have nothing to do with sorting the art apparently.
When i leave out the sorting everything is correct, but when i try to sort them the album names don't match the album art.
How can i sort my albums in the fragment?
Beneath here you can find my code.
Thanks in advance,
Vince
SONG MODEL
// Columns I'll retrieve from the song table
String[] columns = {
SONG_ID,
SONG_TITLE,
SONG_ARTIST,
SONG_ALBUM,
SONG_ALBUMID,
SONG_FILEPATH,
};
// Limits results to only show music files.
//
// It's a SQL "WHERE" clause - it becomes `WHERE IS_MUSIC=1`.
//
final String musicsOnly = MediaStore.Audio.Media.IS_MUSIC + "=1";
// Querying the system
cursor = resolver.query(musicUri, columns, musicsOnly, null, null);
if (cursor != null && cursor.moveToFirst())
{
do {
// Creating a song from the values on the row
Song song = new Song(cursor.getInt(cursor.getColumnIndex(SONG_ID)),
cursor.getString(cursor.getColumnIndex(SONG_FILEPATH)));
song.setTitle (cursor.getString(cursor.getColumnIndex(SONG_TITLE)));
song.setArtist (cursor.getString(cursor.getColumnIndex(SONG_ARTIST)));
song.setAlbumID (cursor.getInt(cursor.getColumnIndexOrThrow(SONG_ALBUMID)));
song.setAlbum (cursor.getString(cursor.getColumnIndexOrThrow(SONG_ALBUM)));
// Using the previously created genre and album maps
// to fill the current song genre.
String currentGenreID = songIdToGenreIdMap.get(Long.toString(song.getId()));
String currentGenreName = genreIdToGenreNameMap.get(currentGenreID);
song.setGenre(currentGenreName);
// Adding the song to the global list
songs.add(song);
}
while (cursor.moveToNext());
}
else
{
// What do I do if I can't find any songs?
}
cursor.close();
public ArrayList<String> getArtists() {
ArrayList<String> artists = new ArrayList<String>();
for (Song song : songs) {
String artist = song.getArtist();
if ((artist != null) && (! artists.contains(artist)))
artists.add(artist);
}
// Making them alphabetically sorted
Collections.sort(artists, new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
return artists;
}
/**
* Returns an alphabetically sorted list with all the
* albums of the scanned songs.
*
* #note This method might take a while depending on how
* many songs you have.
*/
public ArrayList<String> getAlbums() {
ArrayList<String> albums = new ArrayList<String>();
for (Song song : songs) {
String album = song.getAlbum();
if ((album != null) && (! albums.contains(album)))
albums.add(album);
}
SONG CLASS
public class Song implements Serializable {
private long id;
private String data;
private String title = "";
private String artist = "";
private int albumid = -1;
private String album = "";
private String genre = "";
public Song(long songId, String songData){
this.id = songId;
this.data = songData;
}
public long getId(){
return id;
}
public String getData(){return data;}
//Optional meta data
public void setTitle(String title){
this.title = title;
}
public String getTitle() {
return title;
}
public void setArtist(String artist){
this.artist = artist;
}
public String getArtist() {
return artist;
}
public int getAlbumID() {
return albumid;
}
public void setAlbumID(int albumid) { this.albumid = albumid; }
public void setAlbum(String album){
this.album = album;
}
public String getAlbum() { return album; }
public void setGenre(String genre) {
this.genre = genre;
}
public String getGenre() {
return genre;
}
}
First, I'm not sure why you are trying to sort by album when you are storing your returned values by song (see #Usman Rafi above), but..
Add a global arraylist to the top of your fragment
ArrayList<Song> Albums = new Arraylist<>();
don't try to add genre information--you don't need it for your purpose
I tried sorting the album ids but that doesn't fix it because album id have nothing to do with sorting the art apparently.
Album art Uri's can be written as:
ContentUris.withAppendedId(Uri.parse("content://media/external/audio/albumart"),
cursor.getInt(cursor.getInt(cursor.getColumnIndexOrThrow(SONG_ALBUMID))));
So album art and album_id are actually inextricably linked.
So my problem is that when i try to sort the albums...
Use MediaStore.Audio.Media.IS_MUSIC + "=1 ) GROUP BY (" + MediaStore.Audio.Media.ALBUM in the selection variable of your query...
This will return unique album names (it will also only return one song within the album), if the album is repeated (by having several songs from the same album) in your mediastore database, only the first instance which matches your query will be added to your cursor.
to sort the albums...
Use the sort order to sort cursor rows which are returned by album; I personally sort them using sql's alphabetical order (symbols, numbers, a, b, c....)
You should note here that sorting is case sensitive unless you specify "COLLATE NOCASE"
to write your query and sort it I would use the following code:
String[] projection = {"DISTINCT " + MediaStore.Audio.Media.ALBUM_ID,
MediaStore.Audio.Media._ID,
MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media.ARTIST,
MediaStore.Audio.Media.DATA,
MediaStore.Audio.Media.ALBUM,
MediaStore.Audio.Media.IS_MUSIC};
String selection = MediaStore.Audio.Media.IS_MUSIC +
"=1 ) GROUP BY (" + MediaStore.Audio.Media.ALBUM_ID;
String sort = MediaStore.Audio.Media.ALBUM + " COLLATE NOCASE ASC";
Cursor cursor = context.
getContentResolver().
query(MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI,
projection,
selection,
null,
sort);
After this you can simply move through your cursor adding each row to the data object you built, there is no need for further sorting, and things should be in the proper order.
I personally just loop through
if(cursor != null && cursor.getCount >0){
while(cursor.moveToNext()){
//create new song item and add the album information you need
Song album = new Song(cursor.getInt(cursor.getColumnIndex(MediaStore.Audio.Media._ID)),
cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA)));
album.setAlbumId(cursor.getInt(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID)));
album.setAlbumId(cursor.getInt(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM)));
//add the Song item to the global arraylist
Albums.add(album)
}
}
cursor.close();
you can now access the sorted album info by position in the arraylist... you can get to the album art using the Uri builder i showed you at the top...
like this
Song Album = Albums.get(position);
imageView.setURI(ContentUris.withAppendedId(Uri.parse("content://media/external/audio/albumart"),
Album.getAlbumID());
I hope this is useful to you.
i still think you should build a data class called Album
First of all, you should make a class Album that holds the information of one specific album. Then either implement Comparable or Comparator Interface.
Comparable if the albums are always sorted based on the same
field.
Comparator if you need to implement multiple sorting logic
If I understand your code correctly, you're creating a list of strings which contains albums, then sorting this list independent of the original Query songs list. This means the album art remains untouched. I'd suggest implementing a comparable or comparator.
The reason why the 2, coverart and title mismatch is because you sort one and leave out the other as is.
Consider the below example :
Album before sort -
World
Hello
Id before sort-
1
2
Album after sort
1. Hello
2. World
But id remains as such.
So when you build the uri for the coverart using the id list, id picked at position 0 is of album at position 1.
Without changing much in your code, you can achieve what you intend by sorting the Songs using a Comparator. Comparator can be used whenever you need to sort the objects using one of its properties (which is album name in your case for the QuerySongs object)
In your getAlbumsId method, add following
public ArrayList<Long> getAlbumsId() {
ArrayList<Long> albumids = new ArrayList<Long>();
Collections.sort(songs, new Comparator<QuerySongs>() {
#Override
public int compare(QuerySongs o1, QuerySongs o2) {
return o1.getAlbum().compareTo(o2.getAlbum());
}
});
for (QuerySongs song : songs){
Long albumid = song.getAlbumID();
if (! albumids.contains(albumid)) {
albumids.add(albumid);
}
}
return albumids;
}
Above will mutate the songs object, if you don't want that to happend, make a copy of it. Crux is to use comparator to sort the songs.

Populating an ExpandableListView with data from database

I have a SQLite database that contains multiple tables. For each table, I am trying to represent all the data in an ExpandableListView using a custom CursorTreeAdapter. As far as I understand, the method getChildrenCursor returns a cursor that points to the data I needed to populate my child views. However, I do not have a concrete idea on how to retrieve the children cursor using the groupCursor parameter.
#Override
protected Cursor getChildrenCursor(Cursor groupCursor) {
String[] projection = { "columnNeeded" };
return context.getContentResolver()
.query(CONTENT_URI, projection, null, null, null);
}
the above method will return a cursor that returns all rows containing the column I need. Is this the right way to do this?
In the "columnNeeded" column for each row of the table, it contains a String representation of a jsonArray. I was trying to store an arrayList into each row of the table using JSONArray. Therefore, I am trying to retrieve this arrayList and populate the child views with this arrayList like so:
#Override
protected void bindChildView(View view, Context context, Cursor cursor, boolean isLastChild) {
TextView name = (TextView) view.findViewById(R.id.summary_child_name);
TextView bill = (TextView) view.findViewById(R.id.summary_child_bill);
String arrayListString = cursor
.getString(cursor.getColumnIndex("columnNeeded"));
JSONObject json = new JSONObject();
try {
json = new JSONObject(arrayListString);
} catch (JSONException e) {
Log.e(LOG_TAG, "Unable to retrieve list of items");
}
JSONArray jsonArray = json.optJSONArray(ReceiptContract.JSONARRAY_NAME);
// retrieveArrayList iterates through jsonArray and adds it to items
items = retrieveArrayList(jsonArray);
name.setText(What do I put here?);
bill.setText(What do I put here?);
}
As you can see I have managed to retrieve the entire array list as an ArrayList type object. However, I am stuck on displaying the array list in the child views. Any idea on how I can go about doing this?
Use HashMap to store your data as key value and populate your data into your ExpandableListView using BaseExpandableListAdapter

How to make search results clickable

I have created a database app, where the user can search for a film. The app will then return the results of this search in a ScrollView. I then want these search results to be clickable, which will lead to another page to display full details of the film selected. What is the best way to do this?
public void search(View v){
EditText search = (EditText)findViewById(R.id.edtSearch);
String searchresult = "%" + search.getText().toString() + "%";
db = new DbHelper(this).getReadableDatabase();
String[] tblColumns = {"*"};
String where = "film LIKE ? OR actor LIKE ? OR actor2 LIKE ? OR director LIKE ?";
String[] args = {searchresult, searchresult, searchresult, searchresult};
Cursor results = db.query("FILMTABLE", tblColumns, where, args, null, null, null);
film(results);
}
public void film (Cursor c){
c.moveToFirst();
int titleIndex = c.getColumnIndex("film");
int idIndex = c.getColumnIndex("id");
String title = c.getString(titleIndex);
int filmID = c.getInt(idIndex);
TextView txt = new TextView(getApplicationContext());
txt.setId(filmID);
txt.setText(title);
txt.setTextColor(Color.BLACK);
txt.setTextSize(15);
ScrollView scrollView = (ScrollView)findViewById(R.id.scrolLView);
scrollView.addView(txt);
}
Filter your search result in arraylist and use the listview for showing the result. Then implement listview.setOnItemClickListner.
Follow this : http://www.javacodegeeks.com/2013/06/android-listview-tutorial-and-basic-example.html
Every widget in Android is a clickable object. You can format the results however you like, likely in a ListView and set each one's onClick attribute to be whatever function in Java you need executed when clicked. If they all operate the same, you can just put a click listener on the entire list. If they are intended to operate differently (the more likely case) you can individually assign methods to each element in the list using the above.

What type of data structure should I use to hold table rows?

I'm new to Java and just getting into querying databases. So far I have my results in a ResultSetMetaData. I'm think that for each row in the dataset I should add it to some form of collection? Can anyone tell me the best practice for this?
Thanks,
Jonesy
Create an object to hold the data. Loop through the resultset, creating an object for each one, and store them in an ArrayList or HashMap, depending on how you want to use the data. This allows you to close the database, and it gives you good objects on which you can build methods to manipulate the data.
It also allows you to write code that uses the object that doesn't need to rely on the database. If you ever want to pull out the database later and switch to text files or whatever, it's easy to do and you can still use the same objects and methods.
Usually we have a class with fields that correspond to a table. Then, whenever we have a (full) row in a result set, we create an instance of this class.
Example:
Consider a table created like this:
CREATE TABLE customer (First_Name char(50), Last_Name char(50),
Address char(50), City char(50), Country char(25), Birth_Date date);
A model class would be like this:
public class Customer {
private String firstName;
private String lastName;
private String address;
private String city;
private String country;
private Date date;
public String getFirstName() {
return firstName;
}
// getters for all fields
public void setFirstName(String firstName) {
this.firstName = firstName;
}
// setters for all fields
public String toString() {
return String.format("[%s, %s, %s, %s, %s, %s]", firstName,
lastName, address, city, country, date);
}
}
Now if you read data and have a ResultSet, you would create a new customer object and set the fields:
List<Customer> customers = new ArrayList<Customer>();
ResultSet rs = stmt.executeQuery("SELECT * from CUSTOMER;");
while (rs.next()) {
Customer customer = new Customer();
customer.setFirstName(rs.get("First_Name"));
// ... and so on
customers.add(customer);
}
A List seems quite logical. If you are not going to be having duplicates, and you are not bothered about the order of the results, then perhaps a Set.
A relevant implementation of List:
ArrayList: This is backed by an array, so lookups on particular indices should be quick
Relevant implementations of Set:
HashSet: Backed by a HashMap so O(1) insertion time
TreeSet: Respects the ordering of the data (using the compareTo method) - so iterating over the data will be in order - the trade off is O(log n) insertion time
You can create class which represents real world entities. Later if you wish to choose ORM technology/tool like hibernate you can use same classes.
First, the ResultSetMetaData class holds "information about the types and properties of the columns in a ResultSet object." So the results from your query are in the ResultSet, not in the ResultSetMetaData object.
You can see the Retrieving Values from Result Sets Java tutorial for information on how to extract your data from a ResultSet. You should be able to just loop through the ResultSet as shown and put your records in a List or Map, depending on how you want to access the data later.
I usually follow the same pattern as Andreas_D describes.
The object used to contain each row of data (in this case, the Customer class) is referred to as Data Transfer Object (TO).
The code that gets the database connection, queries the db, populates the TOs and returns them (typically in a List), is referred to as a Data Access Object (DAO).
You can read more about this design pattern here
Many of the answers above advice creating a class to hold the columns of a row and create Array list of the Class Objects. Shouldn't we also worry if the result set is huge though only fewer rows are being processed, would it not over consume memory unless the garbage collector reclaims at the same pace at which the objects are being created.
I had this issue with a ResultSet that had a few dozen columns, writing a class with so many members was way too much work for lazy me. So I iterated the ResultSet each field into a HashMap, the column label way the key and the field being the value. Then each put all the hashmaps from each row into a seperate list and all the lists into a master list.
Worked like a charm.
private ArrayList<ArrayList<HashMap<String, Object>>> allRecords = new ArrayList<>();
public MyTable getRecords()throws IOException, SQLException{
try{
String query = new Utils().readResourceFile("MyQuery.sql");
ResultSet rs = DBUtils.dbConnect().createStatement().executeQuery(query);
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
while (rs.next()){
ArrayList<HashMap<String, Object>> row = new ArrayList<>();
for (int i = 1; i < columnCount + 1; i++) {
HashMap<String, Object> data = new HashMap<>();
Object field = rs.getObject(i);
data.put(rsmd.getColumnLabel(i), rs.wasNull()? "": field);
row.add(data);
}
allRecords.add(row);
}
}catch (IOException | ClassNotFoundException | SQLException e){
if(e instanceof SQLException){
DBUtils.printSQLException((SQLException) e);}
e.printStackTrace();
throw e;
}
return this;
}
And here is how I filtered the data:
public MyTable makeChanges(){
for(int i = 0; i < allRecords.size(); i++){
Date startDate = (Date) allRecords.get(i).stream().filter((HashMap<String, Object> field) -> field.containsKey("StartDate")).findFirst().get().get("StartDate");
int product = (int) allRecords.get(i).stream().filter((HashMap<String, Object> field) -> field.containsKey("pk_Product")).findFirst().get().get("pk_Product");
// now do something....
}
return this;
}

Categories