自定义列表字段单击事件

即时编写一个应用程序,我已创建自定义列表字段用于显示列表视图。 我的CustomListField包含一行中的一个图像和文本。 im gettiing字段更改监听器点击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); } */ } 

我想把clickClickner放在listfield行的星形图像上

以下是输出的代码。 在此处输入图像描述

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

背景

正如Arhimed在他的回答中所说的那样 ,正如你在黑莓论坛上所读到的那样,你无法在ListField拥有完善的Field对象。 ListField行的内容直接在drawListRow()作为文本和drawListRow()绘制。内容不是Field实例,因此不可聚焦。

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

然后我将每一行都sublayout()自己的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 (aka 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本身处理等效的星形点击是有意义的。 但是,对我来说,以这种方式处理单击是没有意义的。 所以,我让你在CustomListField本身上设置一个FieldChangeListener ,当选择任何行时会调用它。 请参阅我的屏幕类中的上述示例。 如果你想在CustomListRow类中处理行单击,那很好。 我在那里布局了一个onRowClicked()方法。 在代码中搜索已注释掉的位置,并且可以重新激活该方法( onRowClicked() )。

问题

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

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

一个相对简单/快速的解决方法是切换到VerticalFieldManager (检查其他堆栈溢出问题 )。 但如果列表太长(我相信超过几百个),你就有可能“吃掉”太多的记忆。

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