How to create a dynamic table of data in android? - java

I've looked around at tutorials for TableLayouts and other such things, but all of it seems to be programmed as a static number of rows with textview's. I'm wondering if it would be possible to create a simple table of data, with 3 columns and a variable set of rows I can add/remove items from in the Java code.
Basically, have something like this:
DATA DATA DATA
row1 data data
row2 data data
and fill this table with data from an object array in the activity's Java class. Later, if I want to add another object, it will just create another row.
For instance, if I have this in java:
Class data{
public data(String d1, String d2, String d3){
data1=d1;
data2=d2;
data3=d3;
}
}
and this in the activity class:
Class activity{
data info[] = {
new data("row1", "row1", "row1"), new data("row2","row2","row2"),
new data("row3","row3","row3")};
}
}
And I will use a for loop to add this data into the table in the activity, regardless of how many rows I need for all of it to be fit. If I add a new object of row4, it will just add another row, and end with:
row1 row1 row1
row2 row2 row2
row3 row3 row3
I hope I haven't been too vague... Thanks in advance, fellas! :)

I feel very stupid, but I've figured this out on my own.
I simply created a <TableView> inside of my <SrollView>, and dynamically added rows to it via a for loop that goes from 0 to myArray.Length().
Bam:
TableLayout prices = (TableLayout)findViewById(R.id.prices);
prices.setStretchAllColumns(true);
prices.bringToFront();
for(int i = 0; i < drug.length; i++){
TableRow tr = new TableRow(this);
TextView c1 = new TextView(this);
c1.setText(drug[i].getName());
TextView c2 = new TextView(this);
c2.setText(String.valueOf(drug[i].getPrice()));
TextView c3 = new TextView(this);
c3.setText(String.valueOf(drug[i].getAmount()));
tr.addView(c1);
tr.addView(c2);
tr.addView(c3);
prices.addView(tr);
}
It's a drug-wars style game... Tryin' to start small in the game development field.
But... She works, and does exactly what I want it to do. Now I can wrap this into a seperate method and update it whenever I want. If I want to add a row, I just add an array entry.
Figured I'd answer my own question... lol!

If you want to have a completely dynamic table as you are used to by ListView or RecyclerView there is the table view on GitHub.
Your code will look like this:
String[][] myData = new String[][] { {"row1", "row1", "row1"},
{"row2", "row2", "row2"},
{"row3", "row3", "row3"} };
TableDataAdapter<String[]> myDataAdapter =
new SimpleTableDataAdapter(getContext(), myData);
TableHeaderAdapter myHeaderAdapter =
new SimpleTableHeaderAdapter(getContext(), "Header1", "Header2", "Header3");
TableView<String[]> table = findViewById(R.id.table);
table.setDataAdapter(myDataAdapter);
table.setHeaderAdapter(myHeaderAdapter);
Because the data is managed in a model you can update the table very easily.
myDataAdapter.getData().add(new String[]{"row4", "row4", "row4"});
myDataAdapter.getData().add(new String[]{"row5", "row5", "row5"});
myDataAdapter.notifyDatasetChanged();
As the table provides a lot of customization and styling possibilities result could look like this: (or completely different)
Best regards :)

You can also create it using a RecyclerView. I think it would be a lot quicker to render. All you have to do is to use GridLayoutManager. the number of gridcells on each row is the number of your columns. the Header row can be implemented as a different ViewHolder which uses a different ui and all you have to do with the rest of your data is to put it in a single dimension array.

You can create any view programatically from Java, by example: TextView tv = new TextView(context);
So you can do that for the the Table and for their rows and columns.
Once you have donde creating it you have to add it to your layout, you have to find the root xml element and then added to it:
Viewgroup root = findeViewById(R.id.root);
root.addViews(programaticView);

Related

Editing a specific cell on a TableView

I want to edit a specific cell in a TableView using JavaFX (for example, row 3, column 5).
I tried this code but it doesn't work.
//Define the string
String s = "myString";
//Define the number
int value = 5;
//Synthesize the item = row
Item item = new Item(s, value);
//Set the i-th item
table.getItems().set(i, item);
Is this what you need? In your example you do not mention any position but the question title does.
table.getTableView().getItems().get(
t.getTablePosition().getRow())
).setLastName(t.getNewValue()
You can find here a complete example.
Just edit the list you are providing to the table to what you want and just call
yourtable.refresh();

JTable with 2 different data

Im using a JTable , loading on it a different data depending on the button pressed.
The problem is : when one of the data is loaded, if i try to load the other one, and pass ther mouse over the header or a cell, it updates the header/cell with the data from the first input, if there is data on the header/cell selected.
Any ideas on how to solve it? That's the code im using.
private static void setCompromissosTable(Object[][] data, Object[] header){
try{
compromissosTable.removeAll();
} catch(Exception e){
e.printStackTrace();
}
compromissosTable = new JTable(data, header);
compromissosTable.setRowSelectionAllowed(true);
// Make the entire row selectable, but not editable
int columnMax = compromissosTable.getColumnCount();
for(int column = 0; column < columnMax; column++){
Class<?> col_class = compromissosTable.getColumnClass(column);
compromissosTable.setDefaultEditor(col_class, null);
}
scrollPane = new JScrollPane(compromissosTable);
pane.add(scrollPane);
scrollPane.setBounds(btnAddCompromisso.getX(),
btnAddCompromisso.getHeight() + btnAddCompromisso.getY() + 5
, frame1.getWidth() - 20
, frame1.getHeight() - 20);
compromissosTable.revalidate();
compromissosTable.repaint();
compromissosTable.addMouseListener(new MouseAdapter() {}
//Change mouse behavior.
);
}
This is suspicious...
compromissosTable = new JTable(data, header);
//...
scrollPane = new JScrollPane(compromissosTable);
pane.add(scrollPane);
Basically, assuming that each time you want to switch data sets, you are calling this method, you are creating a new JTable and JScrollPane each time and then are adding it onto the UI...
What about the previous JTable?
Next is this...
scrollPane.setBounds(btnAddCompromisso.getX(),
btnAddCompromisso.getHeight() + btnAddCompromisso.getY() + 5
, frame1.getWidth() - 20
, frame1.getHeight() - 20);
This looks like you're using a null layout. Basically what it "looks" like is happening, is you're just stacking the JScrollPanes ontop of each other, which would explain, in part, the graphics glitches, as the components are actually been added at the same z-deepthness (essentially) and are competing with each other then they are updated.
Two simple answers...
Don't use null layouts. Sure they "seem" like a good idea, but they have a tendency to turn around and bite you in strange and wonderful ways which are hard to diagnose and fix. Use the layout management API which Swing was designed around
Update the JTables model instead of creating a new JTable/JScrollPane each time
See How to use tables and Laying Out Components Within a Container

Java - Coloring Row in JTable depending on value in the tablemodel

I'm trying to color the text of every row in a table depending on one of the columns in the table. I'm having trouble grasping the concept of renderers, and I've tried out several different renderers but don't seem to understand what they do.
I am trying to load the top ten racers from a certain API given to us by our lecturer into the table model, but colouring each row based on the gender of the racer (which is returned by the getCategory() method of a Finisher/Racer object).
FYI, DataTable is an object written by our lecturer. It's basically a 2D array object.
public void showRacers(DefaultTableModel tblModel,
#SuppressWarnings("rawtypes") JList listOfRaces) {
// Clear the model of any previous searches
tblModel.setRowCount(0);
// Initialize an object to the selected city
CityNameAndKey city = (CityNameAndKey) listOfRaces.getSelectedValue();
// Get the runners for this city
DataTable runners = this.getRunners(city);
// Set the column headers
this.setColumnHeaders(tblModel);
// Make an array list of object Finisher
ArrayList<Finisher> finisherList = new ArrayList<Finisher>();
// Make an array that holds the data of each finisher
Object[] finisherData = new Object[6];
// Make a finisher object
Finisher f;
for (int r = 0; r < 10; r++) {
// Assign the data to the finisher object
finisherList.add(f = new Finisher(runners.getCell(r, 0), runners
.getCell(r, 1), runners.getCell(r, 2), runners
.getCell(r, 3), runners.getCell(r, 4), runners
.getCell(r, 5)));
// Add the data into the array
finisherData[0] = f.getPosition();
finisherData[1] = f.getBibNo();
finisherData[2] = f.getTime();
finisherData[3] = f.getGender();
finisherData[4] = f.getCategory();
finisherData[5] = f.getRuns();
// Put it into the table model
tblModel.addRow(finisherData);
}
}
I would greatly appreciate an explanation, rather than just the answer to my question. Guidance to the answer would be great, and some code would be extremely helpful, but please no: "You should have written this: ten lines of code I don't get
Thank you very much! :)
Using a TableCellRenderer will only allow you to color one column. You would have to have one for each column. A much easier approach is to override prepareRenderer(...) in JTable to color an entire row.
See trashgod's answer here or camickr's answer here

How to change data in dynamically added Android Views at runtime

I have and Android app which adds TableViews, TableRows and TextViews dynamically at run time according to data found in a Firebase repository. More specifically, a TableView is added for each "Poll" found in the repo to the static LinearLayout, for each "Election" found in the Poll a TableRow is added to that Tablelayout, and for each "Nominee" in the Election a TableRow containing two TextViews for data is added to the TableLayout as well. So potentially I could have multiple tables added at runtime, each containing rows for multiple elections, and each election having multiple data rows for nominees. Here's my static layout for good measure....
<?xml version="1.0"?>
<ScrollView android:layout_height="android:layout_width="match_parent" android:id="#+id/scrollView1" xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout android:layout_height="match_parent" android:layout_width="match_parent" android:id="#+id/linearLayout" android:orientation="vertical">
</LinearLayout>
</ScrollView>
..and here's a representation of my Firebase repo...
-POLLS
NUMPOLLS - 5
(pollskey) - NAME - Poll1
NUMELECTIONS - 2
ELECTIONS
(electionskey) - NAME - Election1
NUMNOMINATIONS - 2
NUMVOTERS - 2
NUMBERTOELECT - 1
VOTERS - (votesrkey) - NAME - Charles
NUMBER - (678) 333-4444
.
.
.
(voterskey) - ...
NOMINATIONS - (nominationskey) - NAME - Richard Nixon
NUMBEROFVOTES - 2
.
.
.
(nominationskey) - ...
.
.
.
(electionskey) - ...
.
.
.
(pollskey) - ...
So my question really has two threads. The first is Android oriented...
What's the best way to iterate through Android views added dynamically to change TextView data when data in the repo changes, it being the case that I have no idea how many such TableView, or TableRows there will be at compile time?
Right now I'm doing something like this, but it's very slow going I feel like there has to be a better way...
private void appendCandidatesAndVotes(DataSnapshot election, TableLayout tbl) {
Random randGen = new Random();
DataSnapshot nominees = election.child("Nominations");
for (DataSnapshot nominee : nominees.getChildren()) {
// Create row for candidate name and number of votes
TableRow rowNameAndVotes = new TableRow(this);
// Generating a random row ID here allows us to pass this to
// the valueEventListener and quickly locate this row in
// the case of a data change (someone has cast a vote) so we
// can update
int uniqueRowId = randGen.nextInt(1000);
rowNameAndVotes.setId(uniqueRowId);
rowNameAndVotes.setBackgroundColor(Color.WHITE);
rowNameAndVotes.setLayoutParams(new TableRow.LayoutParams (
LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT));
// Create candidate name view
TextView viewCandidateName = new TextView(this);
viewCandidateName.setId(2);
viewCandidateName.setText(nominee.child("Name").getValue(String.class));
viewCandidateName.setTextColor(Color.BLACK);
viewCandidateName.setPadding(5, 5, 5, 5);
rowNameAndVotes.addView(viewCandidateName);
// Create number of votes view
TextView viewNumVotes = new TextView(this);
viewNumVotes.setId(3);
viewNumVotes.setText(nominee.child("NumberOfVotes").getValue(String.class));
viewNumVotes.setTextColor(Color.BLACK);
viewNumVotes.setPadding(3, 5, 5, 5);
rowNameAndVotes.addView(viewNumVotes);
// Add row to table
tbl.addView(rowNameAndVotes);
// Lets get a Firebase reference for this nominee and
// attach a listener to alert us to all future changes of
// the values therein, so we can update vote counts dynamically
Firebase nomineeRef = nominee.getRef();
nomineeRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot nomineeSnapshot) {
String nomineeName = nomineeSnapshot.child("Name").getValue(String.class);
LinearLayout layout = (LinearLayout) findViewById(R.id.linearLayout);
int layoutChildCount = layout.getChildCount();
for (int i = 0; i < layoutChildCount; i++) {
View layoutChildView = layout.getChildAt(i);
// If its a Table, loop through all row
if (layoutChildView instanceof TableLayout) {
TableLayout tbl = (TableLayout)layoutChildView;
int tableChildCount = tbl.getChildCount();
for (int j = 0; j < tableChildCount; j++) {
View tblChildView = tbl.getChildAt(i);
if (tblChildView instanceof TableRow) {
TableRow row = (TableRow)tblChildView;
int rowChildren = row.getChildCount();
for (int k = 0; k < rowChildren; k++) {
TextView tv = (TextView)(row.getChildAt(k));
String rowCandidateName = tv.getText().toString();
if (rowCandidateName) ...
}
}
}
}
}
}
I think the way this is layed out, the only layout I can directly refer to is the base LinearLayout. I can give all TableRows unique ids with setId(), but as far as I know I can't pass any data to the ValueEventListener, nor can I refer to any variables in the enclosing scope (I hope I'm wrong about this, it would make things way easier). I know I can refer to static final variables inside the ValueEventListener, but I don't see how this will help me since I don't know how many Tables or Rows I'll be dealing with at runtime.
So, in short, how can I link a particular call to the ValueEventListener with the TableRow it's data is associated with?
Now for question 2, which is a bit more Firebase specific....
It seems like ValueEventListener is called once when the activity is loaded, and then again for any changes to the underlying data. But I only want it called for changes to underlying data, not on loading. Am I using the wrong EventListener? Or is there some way to avoid this behavior?
For the Firebase specific question - this is by design. Firebase doesn't encourage distinguishing between initial data and data updates, since in a real-time scenario you can never really be "caught up" to the latest data.
If you don't want to retrieve all the data on startup, then I'd use queries and limits to restrict what data you can receive. For instance, if you only want to receive the latest 100 polls, you could do something like:
Firebase ref = new Firebase(url);
Query q = ref.limit(100);
q.addChildEventListener(...);
and the child event listener will be called once per poll. When a new poll is added it will be called again (and there will be a child-removed event for the oldest poll in the list of 100).
Hope this helps!

Retrieving JTable line content after user sorted content by clicking on column

I have a pane with two tables A and B. When a row is selected in A, the content of B should be upated.
My code detects row selections in A fine. But, when the user clicks on the column header to sort rows, it does not seem like this is taken into account in A's table model.
So, I can get the selected row number (which is correct considering the sorting), but when I try to retrieve row field content from A using its table model, it gives me the values as if rows had not been sorted.
How can I retrieve the content of the selected line from the selected row number?
Without any code, it is hard to say for sure what your problem is. However, it sounds like you are mixing up the row indices between the view and the model. You must be very clear about what co-ordinate system you are referring to (view or model) when you have a row number. See the JTable API for the convertRowIndexToModel and convertRowIndexToView methods.
You probably need something like this:
JTable table = ...;
TableModel model = ...;
int viewRow = table.getSelectedRow();
int modelRow = table.convertRowIndexToModel(viewRow);
int viewColumn = table.getSelectedColumn();
int modelColumn = table.convertColumnIndexToModel(viewColumn);
Object cell = model.getValueAt( modelRow, modelColumn );

Categories

Resources