4

リストビューを表示するためのカスタムリストフィールドを作成したアプリケーションを1つ書いています。私の CustomListField には、1 つの画像とテキストが連続して含まれています。リストフィールド行のクリックでフィールド変更リスナーを取得していますが、フィールド変更リスナーも画像に配置したい..どうすればそれができるか教えてもらえますか。

これが私のコードです。

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

リストフィールド行のスター画像にクリックリスナーを配置したい

以下は上記のコードの出力です。 ここに画像の説明を入力

4

2 に答える 2

7

私は過去のプロジェクトでこれと非常によく似たことをしました:

バックグラウンド

Arhimed が彼の回答で述べたように、また BlackBerry フォーラムで読むことができるようFieldに、ListField. 行のコンテンツは、テキストやビットマップなどとしてListField直接描画されるだけです。コンテンツはインスタンスではないため、フォーカスできません。drawListRow()Field

それで、私がしたことListFieldは、のサブクラスに置き換えることでしたManager。もともと、私は を使用してVerticalFieldManagerいましたが、それで問題が発生しました。また、スタック オーバーフローに関する多くの問題も見てきました。そこでは、人々が をサブクラス化VerticalFieldManagerし、1 つの小さな動作だけをカスタマイズすると、すべてが壊れ始めます。VerticalFieldManager通常の動作を受け入れるとうまく機能するように思えます。さらに何かが必要な場合は、Manager直接拡張するだけです。垂直に積み重ねられた行のレイアウトを実行するのは非常に簡単です。

次に、各行を独自Managerに作成し、カスタム レイアウトを実装しsublayout()て、行の を必要な場所に配置しFieldました。次に、行をフォーカス可能にし、行のビットマップ/ボタンを個別にフォーカス可能にすることもできます(星のように)。行をクリックすると 1 つのアクションが呼び出され、星をクリックすると別のアクションが呼び出されます。

ただし、私のアプリでは、10 ~ 20 行しかないため、パフォーマンスは問題ではありませんでした。また、あなたの例に合わせてコードを変更する必要があったため、このコードは軽くテストしただけだと考えてください。ただし、私はそれをアプリに組み込んだので、私の仮定とあなたの説明が有効である限り、問題なく動作するはずです.

実装

まず、あなたが何であるかがわかりませんでしたListRander(そのコードを示していませんでした)。ただし、私のコードでは、1 つの行に関する詳細を含むデータ クラスが必要です。それがあなたが使用した方法のように見えた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最後に、1 つの行が私のクラスによって表されます。

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私のアプリでは、それ自体がスターのクリックに相当するものを処理することは理にかなっています。しかし、私にとっては、のクリックをそのように処理することは意味がありませんでした。そのため、任意の行が選択されたときにコールバックされる a自体を設定できます。上記の画面クラスの例を参照してください。クラスでも行クリックを処理したい場合は、それで問題ありません。そこで方法を打ち出しました。コード内でコメントアウトされている場所を検索すると、そのメソッドを実装して再アクティブ化できます ( )。FieldChangeListenerCustomListFieldCustomListRowonRowClicked()onRowClicked()

問題

  • 私のアプリはリスト検索を必要としませんでした。私はそのサンプル実装をレイアウトしましたListField。しかし、私はそれをテストしませんでした。必要に応じて、それがあなたの仕事です。実装を開始しましたCustomListField(「参考文献」を参照indexOfList())。
  • あなたの「ナビゲーションバー」の目的がわかりませんでした。バーは通常、ステータス バーやツールバーのような全幅のアイテムです。あなたのスクリーンショットにはそのようなものは見当たりません。ナビゲーションアイテムは、各行の右側にある小さな矢印で、詳細を表示します。しかし、あなたのスクリーンショットでもそれはわかりませんでした。だから、私はそのコードを無視しました。ナビゲーション バーが必要な場合は、それがどうあるべきかを明らかに知っているので、それを上記のコードに追加できます。
  • 行の背景画像の一部として星を追加しただけなのか、それとも別の画像を用意したのかわかりませんでした。星を表す別のstar.pngを追加しました。星をクリックすると、それが塗りつぶされたり、強調表示されたりすると思います。しかし、あなたはその問題を説明していないので、それを処理できると思います。星を表すカスタム フィールドが必要な場合は、選択した画像と選択していない画像を含めることができます。それを新しい質問として投稿してください。
  • 行の幅を行の高さの 3 倍に設定しようとしているように見えるコードがいくつかありましたが、それはスクリーン ショットと一致しませんでした。とにかく、ほとんどのリストは全画面幅です。だから、私はそのコードを削除します。私CustomListRowのクラスgetPreferredWidth()は全画面幅を実装して要求します。お好みで変更してください。
于 2012-07-18T08:07:21.063 に答える
6

Android とは異なりListView、BBはリスト項目ListFieldにフォーカス可能/クリック可能なフィールドを持つようには設計されていません。したがって、これを回避しようとすると、いくつかのマイナスの副作用が生じます。

比較的簡単/迅速な回避策は、に切り替えることです(この他のスタックオーバーフローの質問VerticalFieldManagerを確認してください)。しかし、リストが長すぎると (私は数百を超えると思います)、メモリを「食い尽くす」危険があります。

アプリがタッチ スクリーン専用に設計されている場合は、そのままにしてListField、タッチ イベント座標の手動追跡を行うことができます。そのため、リスト フィールドのクリックを (通常の方法で) 検出すると、タッチ座標が星の画像領域 (少なくとも X 軸上) に対応しているかどうかを確認できます。私は実装を発明/提供するつもりはありませんが、アイデアを提供するだけです.

于 2012-07-14T18:53:40.687 に答える