codenameone / CodenameOne

Cross-platform framework for building truly native mobile apps with Java or Kotlin. Write Once Run Anywhere support for iOS, Android, Desktop & Web.

Home Page:https://www.codenameone.com/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

GridLayout issue recalculating prefered size when it items is hidding

DurankGts opened this issue · comments

commented

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
commented

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);

image

Can you simplify this?

commented

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.

commented

please check your support email where I attached the test case in a file more easy to copy.

commented

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

https://github.com/Eric-Chomba/FeaturesCN1-Ant/blob/master/src/com/zomuhtech/cn/features/Dashboard.java

commented

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.
image

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

image

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)

commented

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();
commented

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.

initial rows
image

searched rows
image

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);
commented

your last solution doesn't solve again the issue.
this the result.
image

I found the solution an will show here when I organize the code.

commented

this the correct result expected with this solutions here

image

Reproduce this with the test case I posted here.