DropDownList_demo

The following source code demonstrates a class to create a drop down list. It was designed for use on a tablet, but has not been tested on smaller devices.

 // Drop Down List control parts => a.)display field, b.)arrow, c.)listItems
final int _displayX =  100;
final int _displayY = 120;
final int _displayW = 500;
final int _displayH = 70;
final int _itemH = 70;
final int _arrwX = _displayX + _displayW;
final int _arrwY = _displayY;
final int _arrwSize = _displayH;

boolean drop;
color BLUE = color(64,124,188);
color GREEN = color(0,126,0);
String[] fruit = {"Apples","Peaches","Oranges","Bananas","Pears"};
int[] _itemY;
int selectedItem = -1;
 
class List {
 
 void press(float mx, float my){
   // arrow touches
   if((mx >= _arrwX) && (mx <= _arrwX+_arrwSize) && (my >= _arrwY) && (my <= _arrwY+_arrwSize)){
   if(drop == true){
     drop = false;
    } else {
    drop = true;
   }
   } // list touches
   if (fruit.length > 0) {
   for(int j = 0; j < fruit.length; j++){
    if((mx >= _displayX) && (mx <= _displayX + _displayW) && (my >= _itemY[j] ) && (my <= _itemY[j] + _itemH)) {
     selectedItem = j;
     drop = false;
    } 
   }
  }
 } 
  
 void displayFieldString(String str) {
  fill(255); // display field background color
  rect(_displayX,_displayY,_displayW,_displayH);
  fill(0); // text color
  textSize(42);
  text(str, _displayX + 10, _displayY + 15, _displayW, _displayH);
 }
  
 void display(){
  // display field
 if(selectedItem == -1){
   displayFieldString("Select fruit:");  
   } else {
   displayFieldString(fruit[selectedItem]);
  }
  // arrow
  fill(255); // arrow background color
  rect(_arrwX, _arrwY, _arrwSize, _arrwSize);
  fill(GREEN); // arrow color
  triangle(_arrwX+5, _arrwY+5, _arrwX+_arrwSize-5, _arrwY+5, _arrwX+_arrwSize/2, _arrwY+_arrwSize-5);
  // listItems
  if (drop == true){
   if (fruit.length > 0) {
   // list items  
   _itemY = new int[fruit.length];
   for(int j = 0; j < fruit.length; j++){
    _itemY[j] = (_displayY + _displayH) + j*_itemH;      
    fill(255);
    rect(_displayX,_itemY[j],_displayW,_itemH);
    fill(0);
    textSize(42);
    text(fruit[j], _displayX + 10, _itemY[j] + 15, _displayW, _itemH);
   }  
  }
 }
 } 
  
}

List list;

void setup() {
 fullScreen();
 orientation(LANDSCAPE);    
 list = new List();
 drop = false;
}

void draw() {
 background(BLUE);  
 list.display();
}

void mousePressed(){
 list.press(mouseX, mouseY);
}
2 Likes

Unfortunately I discovered a problem with the original post; when a touch is made in the area of the drop down list at startup (before hitting the arrow) list.press() causes the app to crash. After the arrow has been used this doesn’t seem to happen. Nonetheless this is unacceptable, so I hope the following revision fixes the problem. Please don’t put it in the repository until others have had a chance to use it and it is deemed safe. I have run the code on an older smart phone and it seems to run ok there as well as on a larger tablet. It has no autolayout code so I don’t know how versatile it will be. As far as the revisions go, I split the class into two classes: one for the display+arrow and the other for the listItems, and added more checks to make sure the list is dropped before calling xxxx.press(). As far as I know it fixes the crash problem.

// Drop Down List control parts => a.)display field, b.)arrow, c.)listItems
// Rev_1
final int _displayX =  100;
final int _displayY = 120;
final int _displayW = 500;
final int _displayH = 70;
final int _itemH = 70;
final int _arrwX = _displayX + _displayW;
final int _arrwY = _displayY;
final int _arrwSize = _displayH;

boolean dropped;
color BLUE = color(64,124,188);
color GREEN = color(0,126,0);
String[] fruit = {"Apples","Peaches","Oranges","Bananas","Pears"};
int[] _itemY;
int selectedItem = -1;
 
class ListDisplay {
 
 void press(float mx, float my){
   // arrow touches
   if(dropped){
     dropped = false;
    } else {
    dropped = true;
   }
   println("dropped =",dropped);  
 } 
  
 void displayFieldString(String str) {
  fill(255); // display field background color
  rect(_displayX,_displayY,_displayW,_displayH);
  fill(0); // text color
  textSize(42);
  text(str, _displayX + 10, _displayY + 15, _displayW, _displayH);
 }
  
 void display(){
  // display field
 if(selectedItem == -1){
   displayFieldString("Select fruit:");  
   } else {
   displayFieldString(fruit[selectedItem]);
  }
  // arrow
  fill(255); // arrow background color
  rect(_arrwX, _arrwY, _arrwSize, _arrwSize);
  fill(GREEN); // arrow color
  triangle(_arrwX+5, _arrwY+5, _arrwX+_arrwSize-5, _arrwY+5, _arrwX+_arrwSize/2, _arrwY+_arrwSize-5); 
}
}

ListDisplay listDisplay;

class ListItems {
  
 void press(float mx, float my){
   // item touches
   if (dropped) {
   for(int j = 0; j < fruit.length; j++){
    if((mx >= _displayX) && (mx <= _displayX + _displayW) && (my >= _itemY[j] ) && (my <= _itemY[j] + _itemH)) {
     selectedItem = j;
     dropped = false;
      println("dropped =",dropped);
    } 
   }
  }
  }
  
void display() {
  // list items
  if (dropped){ 
   _itemY = new int[fruit.length];
   for(int j = 0; j < fruit.length; j++){
    _itemY[j] = (_displayY + _displayH) + j*_itemH;      
    fill(255);
    rect(_displayX,_itemY[j],_displayW,_itemH);
    fill(0);
    textSize(42);
    text(fruit[j], _displayX + 10, _itemY[j] + 15, _displayW, _itemH);
   }  
 }
 }
 
}
  
ListItems listItems;

void setup() {
 fullScreen();
// orientation(LANDSCAPE); 
 orientation(PORTRAIT);
 listDisplay = new ListDisplay();
 listItems = new ListItems();
 dropped = false; // initial setting
}

void draw() { 
 background(BLUE); 
 listDisplay.display();
 listItems.display();
}

void mousePressed(){
 if((mouseX >= _arrwX) && (mouseX <= _arrwX+_arrwSize) && (mouseY >= _arrwY) && (mouseY <= _arrwY+_arrwSize)){
  listDisplay.press(mouseX, mouseY);
 }  
 if(dropped) {
  if((mouseX >= _displayX) && (mouseX <= _displayX + _displayW) && (mouseY >= _displayY + _displayH) && (mouseY <= _displayY + _displayH + fruit.length*_itemH)) {
    listItems.press(mouseX, mouseY);
    }
  }
}
1 Like

Hi @svan
Nice look and clean code! Thank you for sharing.
One difficulty I had when I started with P4A is that the same code looks very different on different devices. I really like to draw my own objects in Processing, but using native Android views have the advantage of auto-sizing. However, with some more effort, you can auto-size custom-drawn objects as well. For that, I use the method below. On my three devices with resolutions of 854x480, 1280x800, and 1080x1920 pixels I get the respectively image output from your code shown below. Do you think you can use this method to adapt your code?

  • The following proposed method is meant to ensure that textSize() and object widths and heights,
    appear proportionally the same to all possible screen sizes with different pixel densities.

  • Using solely “textSize * pixelDensity()”, will not be sufficient exactly because not only pisel density, but also screen sizes differ.

  • One of the possible solutions is to take every object size and position in relation to width and height, and take the relation -textWidth()/width-, as a factor for the textSize.

  • With this method using the rectMode(CENTER) and textAlign(CENTER, CENTER), will be nescessary for each object and text, to easily center them.

  • Also using a distinct code for each orientation PORTRAIT and LANDSCAPE with different values for both is recommended.

  • In this code it is not nescessary to set a textSize, but rather a string_lenght_factor and object_lenght_factor.
    Just place the objects like e.g. buttons, with position and dimension in relation to width and height,
    and adjust the object and string factor value to the desired size by empirically trying

void setup() {
  size(400, 800);
  orientation(PORTRAIT);
  rectMode(CENTER);
  textAlign(CENTER, CENTER);
  background(0, 0, 200);
  
  // Text stringing values
  String string = "The big brown fox jumped over the lazy dog";
  color text_color = color (255); 
  float text_x = 0.5 * width; // x value (is the Center of text)
  float text_y = 0.5 * height; // and y value in center as well
  
  // Set here text string lenght factor Must be done empirically !
  float string_lenght_factor = 10;
  
  // Calculation to ensure same text widht of the string on different screens
  textSize(12); // Default text size
  float string_width = textWidth(string); // Text string width
  float text_width_factor = width / string_width;  // Factor of text width compared to screen width
  float adapted_text_size = string_lenght_factor * text_width_factor; // Actual text string size to use
 
  // Drawing of text
  textSize(adapted_text_size);
  text(string, text_x, text_y);
  
  // Button values
  String button_text = "Start button"; 
  float button_width = width/5; // Set here button width relative to screen width
  float button_height = height/30; // and the height relative to screen height
  color button_text_color = color(255); // Button text color
  color button_color = color (200, 0, 0); 
  float but_x = 0.8 * width; // Set here button center x value of the button
  float but_y = 0.85 * height; // and enter y value
  
  // Set here button text lenght factor Must be done empirically !
  float button_string_lenght_factor = 11; // Button text size lenght factor
  
  // Calculation to ensure same text widht of the button on different screens
  textSize(12); // Default text size
  float button_text_width = textWidth(button_text);
  float button_width_factor = button_width / button_text_width;  // Factor of button-width, proportional to buttonn-text-width
  
 // float button_height_factor = 
  float adapted_button_text_size = button_string_lenght_factor * button_width_factor; // Actual button text size of to use
  
  // Drawing the button
  fill (button_color);
  rect(but_x, but_y, button_width, button_height); // Remember rectMode(CENTER);
  textSize(adapted_button_text_size);
  float button_text_height = textAscent() - textDescent();
  println(adapted_button_text_size);
  println(button_text_height);
  println(button_height);
  if(adapted_button_text_size > 0.9 * button_height) { // Ensure that button text height is less than 90% button height
   float button_height_factor = button_height / button_text_height;
   adapted_button_text_size = adapted_button_text_size * button_height_factor / 2;
   println(adapted_button_text_size);
  }
  textSize(adapted_button_text_size);
  fill(button_text_color);
  text(button_text, but_x, but_y);
}

@noel Thank you for the helpful review and suggestions for autoresizing. I would like to try and apply these modifications to the demo to make it more versatile. Will resubmit if I am successful.

1 Like