Create a Custom Listfield - Change Highlight Color

 

Create a Custom Listfield - Change Highlight Color when Scrolling

When you're programming for Blackberry you will likely find yourself in need of displaying a list of items that are selectable to the end user. The ListField class in the Blackberry API is extremely versatile and should be your first choice when considering how to display a list of options. 

Key to coding the ListField is to register a ListFieldCallback - a class that implements the ListFieldCallback interface and can be thought of as a data structure that holds the contents of the List and permits you to modify, update, delete, and read these contents. In other words, the ListField handles the painting of the list and the Callback handles the storage of the list contents. 

For this article I'll show you how to get a ListField up and running on the Blackberry and how to customize the highlighting of each row when the user scrolls across the list. For demonstration purposes we'll create the ListField as a private class within the main class that extends UiApplication and simply add our field managers that will hold the ListField to the MainScreen. Note that the code samples do not include the required code for capturing the user selection and performing an action on it - I may write another tutorial around that if people request it however that's pretty straightforward. In the end, our main goal is to have a list that looks like the one below, where the user selection is highlighted in a different color as they scroll across the list:







Write the Constructor


Please note that all the code below is continuous and should be contained in one file. I'll put a link to download the entire code in one file if the demand is there. However, I think it's a good learning experience to see each piece, understand how it works and put it together at the end into a cohesive working program. First, we'll import the necessary classes and create the main function for our example. I'm going to do all the heavy lifting in the constructor of the class - please note that the VerticalFieldManager and the HorizontalFieldManager in the next sections are also part of the constructor. Also, we're declaring instances of the ListField and ListFieldCallback early in the constructor and will flesh out those classes a bit later. Here's the first part of the code:

  1. import net.rim.device.api.system.Bitmap;  
  2. import net.rim.device.api.system.Display;  
  3. import net.rim.device.api.ui.*;  
  4. import net.rim.device.api.ui.component.*;  
  5. import net.rim.device.api.ui.container.*;  
  6.   
  7. import java.util.Vector;  
  8.   
  9. public class ListFieldTest extends UiApplication {  
  10.    
  11.  public static void main(String[] args){  
  12.     
  13.   //main entry point   
  14.   ListFieldTest theApp = new ListFieldTest();  
  15.   theApp.enterEventDispatcher();  
  16.    
  17.  }  
  18.   
  19.   
  20.  public ListFieldTest(){  
  21.   
  22.    HorizontalFieldManager _hfm;  
  23.                   
  24.    //The _vfm will hold the ListField and we'll add it to the _hfm  
  25.    VerticalFieldManager _vfm;  
  26.   
  27.   //Create the vars for ListField creation  
  28.   ListField myList;  
  29.   ListCallback myCallback;  
  30.   
  31.   //Get the device width and height  
  32.   final int width = Display.getWidth();  
  33.   final int height = Display.getHeight();  
  34.           
  35.   //Create the mainScreen - this holds the _hfm and _vfm managers  
  36.   MainScreen mainScreen;  
  37.   mainScreen = new MainScreen();  
  38.                   
  39.   //Private class that we will create in a minute  
  40.   myCallback = new ListCallback();  
  41.   myCallback.erase();  
  42.   
  43.   myList = new MyListField();  
  44.   myList.setCallback(myCallback);  
  45.                   
  46.   //Populate the list with sample elements  
  47.   for(int i=0;i<20;i++){  
  48.         myList.insert(i);  
  49.         myCallback.insert("Element #" + Integer.toString(i), i);  
  50.           
  51.   }  



Create the HorizontalFieldManager


Now we need to create the HorizontalFieldManager that will hold the ListField and VerticalFieldManager. The HFM will act as the "parent" container and will hold the VFM child and we will also use it to draw the gradient. Refer to the code below for the HorizontalFieldManager:


  1. //Draw background gradient on this manager and add VerticalFieldManager for scrolling.  
  2.    _hfm = new HorizontalFieldManager() {  
  3.      
  4.     public void paint(Graphics g)  
  5.     {  
  6.        
  7.       
  8.      //Variables for drawing the gradient  
  9.      int[] X_PTS_MAIN = { 0, width, width, 0};   
  10.      int[] Y_PTS_MAIN = { 00, height, height };   
  11.      int[] drawColors_MAIN = { Color.BLACK, Color.BLACK, Color.DARKBLUE, Color.DARKBLUE};  
  12.    
  13.        
  14.      try {  
  15.       //Draw the gradients     
  16.          g.drawShadedFilledPath(X_PTS_MAIN, Y_PTS_MAIN, null, drawColors_MAIN, null);  
  17.            
  18.      } catch (IllegalArgumentException iae) {  
  19.          System.out.println("Bad arguments.");   
  20.      }  
  21.        
  22.        
  23.        
  24.      //Call super to paint the graphics on the inherited window   
  25.      super.paint(g);  
  26.        
  27.         
  28.       }  
  29.      
  30.    //Sublayout is passed the width and height of the parent window and will tell the window manager  
  31.    //how to layout the buttons, images, etc.  
  32.    protected void sublayout(int w, int h) {    
  33.       
  34.     //GetFieldCount returns the number of fields attached to the instance of this manager.  
  35.     //and lays out the position  
  36.               if (getFieldCount() >0) {                   
  37.                               
  38.                    Field searchRes = getField(0);  
  39.                    layoutChild(searchRes, width, height);  
  40.                    setPositionChild(searchRes,0,0);  
  41.                   
  42.               }  
  43.                 
  44.                 
  45.                
  46.               setExtent(width,height);  
  47.                
  48.           }  
  49.      
  50.      
  51.   };  


Create the VerticalFieldManager



In the VerticalFieldManager declaration we will pass in the VERTICAL_SCROLL option so the lists scroll properly within the container once we add the ListField. Additionally, make sure to override the navigationMovement() function calling this.invalidate() otherwise when you scroll the ListField won't redraw as the user scrolls over the options. Try it without the invalidate() call to see what happens. Here's the final code for the VFM in the constructor:

  1.   _vfm = new VerticalFieldManager(Manager.VERTICAL_SCROLL|Manager.USE_ALL_HEIGHT|Manager.USE_ALL_WIDTH) {  
  2.      
  3.    public void paint(Graphics g)  
  4.       {  
  5.            g.setColor(Color.GRAY);    
  6.           super.paint(g);  
  7.       
  8.       }  
  9.      
  10.    protected boolean navigationMovement(int dx, int dy, int status, int time){  
  11.           this.invalidate();  
  12.           return super.navigationMovement(dx,dy,status,time);  
  13.          }  
  14.            
  15.      
  16.   };  
  17.        //Add the list to the verticalFieldManager  
  18.         _vfm.add(myList);  
  19.           
  20.        //Add the verticalFieldManager to the HorizontalFieldManager  
  21.        _hfm.add(_vfm);  
  22.       //Finally, add the HorizontalFieldManager to the MainScreen and push it to the stack   
  23.        mainScreen.add(_hfm);  
  24.        pushScreen(mainScreen);  
  25.   
  26.   
  27. }//End Ctor  


Create the ListField Class


Now we'll create a private ListField class and we'll override the paint() method to draw the highlight color on the selected row. The tricky part is forcing the redraw of the highlight color - you must first determine the selected row by calling getSelectedIndex() then mathematically calculate the size of the row to paint the highlight color. Here's the code (the comments are pretty detailed explaining what is going on there):

  1. private class MyListField extends ListField{  
  2.     
  3.     
  4.   //0,ListField.MULTI_SELECT  
  5.     private boolean hasFocus = false;  
  6.       
  7.     public  void onFocus(int direction){  
  8.       hasFocus = true;    
  9.     }  
  10.       
  11.      public void onUnfocus()   
  12.         {  
  13.                hasFocus = false;  
  14.                super.onUnfocus();  
  15.                invalidate();  
  16.         }  
  17.        
  18.     public void paint(Graphics graphics)   
  19.           {   int width = Display.getWidth();  
  20.               //Get the current clipping region   
  21.               XYRect redrawRect = graphics.getClippingRect();  
  22.               if(redrawRect.y < 0)  
  23.               {  
  24.                   throw new IllegalStateException("Error with clipping rect.");  
  25.               }  
  26.   
  27.               //Determine the start location of the clipping region and end.  
  28.               int rowHeight = getRowHeight();  
  29.   
  30.               int curSelected;  
  31.                 
  32.               //If the ListeField has focus determine the selected row.  
  33.               if (hasFocus)   
  34.               {  
  35.                    curSelected = getSelectedIndex();  
  36.                      
  37.               }   
  38.               else   
  39.               {  
  40.                   curSelected = -1;  
  41.               }  
  42.                 
  43.               int startLine = redrawRect.y / rowHeight;  
  44.               int endLine = (redrawRect.y + redrawRect.height - 1) / rowHeight;  
  45.               endLine = Math.min(endLine, getSize() - 1);  
  46.               int y = startLine * rowHeight;  
  47.   
  48.               //Setup the data used for drawing.  
  49.               int[] yInds = new int[]{y, y, y + rowHeight, y + rowHeight};  
  50.               int[] xInds = new int[]{0, width, width, 0};  
  51.   
  52.               //Set the callback - assuming String values.  
  53.               ListFieldCallback callBack = this.getCallback();  
  54.   
  55.               //Draw each row  
  56.               for(; startLine <= endLine; ++startLine)   
  57.               {                 
  58.              //If the line we're drawing is the currentlySelected line then draw the fill path in LIGHTYELLOW and the   
  59.                //font text in Black.    
  60.              if(startLine == curSelected){  
  61.                 
  62.                      graphics.setColor(Color.LIGHTYELLOW);  
  63.                      graphics.drawFilledPath(xInds, yInds, nullnull);  
  64.                      graphics.setColor(Color.BLACK);  
  65.                      graphics.drawText((String)callBack.get(this, startLine), 0, yInds[0]);  
  66.                      
  67.              }  
  68.              else{  
  69.                      //Draw the odd or selected rows.  
  70.                     graphics.setColor(Color.LIGHTGREY);  
  71.                     graphics.drawText((String)callBack.get(this, startLine), 0, yInds[0]);  
  72.              }  
  73.                
  74.               //Assign new values to the y axis moving one row down.  
  75.                 y += rowHeight;  
  76.                 yInds[0] = y;  
  77.                 yInds[1] = yInds[0];  
  78.                 yInds[2] = y + rowHeight;  
  79.                 yInds[3] = yInds[2];  
  80.               }  
  81.                 
  82.               //super.paint(graphics);  
  83.           }  
  84.  }  


Create the ListfieldCallback Class


The final step is to create the private ListFieldCallback class that's attached to our ListField. Since we're not embedding images or doing other fancy things, only writing Strings to the Callback, the code is relatively straightforward, as shown below:


  1. //Private class to populate the ListField private variable  
  2.  private class ListCallback implements ListFieldCallback{  
  3.   
  4.     
  5.   private Vector listElements = new Vector();  
  6.           
  7.      public void drawListRow(ListField list, Graphics g,   
  8.                          int index, int y, int w) {    
  9.   
  10.         String text = (String)listElements.elementAt(index);  
  11.         g.setColor(Color.LIGHTGREY);  
  12.         g.drawText(text, 0, y, 0, w);    
  13.      }  
  14.           
  15.      public Object get(ListField list, int index) {    
  16.         return listElements.elementAt(index);   
  17.      }       
  18.   
  19.      public int indexOfList(ListField list, String p, int s) {    
  20.         //return listElements.getSelectedIndex();  
  21.         return listElements.indexOf(p, s);    
  22.      }  
  23.           
  24.           
  25.      public void insert(String toInsert, int index) {    
  26.         listElements.insertElementAt(toInsert, index);    
  27.      }  
  28.        
  29.      public void add(String toInsert){  
  30.       listElements.addElement(toInsert);  
  31.      }  
  32.           
  33.      public void erase() {    
  34.         listElements.removeAllElements();    
  35.      }  
  36.   
  37.   public int getPreferredWidth(ListField listField) {  
  38.    // TODO Auto-generated method stub  
  39.    return 0;  
  40.   }  
  41.   
  42.     
  43.     
  44.   }  
  45.  }  


The final result will be a scrollable list that highlights the user selected option in a Light Yellow color, as shown in the screenshot at the top of this post. Once you understand how this works you can see how modifications to the overridden paint() method in ListField could permit you to have highly modifiable rows in the list - for example, in a Flikr client app you might have a scrollable list of your friends with a thumbnail on each row showing a sample picture from that friend's account. When the user scrolls to a new friend in the list (startLine == curSelected), you might initiate a function that updates the thumbnail with a changing feed of images from that user's account. The possibilities are almost infinite when customizing ListFields. I'll be on the lookout for questions in the comments section.

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值