Событие клика по полю пользовательского списка

Я пишу одно приложение, в котором я создал настраиваемое поле списка для отображения списка. мой CustomListField содержит одно изображение и текст в строке. Я получаю слушателя смены поля по щелчку строки списка, но я хочу также поместить слушателя смены поля на изображение... Может кто-нибудь сказать мне, как я могу это сделать.

вот мой код

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);
    }    
*/
}

Я хочу поместить список щелчков на звездное изображение строки списка полей

и следующее - вывод вышеуказанного кода.

2 ответа

Решение

Я сделал что-то очень похожее на это в прошлом проекте:

Фон

Как сказал Архимед в своем ответе, и как вы можете прочитать об этом на форумах BlackBerry здесь, вы не можете иметь полноценные 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() и запрашивает полную ширину экрана. Измените, если хотите.

В отличие от Android ListView BB ListField не предназначен для фокусируемых / кликабельных полей внутри элементов списка. Поэтому любая попытка обойти это будет иметь некоторые негативные побочные эффекты.

Относительно простым / быстрым обходным путем было бы переключиться на VerticalFieldManager (проверьте этот другой вопрос переполнения стека). Но если список слишком длинный (я полагаю, более нескольких сотен), вы рискуете "съесть" слишком много памяти.

Если приложение предназначено только для сенсорных экранов, то вы можете попробовать остаться с ListField + сделать ручное отслеживание координат сенсорного события. Поэтому, когда вы обнаруживаете щелчок в поле списка (как обычно), вы можете проверить, соответствуют ли координаты касания области изображения звезды (по крайней мере, на оси X). Я не собираюсь придумывать / предоставлять реализацию, а просто дать идею.

Другие вопросы по тегам