自定义列表字段单击事件

时间:2012-07-14 10:49:21

标签: java blackberry blackberry-jde listfield

我正在编写一个应用程序,其中我创建了用于显示listview的自定义列表字段。 我的CustomListField包含一行中的一个图像和文本。我点击listfield行获取字段更改监听器,但我也想将fieldchange监听器放在图像上.. 任何人都可以告诉我我该怎么做。

这是我的代码。

public class CustomListField extends ListField implements ListFieldCallback {
    private Vector _listData;
    private int _MAX_ROW_HEIGHT = 60;

    public CustomListField(Vector data) {
        _listData = data;
        setSize(_listData.size());
        setSearchable(true);
        setCallback(this);
        setRowHeight(_MAX_ROW_HEIGHT);
    }

    protected void drawFocus(Graphics graphics, boolean on) {
        XYRect rect = new XYRect();
        graphics.setGlobalAlpha(150);
        graphics.setColor(Color.BLUE);
        getFocusRect(rect);
        drawHighlightRegion(graphics, HIGHLIGHT_FOCUS, true, rect.x, rect.y, rect.width, rect.height);
    }

    public int moveFocus(int amount, int status, int time) {
        this.invalidate(this.getSelectedIndex());
        return super.moveFocus(amount, status, time);

    }

    public void onFocus(int direction) {
        super.onFocus(direction);
    }

    protected void onUnFocus() {
        this.invalidate(this.getSelectedIndex());
    }

    public void refresh() {
        this.getManager().invalidate();
    }

    public void drawListRow(ListField listField, Graphics graphics, int index, int y, int w) {
        listField.setBackground(BackgroundFactory.createBitmapBackground(Bitmap.getBitmapResource("listing_bg.png")));
        ListRander listRander = (ListRander) _listData.elementAt(index);

        graphics.setGlobalAlpha(255);
        graphics.setFont(Font.getDefault().getFontFamily().getFont(Font.PLAIN, 24));

        final int margin = 5;
        final Bitmap thumb = listRander.getListThumb();
        final String listHeading = listRander.getListTitle();
        final Bitmap nevBar = listRander.getNavBar();

        // list border
        graphics.setColor(Color.GRAY);
        graphics.drawRect(0, y, w, _MAX_ROW_HEIGHT);

        // thumbnail border & thumbnail image
        graphics.setColor(Color.BLACK);
        // graphics.drawRoundRect(margin-2, y+margin-2,thumb.getWidth()+2, thumb.getHeight()+2, 5, 5);
        graphics.drawBitmap(margin, y + margin, thumb.getWidth(), thumb.getHeight(), thumb, 0, 0);

        // drawing texts
        // graphics.setFont(Font.BOLD);
        graphics.drawText(listHeading, margin + thumb.getWidth(), y + margin);
        graphics.setColor(Color.GRAY);
        // graphics.setFont(Font.smallFont); // graphics.drawText(listDesc, 2*margin+thumb.getWidth(), y+ margin+20); // //
        // graphics.drawText(listDesc2, 2*margin+thumb.getWidth(), y+ margin+32);

        // draw navigation button
        final int navBarPosY = y + (_MAX_ROW_HEIGHT / 2 - nevBar.getHeight() / 2);
        final int navBarPosX = Graphics.getScreenWidth() - nevBar.getWidth() + margin;
        graphics.drawBitmap(navBarPosX, navBarPosY, nevBar.getWidth(), nevBar.getHeight(), nevBar, 0, 0);
    }

    public Object get(ListField listField, int index) {
        String rowString = (String) _listData.elementAt(index);
        return rowString;
    }

    public int indexOfList(ListField listField, String prefix, int start) {
        for (Enumeration e = _listData.elements(); e.hasMoreElements();) {
            String rowString = (String) e.nextElement();
            if (rowString.startsWith(prefix)) {
                return _listData.indexOf(rowString);
            }
        }
        return 0;
    }

    public int getPreferredWidth(ListField listField) {
        return 3 * listField.getRowHeight();

    }

/*
    protected boolean trackwheelClick(int status, int time) {
        invalidate(getSelectedIndex());
        Dialog.alert(" U have selected :" + getSelectedIndex());
        return super.trackwheelClick(status, time);
    }    
*/
}

我想在列表字段行的星形图像上放置点击列表器

以下是abbove代码的输出。 enter image description here

2 个答案:

答案 0 :(得分:7)

我在过去的项目中做了类似的事情:

背景

As Arhimed said in his answer,正如您可以阅读有关on the BlackBerry forums here的内容,您无法在Field内找到完整的ListField个对象。 ListField行的内容直接在drawListRow()中作为文本和位图等绘制。内容不是Field个实例,因此无法调焦。

所以,我所做的是用ListField的子类替换Manager。最初,我使用了VerticalFieldManager,但我遇到了问题。我也看到了很多关于堆栈溢出的问题,人们将VerticalFieldManager子类化,只定制一个小行为,一切都开始破坏。在我看来,如果你接受它的正常行为,VerticalFieldManager效果很好,如果你需要更多的东西,只需直接扩展Manager。为垂直堆叠的行执行布局非常简单。

然后我将每行设为Manager,并在sublayout()中实现自定义布局,将行Field放置在我想要的位置。然后我也可以使行可聚焦,然后行上的位图/按钮可以单独聚焦(就像你的星星一样)。单击该行会调用一个操作,单击该星会调用另一个操作。

但是,我会注意到,在我的应用中,性能不是问题,因为我只有10-20行。此外,我确实需要修改我的代码以匹配您的示例,因此请仔细考虑此代码。但是,我确实将它构建到一个应用程序中,因此只要我的假设和您的描述有效,它就应该可以正常运行。

实施

首先,我不清楚你的ListRander是什么(你没有显示该代码)。但是,在我的代码中,我需要一个数据类来包含有关一个行的详细信息。它看起来就像你使用ListRander的方式,所以我使用的是:

public class ListRander {

   private String _title;
   private Bitmap _thumb;

   public ListRander(String title, Bitmap thumb) {
      _title = title;
      _thumb = thumb;
   }

   public String getTitle() {
      return _title;
   }
   public Bitmap getThumb() {
      return _thumb;
   }
}

然后,我用我自己的CustomListField课替换了

public class CustomListField extends Manager implements FocusChangeListener {

   private int _MAX_ROW_HEIGHT = 60; 
   private boolean _searchable = false;
   private Vector _listData; 
   private FieldChangeListener _fieldListener;

   public CustomListField(Vector data) {
      super(FOCUSABLE | VERTICAL_SCROLL | VERTICAL_SCROLLBAR);
      setSearchable(true);
      setEditable(false);
      setListData(data);
   }

   public void setChangeListener(FieldChangeListener listener) {
      // we need to save this listener, because we set it to listen to all new rows
      _fieldListener = listener;
      int numFields = getFieldCount();
      for (int f = 0; f < numFields; f++) {
         getField(f).setChangeListener(listener);
      }
      super.setChangeListener(listener);
   }

   public int getRowHeight() {
      return _MAX_ROW_HEIGHT;
   }

   public void setSearchable(boolean searchable) {
      _searchable = searchable;
   }

   public int getSelectedIndex() {
      return getFieldWithFocusIndex();  // TODO??
   }

   public Object get(int index) {
      return _listData.elementAt(index);
   }

   public int indexOfList(String prefix, int start) {
      if (start >= _listData.size() || !_searchable) {
         return -1;
      } else {
         int result = getSelectedIndex();  // the default result if we find no matches
         for (Enumeration e = _listData.elements(); e.hasMoreElements(); ) {
            String rowString = (String) e.nextElement(); 
            if (rowString.startsWith(prefix)) { 
               return _listData.indexOf(rowString); 
            } 
         }
         return result;
      }
   }

   protected boolean navigationClick(int status, int time) {
      CustomListRow focus = (CustomListRow) getFieldWithFocus();
      if (focus != null) {
         // see if the row wants to process this click
         if (!focus.navigationClick(status, time)) {
            // let our FieldChangeListener know that this row has been clicked
            fieldChangeNotify(getFieldWithFocusIndex());
         }
         return true;
      } else {
         return false;
      }
   }

   protected void sublayout(int width, int height) {
      int w = Math.min(width, getPreferredWidth());
      int h = Math.min(height, getPreferredHeight());
      int rowHeight = getRowHeight();
      int numRows = getFieldCount();

      setExtent(w, h);
      setVirtualExtent(w, rowHeight * numRows);

      for (int i = 0; i < numRows; i++) {
         Field f = getField(i);
         setPositionChild(f, 0, rowHeight * i);
         layoutChild(f, w, rowHeight);
      }
   }

   public int getPreferredWidth() {
      return Display.getWidth();
   }

   public int getPreferredHeight() {
      return Display.getHeight();
   }

   public void setListData(Vector listData) {
      _listData = listData;
      if (listData != null) {
         int listSize = listData.size();
         int numRows = getFieldCount();
         for (int s = 0; s < listSize; s++) {
            if (s < numRows) {
               // we can reuse existing CustomListRows
               CustomListRow row = (CustomListRow) getField(s);               
               row.setData((ListRander) listData.elementAt(s));
            } else {
               CustomListRow row = new CustomListRow((ListRander) listData.elementAt(s));
               row.setChangeListener(_fieldListener);
               row.setFocusListener(this);
               add(row);
            }
         }

         if (listSize < numRows) {
            // delete the excess rows
            deleteRange(listSize, numRows - listSize);
         }
      } else {
         deleteAll();
      }
      invalidate();
   }

   public void focusChanged(Field field, int eventType) {
      // we handle scrolling here, when focus changes between rows
      if (eventType == FOCUS_GAINED) {
         if (field.getTop() < getVerticalScroll()) {
            // field is off the top of the screen, so scroll up
            setVerticalScroll(field.getTop());
         } else if (field.getTop() >= getVerticalScroll() + getVisibleHeight()) {
            // field is off the bottom of the screen, so scroll down
            setVerticalScroll(field.getTop() - getVisibleHeight() + getRowHeight());
         }          
      }
   }
}

最后,我的CustomListRow类代表了一行:

public class CustomListRow extends Manager implements FieldChangeListener {

   private static final int _MAX_ROW_HEIGHT = 60;   
   private ListRander _data;
   private BitmapField _thumb;
   private LabelField _title;
   private FocusableBitmapField _star;
   private static final Bitmap _starImg = Bitmap.getBitmapResource("star.png");
   private static final Bitmap _bgImg = Bitmap.getBitmapResource("listing_bg.png");
   private SeparatorField _separator;
   private int _fontColor = Color.BLACK;
   private boolean _highlighted = false;
   private int _width;

   // subclass exists to expose focus methods (make public)
   private class FocusableBitmapField extends BitmapField {
      public FocusableBitmapField() {
         super(_starImg, BitmapField.FOCUSABLE | BitmapField.EDITABLE);    
      }
      public void onFocus(int direction) {
         super.onFocus(direction);
      }
      public void onUnfocus() {
         super.onUnfocus();
      }
   }

   public CustomListRow(ListRander data) {
      super(Field.FOCUSABLE | Manager.NO_VERTICAL_SCROLL | Manager.NO_VERTICAL_SCROLLBAR);

      setBackground(BackgroundFactory.createBitmapBackground(_bgImg));
      _width = Display.getWidth();

      long labelStyle = (DrawStyle.LEFT | DrawStyle.TOP | DrawStyle.ELLIPSIS);
      _title = new LabelField("", labelStyle) {   // custom anonymous class to change font color
         protected void paint(Graphics g) {
            int c = g.getColor();
            g.setColor(_fontColor);
            super.paint(g);
            g.setColor(c);
         }
      };
      _title.setFont(Font.getDefault().getFontFamily().getFont(Font.PLAIN, 24));

      _thumb = new BitmapField();

      _star = new FocusableBitmapField();
      _star.setChangeListener(this);

      _separator = new SeparatorField() {  // custom anonymous class to change separator color
         protected void paint(Graphics g) {
            int c = g.getColor();
            g.setColor(Color.GRAY);
            super.paint(g);
            g.setColor(c);
         }
      };

      setData(data);

      add(_thumb);
      add(_title);
      add(_star);
      add(_separator);
   }

   public ListRander getData() {
      return _data;
   }

   public void setData(ListRander value) {
      if (value != _data) {
         _data = value;

         _title.setText(value.getTitle());
         _thumb.setBitmap(value.getThumb());
      }
   }

   private void onStarClicked() {
      Dialog.alert("Star has been clicked or tapped!");
   }

   private void onRowClicked() {
      Dialog.alert("Row has been clicked or tapped!");
   }

   public void fieldChanged(Field field, int context) {
      if (field == _star) {
         onStarClicked();
      }
   }  

   public boolean navigationClick(int status, int time) {
      if (_star.isFocus()) {
         onStarClicked();
         return true;
      } /* else {
            onRowClicked();
            return true;
        } */
      return false;  // we will not consume this event
   }

   protected void highlight(boolean onRow) {
      _fontColor = onRow ? Color.WHITE : Color.BLACK;  // change font color for contrast
      _highlighted = onRow;         
      invalidate();
   }

   protected void onFocus(int direction) {
      // called when focus first transfers to this row, from another Field
      if (direction == 1) {
         // coming from top to bottom, we highlight the row first, not the star  
         highlight(true);
      } else if (direction == -1) {
         // coming from bottom to top, we highlight the star button first, not the row
         _star.onFocus(direction);
         highlight(false);
      }
   }  

   protected void onUnfocus() {
      // remove highlighting of the row, if any
      highlight(false);
      super.onUnfocus();
   }

   protected int moveFocus(int amount, int status, int time) {
      // called when this row already has focus (either on row, or star button)
      if (amount > 0) {
         // moving top to bottom
         if (!_star.isFocus()) {
            // we were on the row, now move to the star button
            _star.onFocus(1);
            highlight(false);
            amount--;             // consume one unit of movement
         }
      } else {
         // moving from bottom to top
         if (_star.isFocus()) {
            // we were on the star button, now move back over to the row
            _star.onUnfocus();
            highlight(true);
            amount++;             // consume one unit of movement
         }
      }
      return amount;
   }

   protected boolean touchEvent(net.rim.device.api.ui.TouchEvent event) {
      // We take action when the user completes a click (a.k.a. unclick)
      int eventCode = event.getEvent();
      if ((eventCode == TouchEvent.UNCLICK) || (eventCode == TouchEvent.DOWN)) {
         // Get the touch location, within this Manager
         int x = event.getX(1);
         int y = event.getY(1);

         if ((x >= 0) && (y >= 0) && (x < _width) && (y < _MAX_ROW_HEIGHT)) {
            int field = getFieldAtLocation(x, y);
            if ((field >= 0) && (getField(field) == _star)) {
               // Let event propagate to (star) button field
               return super.touchEvent(event);
            } else {
               if (eventCode == TouchEvent.UNCLICK) {
                  // A completed click anywhere else in this row should popup details for this selection
                  fieldChangeNotify(1);
                  onRowClicked();
               } else {
                  // This is just a soft touch (TouchEvent.DOWN), without full click
                  setFocus();
               }
               // Consume the event
               return true;
            }
         }
      }
      // Event wasn't for us, let superclass handle in default manner
      return super.touchEvent(event);
   }

   protected void sublayout(int width, int height) {
      height = Math.min(getPreferredHeight(), height);
      setExtent(_width, height);

      final int margin = 5;

      int thumbWidth = _thumb.getPreferredWidth();
      layoutChild(_thumb, thumbWidth, _thumb.getPreferredHeight());
      setPositionChild(_thumb, margin, margin);

      int starWidth = _star.getPreferredWidth();
      int starHeight = _star.getPreferredHeight();
      layoutChild(_star, starWidth, starHeight);
      setPositionChild(_star, width - starWidth - margin, (height - starHeight) / 2);

      // this assumes you want margin between all fields, and edges
      layoutChild(_title, width - thumbWidth - starWidth - 4 * margin, _title.getPreferredHeight());
      setPositionChild(_title, margin + thumbWidth /* + margin */, margin);  // TODO?
   }    

   protected void paintBackground(Graphics g) {
      super.paintBackground(g);
      if (_highlighted) {
         // you can't override drawFocus() for a Manager, so we'll handle that here:
         int oldColor = g.getColor();
         int oldAlpha = g.getGlobalAlpha();
         XYRect rect = new XYRect();
         g.setGlobalAlpha(150); 
         g.setColor(Color.BLUE); 
         getFocusRect(rect); 
         drawHighlightRegion(g, HIGHLIGHT_FOCUS, true, rect.x, rect.y, rect.width, rect.height);
         g.setGlobalAlpha(oldAlpha);
         g.setColor(oldColor);
      }
   }

   public int getPreferredWidth() {
      return _width;
   }

   public int getPreferredHeight() {
      return _MAX_ROW_HEIGHT;
   }
}

用法

您可以使用整个列表字段(可能在Screen类中):

public class ListScreen extends MainScreen implements FieldChangeListener {
   public ListScreen() {
      try {
         Vector data = new Vector();
         Bitmap icon = Bitmap.getBitmapResource("list_icon.png");
         for (int i = 0; i < 15; i++) {
            ListRander lr = new ListRander("Product Name " + i, icon);
            data.addElement(lr);
         }
         CustomListField list = new CustomListField(data);
         add(list);
         list.setChangeListener(this);
      } catch (Exception e) {
         e.printStackTrace();
      }
   }

   public void fieldChanged(Field field, int context) {
      if (field instanceof CustomListRow) {
         CustomListRow row = (CustomListRow) field;
         Dialog.alert(row.getData().getTitle() + " was selected!");
      }      
   }    
}

在我的应用中,CustomListRow本身处理等同于您的星形点击是有意义的。但是,对我来说, 点击是否有意义。所以,我允许你在FieldChangeListener本身设置一个CustomListField,当选择任何行时会回调它。请参阅我的屏幕类中的上述示例。如果你想处理行,也可以点击里面的 CustomListRow类,那很好。我在那里列出了onRowClicked()方法。在代码中搜索已注释掉的位置,并且可以重新激活该方法(onRowClicked())。

问题

  • 我的应用不需要列表搜索。我列出了一个示例实现,就像ListField一样。但是,我没有测试它。如果你需要,这是你的工作。我刚开始使用CustomListField实现(请参阅indexOfList())。
  • 我没看到你的“导航栏”是为了什么。条形图通常是全角项目,如状态栏或工具栏。我的截图中没有看到类似的内容。导航可能是每行右侧的一个小箭头,以显示详细信息。但是,我在你的截图中也没有看到。所以,我忽略了那段代码。如果你需要一个导航栏,你显然知道它应该是什么,并且可以将它添加到我上面的代码中。
  • 我不知道你是否只是将星星作为行背景图像的一部分添加,或者你是否有一个单独的图像。我添加了一个单独的 star.png 来代表明星。我会假设点击星星填充它,或突出显示它或其他东西。但是,你没有描述这个问题,所以我认为你可以解决这个问题。如果您需要自定义字段来表示星标,可以选择选择取消选择图片,只需将其作为新问题发布。
  • 你有一些代码似乎试图将行宽设置为行高的3倍,但这与你的屏幕截图不匹配。无论如何,大多数列表都是全屏宽度。所以,我删除了该代码。我的CustomListRow类实现getPreferredWidth()并请求全屏宽度。如果你愿意,可以改变。

答案 1 :(得分:6)

与Android的ListView不同,BB的ListField并非设计为列表项中的可聚焦/可点击字段 。因此,任何解决此问题的尝试都会产生一些负面影响。

相对简单/快速的解决方法是切换到VerticalFieldManager(检查this other stack overflow question)。但如果列表太长(我相信超过几百个),你就有可能“吃掉”太多的记忆。

如果该应用仅适用于触摸屏,那么您可以尝试使用ListField +对触摸事件坐标进行一些手动跟踪。因此,当您检测到列表字段单击时(以通常的方式执行),您可以检查触摸坐标是否对应于星形图像区域(至少在X轴上)。我不会发明/提供一个实现,只是给出一个想法。