【问题标题】:BlackBerry 6/7 Table Layout with ColSpan - Number Selection WidgetBlackBerry 6/7 Table Layout with ColSpan - Number Selection Widget
【发布时间】:2012-06-09 23:57:48
【问题描述】:

尝试为 BlackBerry 6/7 制作一个简单的数字点击器控件,如下所示:

本质上它只是一个文本字段和两个按钮,并有一个管理器将它们分开。

我知道不支持的附加 TableManager,但它不支持列扫描。而且,我发现使用深度嵌套的 Manager 的想法......令人不安。

而且,这会出现多次,所以我想要一个简单、可重用的组件。

因此,我构建了一个简单的管理器来包含这三个组件,甚至允许您出于文体原因提供自己的文本字段或按钮。代码附在下面。显然比它需要的更漂亮,但工作都是在子布局中完成的。

实际发生的情况是 3 个组件中的每一个的右上角都出现在正确的位置,但是 3 个组件被“收缩包装”到显示其内容所需的最小尺寸,忽略了请求的 USE_ALL_WIDTH 和 USE_ALL_HEIGHT。这可能是一个小问题,但我怎样才能让这些组件真正使用所有宽度和使用所有高度?我在 USE_ALL_* 上尝试了几种变体,但还没有找到获胜的变体。当然也欢迎任何其他改进。

谢谢。

package layout;

import net.rim.device.api.system.Display;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.Manager;
import net.rim.device.api.ui.XYEdges;
import net.rim.device.api.ui.component.ButtonField;
import net.rim.device.api.ui.component.EditField;

/**
 * XXX BROKEN DO NOT USE YET - layout fail, components get shrink-wrapped.
 * 
 * NumberClicker Makes a layout with three components, like this:
 * <pre>
 * +-------------------+ +-------------------+
 * |                   | |          +        |
 * |         3         | |-------------------|
 * |                   | |-------------------|
 * |                   | |          -        |
 * |-------------------| |-------------------|
 * </pre>
 * Note that by default, the buttons are set to increment and decrement the number in the textfield!
 * @author Ian Darwin
 */
public class NumberClicker extends Manager {

    private static final long SUBCOMPONENT_STYLE = Field.USE_ALL_HEIGHT | Field.USE_ALL_WIDTH;
    private static final long MANAGER_STYLE = Field.FIELD_HCENTER | Field.FIELD_VCENTER;

    final XYEdges MARGINS = new XYEdges(10,10,10,10);

    EditField number = new EditField(SUBCOMPONENT_STYLE);
    ButtonField plus = new ButtonField("+", SUBCOMPONENT_STYLE);
    ButtonField minus = new ButtonField("-", SUBCOMPONENT_STYLE);

    public NumberClicker() {
        this(MANAGER_STYLE);
    }
    public NumberClicker(long style) 
    {
        this(null, null, null, style);
    }

    /** Constructor allows you to provide your own three fields */
    public NumberClicker(EditField number, ButtonField plus, ButtonField minus) { 
        this(number, plus, minus, MANAGER_STYLE);
    }

    /** Constructor allows you to provide your own three fields ANd override style.
     * If any of the fields is null, the default value is used.
     */
    public NumberClicker(EditField number, ButtonField plus, ButtonField minus, long style) {
        super(style);
        if (number != null) {
            this.number = number;
        } else {
            this.number.setMargin(MARGINS); // set margins on our default, constructed above.
        }
        setValue(1);
        add(this.number); // Nulls allowed, so must be careful to use "this." throughout this method.

        if (plus != null) {
            this.plus = plus;
        } else {
            this.plus.setMargin(MARGINS);
        }
        add(this.plus);

        if (minus != null) {
            this.minus = minus;
        } else {
            this.minus.setMargin(MARGINS);
        }
        add(this.minus);

        this.plus.setRunnable(new Runnable() {
            public void run() {
                increment();
            }           
        });
        this.minus.setRunnable(new Runnable() {
            public void run() {
                decrement();
            }           
        });
    }

    public void increment() {
        number.setText(Integer.toString(Integer.parseInt(number.getText().trim()) + 1));
    }

    public void decrement() {
        number.setText(Integer.toString(Integer.parseInt(number.getText().trim()) - 1));
    }

    /** Return the integer value of the clicker. Do not call if you are re-using this as a three-component layout manager! */
    public int getValue() {
        return Integer.parseInt(number.getText().trim());
    }

    public void setValue(int value) {
        number.setText(Integer.toString(value));
    }

    /** 
     * Compute sizes and positions of subfields.
     * 
     * Required by Manager 
     */
    public void sublayout(int width, int height)  {
        int layoutWidth = width;
        int layoutHeight = Math.min(height, Display.getHeight()); // no scrolling here
System.err.println("Display:" + Display.getWidth() + "x" + Display.getHeight());

        int halfX = layoutWidth / 2;
        int halfY = layoutHeight / 2;
System.err.println("sublayout:" + width + "," + height + "; " + halfX + "," + halfY);

        int numberWidth = halfX - number.getMarginLeft() - number.getMarginRight();
        int numberHeight = layoutHeight - number.getMarginTop() - number.getMarginBottom();
        layoutChild(number, numberWidth, numberHeight);
        setPositionChild(number, 0 + number.getMarginLeft(), 0 + number.getMarginTop());
System.err.println(number + " " + numberWidth + "," + numberHeight + " " +number.getMarginLeft());

        int plusWidth = halfX - plus.getMarginLeft() - plus.getMarginRight();
        int plusHeight = halfY - plus.getMarginTop() - plus.getMarginBottom();
        layoutChild(plus, plusWidth, plusHeight);
        setPositionChild( plus, halfX + plus.getMarginLeft(), plus.getMarginTop());

        int minusWidth = halfX - minus.getMarginLeft() - minus.getMarginRight();
        int minusHeight = halfY - minus.getMarginTop() - minus.getMarginBottom();
        layoutChild(minus, minusWidth, minusHeight);
        // Use plus.getMarginHeight() for better alignment.
        setPositionChild( minus, halfX + plus.getMarginLeft(), halfY + minus.getMarginTop() );

        //setVirtualExtent(layoutWidth, height);
        setExtent(layoutWidth, height);
    }

    public EditField getNumberField() {
        return number;
    }
    public void setNumberField(EditField number) {
        this.number = number;
    }
    public ButtonField getPlusField() {
        return plus;
    }
    public void setPlusField(ButtonField plus) {
        this.plus = plus;
    }
    public Field getMinusField() {
        return minus;
    }
    public void setMinusField(ButtonField minus) {
        this.minus = minus;
    }
}

【问题讨论】:

    标签: layout blackberry


    【解决方案1】:

    最接近你想要达到的目标是

    几点说明:

    1. EditField 始终使用USE_ALL_WIDTH。无论您是否请求它都没有关系。因此,如果你想限制它的宽度,你已经覆盖了它的layout() 方法。在我的代码 sn-p 中,它的宽度受到该字段值允许的最大字符数的限制(请参阅CustomEditField)。

    2. ButtonField 忽略 USE_ALL_WIDTH 和 USE_ALL_HEIGHT。它的范围仅取决于按钮内的文本。为了达到USE_ALL_WIDTH的效果,你必须给它添加水平内边距。

    3. 很遗憾,如果您想实现 USE_ALL_HEIGHT 效果,填充技巧将不起作用。当您向按钮添加垂直填充时,在某个阶段它会垂直重复其背景。如果需要,您必须为其编写自定义按钮字段。

    还可以查看 BlackBerry 的高级 UI 组件 at this page。

    代码如下:

    import net.rim.device.api.ui.Font;
    import net.rim.device.api.ui.FontMetrics;
    import net.rim.device.api.ui.Manager;
    import net.rim.device.api.ui.UiApplication;
    import net.rim.device.api.ui.XYEdges;
    import net.rim.device.api.ui.component.ButtonField;
    import net.rim.device.api.ui.component.EditField;
    import net.rim.device.api.ui.decor.Border;
    import net.rim.device.api.ui.decor.BorderFactory;
    import net.rim.device.api.ui.text.NumericTextFilter;
    
    public class NumberClicker extends Manager {
    
        private class CustomEditField extends EditField {
            public int getPreferredWidth() {
                FontMetrics fontMetrics = new FontMetrics();
                getFont().getMetrics(fontMetrics);
                return getMaxSize()*fontMetrics.getMaxCharWidth();
            };
    
            public int getPreferredHeight() {
                // forcing the field to be single lined
                return getFont().getHeight();
            }
    
            protected void layout(int width, int height) {
                super.layout(
                    Math.min(width, getPreferredWidth()),
                    Math.min(height, getPreferredHeight())
                );
            }
        }
    
        final XYEdges MARGINS = new XYEdges(2,2,2,2);
    
        EditField _number;
        Manager _numberManager;
    
        ButtonField _plus;
        ButtonField _minus;
    
    
        public NumberClicker() {
            super(0);
    
            Font font = getFont();
            font = font.derive(Font.BOLD, font.getHeight() + 10);
            _number = new CustomEditField();
            _number.setFilter(new NumericTextFilter());
            _number.setMaxSize(1);
            _number.setFont(font);
            setValue(1);
    
            _numberManager = new Manager(0) {
                protected void sublayout(int width, int height) {           
                    layoutChild(_number, width, height);
                    setPositionChild(_number, 
                        Math.max(0, (width - _number.getWidth())/2), 
                        Math.max(0, (height - _number.getHeight())/2)
                    );
                    setExtent(width, height);
                }
            };
    
            _numberManager.setBorder(BorderFactory.createRoundedBorder(new XYEdges()));
            _numberManager.setMargin(MARGINS);
            _numberManager.add(_number);
            add(_numberManager);
    
            _plus = new ButtonField("+", 0);
            _plus.setMargin(MARGINS);
            add(_plus);
    
            _minus = new ButtonField("-");
            _minus.setMargin(MARGINS);
            add(_minus);
    
            _plus.setRunnable(new Runnable() {
                public void run() {
                    increment();
                }           
            });
    
            _minus.setRunnable(new Runnable() {
                public void run() {
                    decrement();
                }           
            });
        }
    
        private void increment() {
            synchronized (UiApplication.getEventLock()) { //probably not needed here. overkill.
                _number.setText(Integer.toString(Integer.parseInt(_number.getText().trim()) + 1));
            }
        }
    
        private void decrement() {
            if (Integer.parseInt(_number.getText()) <= 0) {
                return;
            }
    
            synchronized (UiApplication.getEventLock()) { //probably not needed here. overkill.
                _number.setText(Integer.toString(Integer.parseInt(_number.getText().trim()) - 1));
            }
        }
    
        public void setValue(int value) {
            if (value < 0) {
                return;
            }
    
            synchronized (UiApplication.getEventLock()) { // MUST. can be called from non UI thread.
                _number.setText(Integer.toString(value));
            }
        }
    
        /** 
         * Compute sizes and positions of subfields.
         */
        public void sublayout(int width, int height)  {
            int heightUsed = 0;
            int halfX = width / 2;
    
            Border border = _plus.getBorder();
            int plusWidth = halfX - _plus.getMarginLeft() - _plus.getMarginRight();
            int plusHeight = height - _plus.getMarginTop() - _plus.getMarginBottom();
    
            // calculate horizontal padding so the button will look like USE_ALL_WIDTH
            int plusHPadding = (Math.max(0, plusWidth - _plus.getPreferredWidth() - border.getLeft() - border.getRight()))/2;
            _plus.setPadding(0, plusHPadding, 0, plusHPadding);
    
            layoutChild(_plus, plusWidth, plusHeight);
            setPositionChild( _plus, halfX + _plus.getMarginLeft(), _plus.getMarginTop());
            heightUsed += _plus.getHeight() + _plus.getMarginTop() + _plus.getMarginBottom();
    
            border = _minus.getBorder();
            int minusWidth = halfX - _minus.getMarginLeft() - _minus.getMarginRight();
            int minusHeight = height - _plus.getHeight() - _minus.getMarginTop() - _minus.getMarginBottom();
    
            // calculate horizontal padding so the button will look like USE_ALL_WIDTH
            int minusHPadding = (Math.max(0, minusWidth - _minus.getPreferredWidth() - border.getLeft() - border.getRight()))/2; 
            _minus.setPadding(0, minusHPadding, 0, minusHPadding);
    
            layoutChild(_minus, minusWidth, minusHeight);
            setPositionChild( _minus, halfX + _plus.getMarginLeft(), heightUsed + _minus.getMarginTop());
            heightUsed += _minus.getHeight() + _minus.getMarginTop() + _minus.getMarginBottom();
    
            int numberWidth = halfX - _numberManager.getMarginLeft() - _numberManager.getMarginRight();
            int numberHeight = heightUsed - _numberManager.getMarginTop() - _numberManager.getMarginBottom();
            layoutChild(_numberManager, numberWidth, numberHeight);
            setPositionChild(_numberManager, _numberManager.getMarginLeft(), _numberManager.getMarginTop());
    
            setExtent(width, heightUsed);
        }
    }
    

    【讨论】:

    • 太棒了,谢谢!我认为这将涉及各种组件的复杂性。当我们有一个一致的、相当完整的和更可预测的 GUI API 来使用时会很高兴,但遗憾的是它不会基于 Java(至少不是在 BlackBerry 平台上)。
    猜你喜欢
    • 2023-04-07
    • 1970-01-01
    • 2021-04-05
    • 1970-01-01
    • 2019-11-09
    • 1970-01-01
    • 1970-01-01
    • 2013-07-21
    相关资源
    最近更新 更多