0

I have a very strange problem at the moment.

Basically when I scroll in a screen, the fields don't get redrawn completely or consistently.

I have a Screen (NO_VERTICAL_SCROLL), with a manager as a titlebar. Below that I have a vertical field manager (VERTICAL_SCROLL) with labelfields. When I scroll the vfm one or two lines of the labelfields, which were already visible, get redrawn. The section I'm scrolling down to has absolutely nothing drawn.

I tried invalidate(), and calling doPaint in a scrollchangelistener, but its actually much worse. It results in the titlebar only being partially redrawn.

In the code below I used a custom FontManager, DimenManager, and ImageResourceManager to return values dependent on screen size. I used a custom BitmapButtonField and ClickableLabel in order to change the state of a field when a click is being held in.

public class BaseScreen extends MainScreen implements StringsResource
{
    protected ResourceBundle resources;

    public BaseScreen(long style)
    {
        super(style);

        StandardTitleBar titlebar = new StandardTitleBar();
        titlebar.addSignalIndicator();
        titlebar.addClock();
        titlebar.addNotifications();

        setTitle(titlebar);

        resources = ResourceBundle.getBundle(BUNDLE_ID, BUNDLE_NAME);
    }
}

public class TandCScreen extends BaseScreen
{
    final String copy_text1 = "long text here";
    final String copy_text2 = "even longer text here";

    ColoredLabelField label_title;
    ColoredLabelField label_subtitle;
    ColoredLabelField label1;
    ColoredLabelField label2;
    ClickableLabel label3;

    public TandCScreen()
    {
        super(NO_VERTICAL_SCROLL | NO_VERTICAL_SCROLLBAR);
        label_title = new ColoredLabelField(resources.getString(STRING_APP_NAME), Color.WHITE, DrawStyle.HCENTER | USE_ALL_WIDTH);
        label_title.setBackground(BackgroundFactory.createSolidBackground(0x60223b));
        label_subtitle = new ColoredLabelField(resources.getString(STRING_TANDC_TITLE), 0x58585b, DrawStyle.HCENTER | USE_ALL_WIDTH);

        label1 = new ColoredLabelField(copy_text1, 0x58585b, USE_ALL_WIDTH);
        label2 = new ColoredLabelField("", 0xa7a9ab, USE_ALL_WIDTH);

        label3 = new ClickableLabel("Read more...")
        {
            protected void unclick()
            {
                super.unclick();
                UiApplication.getUiApplication().invokeLater(new Runnable()
                {
                    public void run()
                    {
                        ColoredLabelField label = new ColoredLabelField(copy_text2, 0xa7a9ab, 0);
                        label.setFont(FontManager.body());
                        label.setMargin(0, 0, DimenManager.interField(), 0);
                        label2.getManager().replace(label2, label);
                        label3.getManager().delete(label3);
                    }
                });
            }
        };

        label_title.setFont(FontManager.subtitle());
        label_subtitle.setFont(FontManager.subtitle());
        label1.setFont(FontManager.body());
        label2.setFont(FontManager.body());
        label3.setFont(FontManager.body());

        BitmapButtonField button_accept = new BitmapButtonField(ImageResourceManager.buttonAccept(), ImageResourceManager.buttonAcceptHover(), FIELD_HCENTER)
        {
            protected void click()
            {
                super.click();
                setImage(ImageResourceManager.buttonAcceptSelected());
                setFocusImage(ImageResourceManager.buttonAcceptSelected());
            }

            protected void unclick()
            {
                super.unclick();
                PersistentStoreManager.setTandCAccepted(true);
                UiApplication.getUiApplication().pushScreen(new LoginScreen());
                close();
            }
        };

        BitmapButtonField button_decline = new BitmapButtonField(ImageResourceManager.buttonDecline(), ImageResourceManager.buttonDeclineHover(), FIELD_HCENTER)
        {
            protected void click()
            {
                super.click();
                setImage(ImageResourceManager.buttonDeclineSelected());
                setFocusImage(ImageResourceManager.buttonDeclineSelected());
            }

            protected void unclick()
            {
                super.unclick();
                close();
            }
        };

        int margin = (VariableManager.DISPLAY_WIDTH - button_accept.getPreferredWidth()) / 2;
        // calculate where to put ellipsis
        Font font = label2.getFont();
        int max_length = (VariableManager.DISPLAY_WIDTH - margin * 2) * 2;
        int i = copy_text2.length() - 1;
        while (font.getAdvance(copy_text2.substring(0, i)) + font.getAdvance("...") >= max_length)
            i--;

        label2.setText(copy_text2.substring(0, i).trim() + "...");

        VerticalFieldManager vfm = new VerticalFieldManager(VERTICAL_SCROLL | VERTICAL_SCROLLBAR);
        vfm.add(new NullField());
        vfm.add(label_subtitle);
        vfm.add(new Seperator());
        vfm.add(label1);
        vfm.add(label2);
        vfm.add(label3);
        vfm.add(button_accept);
        vfm.add(button_decline);

        vfm.setMargin(0, margin, 0, margin);

        // paddings
        int padding = (DimenManager.header() - label_title.getPreferredHeight()) / 2;
        label_title.setPadding(padding, 0, padding, 0);
        label_subtitle.setPadding(DimenManager.interField(), 0, DimenManager.interField(), 0);
        label1.setMargin(DimenManager.interField(), 0, DimenManager.interField(), 0);
        label3.setMargin(DimenManager.interField(), 0, DimenManager.interField(), button_accept.getPreferredWidth() - label3.getPreferredWidth());
        button_decline.setMargin(DimenManager.interField(), 0, DimenManager.interButton(), 0);

        add(label_title);
        add(vfm);
    }

    protected boolean onSavePrompt()
    {
        return false;
    }

    protected void makeMenu(Menu menu, int instance)
    {
        if (instance == Menu.INSTANCE_CONTEXT)
        {
            ContextMenu contextMenu = ContextMenu.getInstance();
            contextMenu.setTarget(this);
            contextMenu.clear();
            this.makeContextMenu(contextMenu);
            menu.deleteAll();
            menu.add(contextMenu);
        }
        else
        {
            super.makeMenu(menu, instance);
        }
    }
    protected void makeContextMenu(ContextMenu contextMenu)
    {
    }

    /**
     * Clickable labelfield which changes color on down press, and fires action
     * on release. Action is canceled if touch moves outside field bounds.
     * 
     * @author kevin
     * 
     */
    private class ClickableLabel extends LabelField
    {
        private boolean canceled = true;
        private boolean consumed = false;
        protected boolean pressed = false;

        public ClickableLabel(String label)
        {
            super(label, LabelField.FOCUSABLE | USE_ALL_WIDTH);
            setFont(FontManager.body());
        }

        protected void paint(Graphics g)
        {
            // background
            if (pressed)
            {
                g.setColor(0x2C1721);
            }
            else if (isFocus())
            {
                g.setColor(0x993C6B);
            }
            else
            {
                g.setColor(0x60223B);
            }

            int padding_y = (getPreferredHeight() - getFont().getHeight()) / 2;
            int padding_x = getPaddingLeft();
            g.drawText(getText(), padding_x, padding_y);
        }

        public int getPreferredHeight()
        {
            return ImageResourceManager.highlight().getHeight();
        }

        protected void layout(int width, int height)
        {
            height = getPreferredHeight();
            super.layout(width, height);
            setExtent(width, height);
        }

        // --------- Highlight selected row ---------
        protected void onFocus(int direction)
        {
            super.onFocus(direction);
            invalidate();
        }

        protected void onUnfocus()
        {
            super.onUnfocus();
            invalidate();
        }
        // --------------------------------------------

        protected void drawFocus(Graphics graphics, boolean on)
        {
        }

        /**
         * Called when trackpad pressed, or touchscreen touched
         */
        protected void click()
        {
            pressed = true;
            invalidate();
        }

        /**
         * Called when trackpad released, or touchscreen released
         */
        protected void unclick()
        {
            cancel();
        }

        protected void cancel()
        {
            pressed = false;
            invalidate();
        }

        protected boolean navigationClick(int status, int time)
        {
            if (status != 0)
            {
                if (consumed)
                {
                    consumed = false;
                }
                else
                {
                    click();
                }
            }
            return true;
        }

        protected boolean navigationUnclick(int status, int time)
        {
            if (status != 0)
            {
                if (consumed)
                    consumed = false;
                else
                    unclick();
            }
            return true;
        }

        protected boolean touchEvent(TouchEvent message)
        {
            int x = message.getX(1);
            int y = message.getY(1);
            if (x < 0 || y < 0 || x > getExtent().width || y > getExtent().height)
            {
                // Outside the field
                if (!canceled)
                {
                    cancel();
                }
                canceled = true;
                return false;
            }

            if (message.getEvent() == TouchEvent.UP)
            {

                if (canceled)
                    cancel();
                else
                    unclick();
                consumed = true;

                return true;
            }
            if (message.getEvent() == TouchEvent.DOWN)
            {
                click();
                consumed = true;
                canceled = false;

                return true;
            }

            return super.touchEvent(message);
        }
    }

    private class Seperator extends SeparatorField
    {
        protected void paint(Graphics graphics)
        {
            graphics.setColor(0xa7a9ab);
            super.paint(graphics);
        }
    }
}

Thanks in advance for any suggestions

Kevin
  • 1,626
  • 16
  • 30
  • We can't tell you what's wrong with your code without seeing the actual code. Please **edit** the question to paste in the important parts. Thanks. – Nate Dec 03 '13 at 10:47
  • May we see the paint methods for the Fields that are not being drawn correctly? – Peter Strange Dec 03 '13 at 11:58
  • Hi peter, i managed to find the issue. but not able to mark this as answered though :/. thanks a lot for the effort. – Kevin Dec 03 '13 at 12:09
  • @Kevin, I think there's a waiting period on accepting your own answers, but if your posted answer really is the solution, it's preferable to accept it, for the benefit of people reading this later. So, I'd just try back tomorrow. Thanks. – Nate Dec 04 '13 at 05:56
  • Agreed, I've tried yesterday but there is a 2 day waiting period. – Kevin Dec 04 '13 at 06:21

2 Answers2

0

I don't think it is possible to do anything but guess at your problem without looking at your code. But whatever your problem is, I am confident that it is based on a misunderstanding of how to use Managers within a Screen. So I recommend that you review the following articles to improve your knowledge in this area and so hopefully resolve the problem yourself:

Start here: UI Introduction This provides the Background around Managers and Fields.

Then read this article: MainScreen Explained

I suspect as a result of reading this article, you may be able to discard your 'title bar' and use setTitle() or setBanner() to provide this function.

I hope this resolves your problems.

A few other points:

In all my years of BB programming, I have never had to use doPaint() to get something painting the way I wanted. I can't think of a situation that this will in fact help. So if you think you need it, try invalidate() instead.

I have used invalidate() when I making a change to the Field that will change its on screen appearance (but not its size). I have used it in a scroll change listener. But it is a method of last resort.

Remember that LabelFields are not focusable, so in OS's before 6, that made them a problem to scroll.

Peter Strange
  • 2,640
  • 11
  • 11
  • hi peter, thanks for the response. I can't use setTitle() since I'm already using that you display the clock/battery/network/etc. I did try invalidate() before doPaint(), neither worked. I'm targeting OS6 and 7. – Kevin Dec 03 '13 at 10:35
  • You can use setBanner() and setTitle(). So two non scrolling areas at the top of the screen. – Peter Strange Dec 03 '13 at 11:49
0

Found the issue. On the labelfields, there was a setExtent(getPreferredWidth(), getPreferredHeight()); that was reducing the size of the area to redraw. Very stupid mistake.

Thanks to everyone who tried to help.

Kevin
  • 1,626
  • 16
  • 30