I'm starting using Jetpack compose and i'm facing some issues with Lazy Column.
The problem is that i want to make a list that contains another list inside every root item (such as a card that containt another list inside).
Unfortunately, when i run the following code (below the snippet that resume what i'm trying to do).
I catch the exception:
java.lang.IllegalStateException: Vertically scrollable component was measured with an infinity maximum height constraints, which is disallowed. One of the common reasons is nesting layouts like LazyColumn and Column(Modifier.verticalScroll()). If you want to add a header before the list of items please add a header as a separate item() before the main items() inside the LazyColumn scope. There are could be other reasons for this to happen: your ComposeView was added into a LinearLayout with some weight, you applied Modifier.wrapContentSize(unbounded = true) or wrote a custom layout. Please try to remove the source of infinite constraints in the hierarchy above the scrolling container.
Code:
LazyColumn(modifier = Modifier.wrapContentHeight()) {
items(list.value) {
item -> LazyColumn(modifier = Modifier.wrapContentHeight()) {
//some Text
}
}
}
I don't need the nested Lazy Column to be scrollable, so i'm wondering if for this purpose there is another element to use instead of LazyColumn, or i'm making some other mistake.
I've also tried to put in item{} block other layout elements, as suggested by the Exception, but with the same result.
No, unfortunately, nesting scrollable composables in the same direction is not allowed.
It's not supported in jetpack compose, but I think it should be supported because many use cases need that. e.g e-commerce applications,food delivery (like DoorDash)
Related
I am building a JTreeTable. I found some starter code and have come pretty far. In the end my goal is to be able to have different data at different levels like a hierarchical list.
Currently, I have it working with data at different levels. However, I am running up against a wall when it comes to changing the columns as a next goal. From where I currently stand I have 3 more milestones:
Show different set of columns for different levels
Ability to adjust column widths for different levels
Ensure the JTree part of the table always stays to left
I am getting close to closing out this task but again stuck at the first of these 3.
Since creating a JTreeTable is complex, the minimum example leverages several class listed below in the image:
I am happy to post the code to any of those classes but I also did not want clog the question with useless code. First let me show the functionality I want.
The first image is when the top level is selected and the second image is when the second level is selected. Notice how the columns are different. That is what I want to happen in my application.
Top level selected:
Second level selected:
So one way I tried to solve this problem, is when the list selection is changed inside this section of code:
ListSelectionListener listener = (ListSelectionEvent e) -> {
TreeTableModelAdapter adapter = (TreeTableModelAdapter) JTreeTable.this.getModel();
//Need to see why this breaks.
JTreeTable.this.getTableHeader().setColumnModel(adapter.getColumnModel());
};
this.getSelectionModel().addListSelectionListener(listener);
This code is in the initialization of the JTreeTable. I have tried setting the column model on both the TableHeader and the table as well. Below is what happens then when I select a row:
The columns just disappear on me. The creation of the column model is happening in the TreeTableModelAdapter class with the following method:
public TableColumnModel getColumnModel(){
DefaultTableColumnModel model = new DefaultTableColumnModel();
for(int i=0;i<getColumnCount();i++){
TableColumn column = new TableColumn();
column.setIdentifier(getColumnName(i));
model.addColumn(column);
}
return model;
}
Any direction would be very helpful. Again happy to post any code you think could be helpful to answer the question. Just put a comment in and I will add it right away.
I will add the milestones as I find them in case this helps others, but for now this question is answered.
Milestone 1
I was actually able to solve the first milestone. The key is to trigger the creation of the columns of the column model, not to create a new column model. Below is the code for when the row selection is changed:
//Change columns depending on row
ListSelectionListener listener = (ListSelectionEvent e) -> {
createDefaultColumnsFromModel();
};
this.getSelectionModel().addListSelectionListener(listener);
This code creates the columns based on the row selected in the JTree part of the JTreeTable. The TreeTableModelAdapter implements the getColumnCount() and getColumnName() methods by also passing the selected row in the JTree to the JTreeTableModel so that the columns and their names are dynamically retrieved based on a particular node in the JTree. The key for this for me was trigger those to be called again to update the JTreeTable.
Milestone 2
Adjusting column widths based on the data level proved to be much more difficult than I had originally anticipated. In order to retain the cells state when the column model changed I had to disconnect the painting of the cells from it. This is a hairy process because this is done inside BasicTableUI and the method that gets the rectangle of the cell is private. So I had to subclass it, overload the paint() method and create my own methods that get called inside the paint method. There was a lot of copy pasting so that I could call normally private methods. I just renamed them and referenced these methods instead. The way the ui class was designed did not make it very flexible. Below is 2 images where I am selecting different levels and the columns are obviously different widths at different levels.
Milestone 3
I was able to make this work by keeping track of the view in the model. This seems very dirty to me as the model should separated from the view. Since the tree column's class is unique, I just returned the right class if that column was the first in the view.
The one problem I have with this technique is that I get unexpected behavior where the value returned is not consistent. I attempted to resolve this by overriding JTree.covertValueToText(). Since a JTree only expects 1 value and depending on the sequence of columns in the view this value could change. So in overriding this method I check the stored index for the JTree column's value. Again this causes the unexpected behavior. I will update the post if I find the fix.
I'm working on creating a basic user interface and I wanted to try and create a portion that is in a scrollTaskPane and is capable of holding multiple entries. As I'm going about creating it I can obviously test it with a simple amount of entries but I'm confused how I can go about later allowing for it to take input to create entries in the scrollTaskPane of maybe 1 entry one time, and then later needing to allow for input of 20 entries. I only know how to use absolute positioning and am trying to figure out the best way to go about it. I also need to later be able to select each entry.
For the entries that will eventually be called and displayed in my interface, I'm planning to store them in a simple text file and use a semicolon as a delimiter between the task "Type" "Name" "Description"(which will be accessible through a button) and "Due Date". Or I may try to learn to use a database for the information. But I haven't decided yet and don't know anything about connecting a database with a java program.
This is the current look (the scrollTaskPane in the middle). And my goal is to put in entries that are each rectangle boxes going across the scrollTaskPane with a checkbox on the end of them. Should I use some sort of grid layout? Or something else? I'm a beginner at user interfaces, so any help is appreciated!
You can make a custom layout, and then keep adding those layout. So extend a layout class, add TextField and a check box in the layout. Initialize the layout with your values, add then add to the ScrollTaskPane.
I've started to use the tree-view-list to show my data. The tree-view-list can be found here:
http://code.google.com/p/tree-view-list-android/
The code that adds text to the tree view line is this:
private String getDescription(final long id) {
final Integer[] hierarchy = getManager().getHierarchyDescription(id);
return "Node " + id + Arrays.asList(hierarchy);
}
in the SimpleStandardAdapter.java file. The data shown in the demo is based on the row id and nothing external to the treeview.
How would I go about adding text for a line of the tree that is external to the tree view?
For example, an array of data that is filled at run time.
The adapter (pretty much as any other adapter) build views from the data provided by model. The AbstractTreeViewAdapter is written in the way that it provides implementation for a wrapped view - so it will either create a new child view (abstract getNewChildView) or reuse one (updateView) - and then adds it as a sub-view to cell item with the right padding/margin etc. (this provides automatically indenting in the tree).
So whenever you need to display something in your own way as a tree cell, you should build your own adapter (you can either extend SimpleStandardAdapter or build your own Adapter from the scratch in similar way to SimpleStandardAdapter and return appropriate view that you want to display as result of getNewChildView and updateView methods.
Both methods get TreeNodeInfo treeNodeInfo as parameter, so you can figure out which node of the tree you are about to render. The type is - unfortunately - not changeable, I tried at the beginning to make a generic solution where you will be able to use any object type but failed at that, and it has to be Long. Probably it should be changed to reflect that.
So you will get the Long id from TreeNodeInfo that will identify the node, and the best way to use it is to use the Long value as index to some Map where you will keep reference to your node's object(s) and this way to know which data to render.
I hope it helps.
In my application, I have URN-identified data coming in from the server. I'm in the process of abstracting as far as possible so there is very little to no logical code in my views, and I'm using a generic presenter that wraps those views. All widgets have URNs, making it super easy to map incoming data to a specific widget (until now, a 1 to 1 relationship). This has worked well for pretty much every widget, and now I've reached a point where I'm tripped up.
Assume I have (just for simplicity's sake) two RadioButton elements on a view. These buttons belong to a "group" (just by setting their name values to the same thing), but obviously they're 2 distinct elements. I can't map my URN-identified data to a single widget as in every other case because, in this case, it is two widgets.
Here's an example of what I mean:
Utility Company is a ListBox, so just one widget there. I map each item in the list to a specific Enum value.
Utility Rate is a TextBox, so again just one widget to map.
For Energy Usage, they can select to use either an average for the year or input 12 monthly values. I'm stuck here. I can't map to just one of the RadioButton elements, because then I'd need some extra logic in the view to handle the behavior appropriately.
Am I stuck mapping to just one widget and sticking (unwanted) logic in my view to determine what the state of all of the elements should be based on the value that came in for the one widget that is mapped?
How should I handle this case?
Edit (Solution):
Following the concepts of jusio's answer, I came up with a workable solution. Because I didn't want to go sticking special case handling through my logic to take care of a non-widget, I created a RadioButtonSet faux widget (public class RadioButtonSet <T extends Enum<?> & HasDisplayText> extends Widget implements HasValueChangeHandlers<T>, HasValue<T>), into which I manually pass the radios I intend to group. Having done that, I can get or set its value and have it fire appropriate events when the user changes the selection. Then mapping the collection of radios is no different than doing so for a listbox. Thanks jusio.
I believe in your case you shouldn't treat radio buttons as two separate widgets, basically in your case you can treat the radio button group as combo box, because behavior is almost the same (the only problem is that you have additional master detail). So basically what you will have to do is to wrap real BO objects into some kind of RadioButtonGroupModel, and give it to view, view can take this model and generate radio buttons (with some editors or whatever else). I remember running into this problem when i was extending databinding FW for JFace, and this was the best way I could find to solve this problem.
If I understood correctly the problem, there are 2 possible solutions:
Give each RadioButton a unique URN (ex: oldURN_1 , oldURN_2)
When you send data for a URN, disable the other one
Keep the same Name for each RadioButton but add a number variable in the data the server sends indicating which radioButton it is supposed to use (ex: 0 for Average and 1 for Monthly)
I have created a custom ExpandableListAdapter and everything works properly. What I'd like to be able to do is in each of the groups add a different type of child to the end. I have tried adding 1 to the getChildrenCount() number and then testing isLastChild in the getChildView() method, but that doesn't seem to work.
If a group has three children what I have working looks like this:
Group
NormalChild
NormalChild
NormalChild
But I'd really like something like this:
Group
NormalChild
NormalChild
NormalChild
AlternateChild
The idea being that the AlternateChild could be a link to more info about the group. Any Ideas?
EDIT:
ListView has addFooterView() which will allow you to add a footer to a whole ListView... wonder how to add them to the ExpandableListView's children, or if it's even possible
Perhaps you could try a slightly different approach.
Rather than trying to add an extra item to the ListView directly, maybe try adding an 'AlternateChild' object to the underlying data source using a 'isAlternateChild' flag (or subclassing NormalChild or creating an IChild interface that you extend with NormalChild and AlternateChild.
Then within getChildView you can check to see if the object being displayed within the view is normal or alternate and create or populate the appropriate View-type accordingly.
By adding your extra object to the underlying data/list directly you can let the ExtendedListView do its thing normally. As an added bonus this means you can make the AlternateChild data dynamic and easily make changes to the data displayed in the view by modifying the corresponding object.
In my opinion this is a better answer
I was trying to put an action bar below the last child of each group if needed, this works very nice for that. Since the action bar has nothing to do with my data, I couldn't use the same object.