GridLayout issue recalculating prefered size when it items is hidding
DurankGts opened this issue · comments
Please see the attached pdf about the issue
GridLayout issue_040423_1043PM.pdf
this my test case. Just excecute the method containerGridLayoutIssue() in your star() project.
//-----------------------------------------------------------
//ISSUE:04-04-23
//https://github.com/codenameone/CodenameOne/issues/3692
//----------------------------------------------------------
private interface OnContainerServiceComplete {
public void onContainerServiceReady(final Container container);
}
private ArrayList<Button> searchedItemsList = null;
private ArrayList<Container> allItems = null;
private void containerGridLayoutIssue() {
Form myform = new Form(new BorderLayout());
myform.setUIID("MyForm");
myform.getToolbar().setTitle("GridLayoutIssue");
Container cntServicesByCategory = new Container(BoxLayout.y());
cntServicesByCategory.setAlwaysTensile(false);
cntServicesByCategory.setTensileDragEnabled(false);
cntServicesByCategory.setTensileLength(0);
cntServicesByCategory.setScrollableY(true);
cntServicesByCategory.setScrollVisible(false);
//----------------------------------
//searching services
//----------------------------------
TextField find = new TextField("", "Find service here");
find.getHintLabel().setUIID("textFieldHint");
find.addDataChangedListener(new DataChangedListener() {
@Override
public void dataChanged(int type, int index) {
String methodName = "onSearch";
try {
final String textToSearch = find.getText().toLowerCase();
boolean isItemPaintedInList = (cntServicesByCategory.getComponentCount() > 0);
if ((!isItemPaintedInList && (textToSearch == null || textToSearch.isEmpty()))
|| (isItemPaintedInList && (textToSearch == null || textToSearch.isEmpty()))
|| (!isItemPaintedInList && searchedItemsList != null)) {
//showAllList(cntServicesByCategory);
updateGridLayoutContainerList(cntServicesByCategory);
} else {
if (allItems != null && textToSearch.length() >= 3) {
//Deleting all service to find
searchedItemsList = null;
//cntServicesByCategory.removeAll();
//-----------------------------------------
//CHECKING CATEGOIRES
//-----------------------------------------
for (int i = 0; i < allItems.size(); i++) {
Container cntBgnItem = allItems.get(i);
Label lbCategory = (Label) cntBgnItem.getComponentAt(0);
Container cntBgnCategoryServices = (Container) cntBgnItem.getComponentAt(1);
String categoryName = lbCategory.getText();
int qtyServicesMatchInCategory = 0;
//------------------------------------------------------------
//CHECKING SERVICES BY CATEGORIES
//------------------------------------------------------------
for (int j = 0; j < cntBgnCategoryServices.getComponentCount(); j++) {
Button mb = (Button) cntBgnCategoryServices.getComponentAt(j);
String serviceName = mb.getText().toLowerCase();
String textToSearchFf = textToSearch;
boolean visible = serviceName.toLowerCase().contains(textToSearchFf);
if (visible) {
mb.setHidden(false, false);
mb.setVisible(true);
if (searchedItemsList == null) {
searchedItemsList = new ArrayList<>();
}
searchedItemsList.add(mb);
qtyServicesMatchInCategory++;
} else {
mb.setHidden(true, true);
mb.setVisible(false);
}
cntBgnItem.revalidate();
}//endFor services
//------------------------------------------------------
//CHECK IF CATEGORY MACTH
//------------------------------------------------------
if (qtyServicesMatchInCategory == 0) {
//hidding category no match
cntBgnItem.setHidden(true, true);
} else {
cntBgnItem.setHidden(false, false);
//-----------------------------------------------------------------
// using this way This method delete the items loaded in allItems and cause that some buttons delete from allItems
//-----------------------------------------------------------------
// cntBgnCategoryServices.removeAll();
CalculateRowsColumnsLayout rc = new CalculateRowsColumnsLayout(qtyServicesMatchInCategory, 4);
GridLayout gl = new GridLayout(rc.getRows(), rc.getColumns());
gl.setHideZeroSized(true);
cntBgnCategoryServices.setLayout(gl);
cntBgnCategoryServices.setShouldCalcPreferredSize(true);
try {
// //----------------------------------------------
// //ADDING FOUNDED SERVICE TO CATEGORY
// //----------------------------------------------
// for (int k = 0; k < searchedItemsList.size(); k++) {
// Button mb = searchedItemsList.get(k);
//
// mb.setHidden(false, false);
// mb.setVisible(true);
// mb.setShouldCalcPreferredSize(false);
// //cntBgnCategoryServices.addComponent(k, mb);
// }
// //----------------------------------------------
// cntServicesByCategory.add(cntBgnItem);
searchedItemsList.clear();
qtyServicesMatchInCategory = 0;
} catch (Exception e) {
}
}
cntBgnCategoryServices.revalidate();
cntBgnItem.revalidate();
}//endFor categories
}
}
//----------------------------------------------
//Refresh list
//----------------------------------------------
CN.callSerially(new Runnable() {
@Override
public void run() {
if (cntServicesByCategory != null) {
cntServicesByCategory.revalidate();
}
}
});
//----------------------------------------------
} catch (Exception e) {
}
}
});
myform.add(BorderLayout.NORTH, find);
myform.add(BorderLayout.CENTER, cntServicesByCategory);
FontImage back = FontImage.createMaterial(FontImage.MATERIAL_ARROW_BACK, "iconBack", 5);
myform.getToolbar().addCommandToLeftBar("", back, new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
//updateGridLayoutContainerList(cntServicesByCategory);
showAllList(cntServicesByCategory);
}
});
myform.setSafeArea(true);
myform.setFormBottomPaddingEditingMode(true);
myform.setScrollableY(false);
myform.show();
updateGridLayoutContainerList(cntServicesByCategory);
}
//----------------------------------------------------------
private void updateGridLayoutContainerList(final Container cntServicesByCategory) {
int qtyServices = 10;
int qtyCategory = 5;
CN.scheduleBackgroundTask(new Runnable() {
@Override
public void run() {
//-----------------------------
//DELETING LIST
//-----------------------------
CN.callSerially(new Runnable() {
@Override
public void run() {
if (cntServicesByCategory != null) {
cntServicesByCategory.removeAll();
}
}
});
if (allItems != null) {
allItems.clear();
}
if (searchedItemsList != null) {
searchedItemsList.clear();
}
//--------------------------------------
//Adding category
//--------------------------------------
for (int i = 0; i < qtyCategory; i++) {
final int ii = i;
createBackGroundContainerServices(qtyServices, new OnContainerServiceComplete() {
@Override
public void onContainerServiceReady(Container cntBgnServices) {
//--------------------------------------
//Adding servicesTo category
//--------------------------------------
for (int j = 0; j < qtyServices; j++) {
Button b = new Button("C" + ii + "SE" + j, "Button");
b.setIconUIID("iconButton");
cntBgnServices.addComponent(j, b);
}
Label l = new Label("Category: " + ii);
Container cntBgnCategory = BoxLayout.encloseY(l);
cntBgnCategory.setScrollableY(false);
cntBgnCategory.add(cntBgnServices);
cntBgnCategory.revalidate();
if (allItems == null) {
allItems = new ArrayList<>();
}
allItems.add(cntBgnCategory);
cntServicesByCategory.add(cntBgnCategory);
//refresh List
CN.callSerially(new Runnable() {
@Override
public void run() {
cntServicesByCategory.revalidate();
}
});
}
});
}//Enfor
//------------------
//refresh List
//------------------
CN.callSerially(new Runnable() {
@Override
public void run() {
cntServicesByCategory.revalidate();
}
});
}
});
}
//----------------------------------------------------------
private void createBackGroundContainerServices(final int qtyServices, final OnContainerServiceComplete on) {
CN.callSerially(new Runnable() {
@Override
public void run() {
try {
CalculateRowsColumnsLayout rc = new CalculateRowsColumnsLayout(qtyServices, 4);
GridLayout gl = new GridLayout(rc.getRows(), rc.getColumns());
gl.setHideZeroSized(true);
final Container cntBgnItems = new Container(gl, "Container");
cntBgnItems.setScrollableY(false);
on.onContainerServiceReady(cntBgnItems);
} catch (Exception e) {
}
}
});
}
//----------------------------------------------------------
private Container showAllList(Container cntServicesByCategory) {
String methodName = "showAllList";
try {
if (cntServicesByCategory != null && allItems != null) {
cntServicesByCategory.removeAll();
for (int i = 0; i < allItems.size(); i++) {
Container itemCategory = allItems.get(i);
Container itemServices = (Container) itemCategory.getComponentAt(1);
int qtyS = itemServices.getComponentCount();
for (int j = 0; j < qtyS; j++) {
Button mb = (Button) itemServices.getComponentAt(j);
mb.setShouldCalcPreferredSize(true);
mb.setHidden(false, false);
mb.setVisible(true);
} //endFor
itemCategory.setHidden(false, false);
itemCategory.revalidate();
cntServicesByCategory.add(itemCategory);
}//endFor
searchedItemsList = null;
}
} catch (Exception e) {
}
if (cntServicesByCategory != null) {
cntServicesByCategory.revalidate();
}
return cntServicesByCategory;
}
//----------------------------------------------------------
missing Class
public class CalculateRowsColumnsLayout {
int r = 1;
int c = 1;
//-------------------------------
public CalculateRowsColumnsLayout(int qtyComponents, int maxColums) {
if (qtyComponents > maxColums) {
r = qtyComponents / maxColums;
if (!(qtyComponents % maxColums == 0)) {
r++;
}
}
c = maxColums;
}
//-------------------------------
public int getRows() {
return r;
}
//-------------------------------
public int getColumns() {
return c;
}
//-------------------------------
}//endClass
with this commetted lines I could solve the problem seaching item, but other problem is generated inside the GridLayout childs item.
//I use this code to recalculate the quantity of rows and colums in the GridLayout
CalculateRowsColumnsLayout rc = new CalculateRowsColumnsLayout(qtyServicesMatchInCategory, 4);
GridLayout gl = new GridLayout(rc.getRows(), rc.getColumns());
gl.setHideZeroSized(true);
cntBgnCategoryServices.setLayout(gl);
Can you simplify this?
not this the more simple test case that I can create. You just need to copy paste all that code and call the method containerGridLayoutIssue(); in the start(); of the codenameone app.
please check your support email where I attached the test case in a file more easy to copy.
See this with option to search menu(s) in dashboard.
When you scroll up, a search TextField shows. As you type menus are filtered.
Ant and Maven projects
https://github.com/Eric-Chomba
Dashboard with search
I download your app and check your code and I could check that is different use of GridLayout searching item. You just use and static GridLayout but in my text case is one GridLayout by every category that can filled dinamically from a server by example. My code work perfecty but when I try to find and item that match in various category the GridLayout don't calculate correctly the size.
check this result searching se0 by example.
this using this code in my test case.
private void containerGridLayoutIssue() {
Form myform = new Form(new BorderLayout());
myform.setUIID("MyForm");
myform.getToolbar().setTitle("GridLayoutIssue");
Container cntServicesByCategory = new Container(BoxLayout.y());
cntServicesByCategory.setAlwaysTensile(false);
cntServicesByCategory.setTensileDragEnabled(false);
cntServicesByCategory.setTensileLength(0);
cntServicesByCategory.setScrollableY(true);
cntServicesByCategory.setScrollVisible(false);
//----------------------------------
//searching services
//----------------------------------
TextField find = new TextField("", "Find service here");
find.getHintLabel().setUIID("textFieldHint");
find.addDataChangedListener(new DataChangedListener() {
@Override
public void dataChanged(int type, int index) {
String methodName = "onSearch";
try {
final String textToSearch = find.getText().toLowerCase();
boolean isItemPaintedInList = (cntServicesByCategory.getComponentCount() > 0);
if ((!isItemPaintedInList && (textToSearch == null || textToSearch.isEmpty()))
|| (isItemPaintedInList && (textToSearch == null || textToSearch.isEmpty()))
|| (!isItemPaintedInList && searchedItemsList != null)) {
//showAllList(cntServicesByCategory);
updateGridLayoutContainerList(cntServicesByCategory);
} else {
if (allItems != null && textToSearch.length() >= 3) {
//Deleting all service to find
searchedItemsList = null;
//cntServicesByCategory.removeAll();
//-----------------------------------------
//CHECKING CATEGOIRES
//-----------------------------------------
for (int i = 0; i < allItems.size(); i++) {
Container cntBgnItem = allItems.get(i);
Label lbCategory = (Label) cntBgnItem.getComponentAt(0);
Container cntBgnCategoryServices = (Container) cntBgnItem.getComponentAt(1);
String categoryName = lbCategory.getText();
int qtyServicesMatchInCategory = 0;
//------------------------------------------------------------
//CHECKING SERVICES BY CATEGORIES
//------------------------------------------------------------
for (int j = 0; j < cntBgnCategoryServices.getComponentCount(); j++) {
Button mb = (Button) cntBgnCategoryServices.getComponentAt(j);
String serviceName = mb.getText().toLowerCase();
String textToSearchFf = textToSearch;
boolean visible = serviceName.toLowerCase().contains(textToSearchFf);
if (visible) {
mb.setHidden(false, false);
mb.setVisible(true);
if (searchedItemsList == null) {
searchedItemsList = new ArrayList<>();
}
searchedItemsList.add(mb);
qtyServicesMatchInCategory++;
} else {
mb.setHidden(true, true);
mb.setVisible(false);
}
cntBgnItem.revalidate();
}//endFor services
//------------------------------------------------------
//CHECK IF CATEGORY MACTH
//------------------------------------------------------
if (qtyServicesMatchInCategory == 0) {
//hidding category no match
cntBgnItem.setHidden(true, true);
} else {
cntBgnItem.setHidden(false, false);
//-----------------------------------------------------------------
// using this way This method delete the items loaded in allItems and cause that some buttons delete from allItems
//-----------------------------------------------------------------
// cntBgnCategoryServices.removeAll();
// CalculateRowsColumnsLayout rc = new CalculateRowsColumnsLayout(qtyServicesMatchInCategory, 4);
// GridLayout gl = new GridLayout(rc.getRows(), rc.getColumns());
// gl.setHideZeroSized(true);
// cntBgnCategoryServices.setLayout(gl);
// cntBgnCategoryServices.setShouldCalcPreferredSize(true);
try {
// //----------------------------------------------
// //ADDING FOUNDED SERVICE TO CATEGORY
// //----------------------------------------------
// for (int k = 0; k < searchedItemsList.size(); k++) {
// Button mb = searchedItemsList.get(k);
//
// mb.setHidden(false, false);
// mb.setVisible(true);
// mb.setShouldCalcPreferredSize(false);
// //cntBgnCategoryServices.addComponent(k, mb);
// }
// //----------------------------------------------
// cntServicesByCategory.add(cntBgnItem);
searchedItemsList.clear();
qtyServicesMatchInCategory = 0;
} catch (Exception e) {
}
}
cntBgnCategoryServices.revalidate();
cntBgnItem.revalidate();
}//endFor categories
}
}
//----------------------------------------------
//Refresh list
//----------------------------------------------
CN.callSerially(new Runnable() {
@Override
public void run() {
if (cntServicesByCategory != null) {
cntServicesByCategory.revalidate();
}
}
});
//----------------------------------------------
} catch (Exception e) {
}
}
});
myform.add(BorderLayout.NORTH, find);
myform.add(BorderLayout.CENTER, cntServicesByCategory);
FontImage back = FontImage.createMaterial(FontImage.MATERIAL_ARROW_BACK, "iconBack", 5);
myform.getToolbar().addCommandToLeftBar("", back, new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
//updateGridLayoutContainerList(cntServicesByCategory);
showAllList(cntServicesByCategory);
}
});
myform.setSafeArea(true);
myform.setFormBottomPaddingEditingMode(true);
myform.setScrollableY(false);
myform.show();
updateGridLayoutContainerList(cntServicesByCategory);
}
this the commentted lines
// GridLayout gl = new GridLayout(rc.getRows(), rc.getColumns());
// gl.setHideZeroSized(true);
// cntBgnCategoryServices.setLayout(gl);
// cntBgnCategoryServices.setShouldCalcPreferredSize(true);
if I uncommentted this lines to recalculate the Dinamic GridLayout , the this the result
the GridLayout is recalculated perfectly but the item found doesn't show becauses the height isn't calculate correctly. (See the -1 in the picture up)
I have updated the missing Class in the testCase (above) CalculateRowsColumnsLayout
A better test case:
Form hi = new Form("Dynamic Grid", BoxLayout.y());
GridLayout grid = new GridLayout(4, 1);
grid.setAutoFit(true);
Container dynamicGrid = new Container(grid);
grid.setHideZeroSized(true);
TextField amount = new TextField();
Button populate = new Button("Populate");
Button hide = new Button("Hide");
Button show = new Button("Show");
hi.addAll(dynamicGrid, amount, populate, hide, show);
populate.addActionListener(e -> {
final int total = Integer.parseInt(amount.getText());
dynamicGrid.removeAll();
for(int iter = 0 ; iter < total ; iter++) {
dynamicGrid.add(new Label("Lbl " + iter));
}
dynamicGrid.revalidate();
});
hide.addActionListener(e -> {
int total = Integer.parseInt(amount.getText());
for(Component cmp : dynamicGrid) {
boolean hidden = total > 0;
cmp.setVisible(!hidden);
cmp.setHidden(hidden);
total--;
}
dynamicGrid.revalidate();
});
show.addActionListener(e -> {
for(Component cmp : dynamicGrid) {
cmp.setVisible(true);
cmp.setHidden(false);
}
dynamicGrid.revalidate();
});
hi.show();
your test case and solution, don't solve the issue describe above. in my real case I create a class GridLayout with your last updates and I used in my test case.
as I told you above the GridLayout seaching item don't recalculate correctly the size or the rows. The initial rows is 2 in my test case but when I continue searching and hidding not necessary category. I could check the it rows don't recalculate correctly.
the components don't recalculate correctly inside the gridLayout because I was using the method //mb.setShouldCalcPreferredSize(true);
private Container showAllList(Container cntServicesByCategory) {
String methodName = "showAllList";
try {
if (cntServicesByCategory != null && allItems != null) {
cntServicesByCategory.removeAll();
for (int i = 0; i < allItems.size(); i++) {
Container itemCategory = allItems.get(i);
Container itemServices = (Container) itemCategory.getComponentAt(1);
int qtyS = itemServices.getComponentCount();
for (int j = 0; j < qtyS; j++) {
Button mb = (Button) itemServices.getComponentAt(j);
//mb.setShouldCalcPreferredSize(true);
mb.setVisible(true);
mb.setHidden(false);
} //endFor
itemCategory.setVisible( true);
itemCategory.setHidden( false);
itemCategory.revalidate();
cntServicesByCategory.add(itemCategory);
}//endFor
searchedItemsList = null;
}
} catch (Exception e) {
}
if (cntServicesByCategory != null) {
cntServicesByCategory.revalidate();
}
return cntServicesByCategory;
}
but when I found one item inside the specific category that have a GridLayout in my case. The GridLayout I could understand now that don't recalculate it rows. (see picture above). This the cause of this code in may case.
CalculateRowsColumnsLayout rc = new CalculateRowsColumnsLayout(qtyServicesMatchInCategory, 4);
GridLayout gl = new GridLayout(rc.getRows(), rc.getColumns());
gl.setHideZeroSized(true);
gl.setAutoFit(true);
cntBgnCategoryServices.setLayout(gl);
Reproduce this with the test case I posted here.