de.swm.commons.mobile.client.widgets.scroll.ScrollPanel.java Source code

Java tutorial

Introduction

Here is the source code for de.swm.commons.mobile.client.widgets.scroll.ScrollPanel.java

Source

/*
 * Copyright 2011 SWM Services GmbH.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package de.swm.commons.mobile.client.widgets.scroll;

import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Node;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.Widget;
import de.swm.commons.mobile.client.SWMMobile;
import de.swm.commons.mobile.client.base.PanelBase;
import de.swm.commons.mobile.client.event.*;
import de.swm.commons.mobile.client.utils.Utils;

/**
 * Scroll panel - has the ability to keep the {@link de.swm.commons.mobile.client.widgets.itf.IHeaderPanel} implementations always on the top.
 */
public class ScrollPanel extends PanelBase
        implements HasWidgets, DragEventsHandler, SwipeEventsHandler, IScrollPanel {

    private static final int TIME_FACTOR = 3000;
    private static final double DISTANCE_FACTOR = 0.25;
    private static final int DEFAULT_TRANSITION_DURATION = 500;
    private static final int OFFSET = 2;
    private boolean myHasTextBox = false;
    private IScrollMonitor scrollMonitor;

    private ScrollPanelEventsHandler scrollPanelEventsHandler;
    private int offsetHeight = -1;
    /**
     * Defines how many pixels a button overscrolling should be possible (e.g 50px).
     */
    private int overscrollButtonTolerance = 0;

    /**
     * Default constructor.
     */
    public ScrollPanel() {
        setStyleName(SWMMobile.getTheme().getMGWTCssBundle().getScrollPanelCss().scrollPanel());
    }

    /**
     * A scroll monitor provides callback when the scrolling is done.
     *
     * @param scrollMonitor
     */
    @Override
    public void setScrollMonitor(IScrollMonitor scrollMonitor) {
        this.scrollMonitor = scrollMonitor;
    }

    public void setHasTextBox(boolean hasTextBox) {
        myHasTextBox = hasTextBox && SWMMobile.getOsDetection().isAndroid();
    }

    public boolean getHasTextBox() {
        return myHasTextBox;
    }

    @Override
    public void onLoad() {
        DragController.get().addDragEventsHandler(this);
        DragController.get().addSwipeEventsHandler(this);
    }

    @Override
    public void onUnload() {
        DragController.get().removeDragEventsHandler(this);
        DragController.get().removeSwipeEventHandler(this);
    }

    @Override
    public Widget getWidget() {
        return myFlowPanel.getWidget(0);
    }

    public ScrollPanelEventsHandler getScrollPanelEventsHandler() {
        return scrollPanelEventsHandler;
    }

    /**
     * Neuen Handler fuer Scroll Panel Events registrieren.
     *
     * @param scrollPanelEventsHandler eventhandler
     */
    public void addScrollPanelEventsHandler(ScrollPanelEventsHandler scrollPanelEventsHandler) {
        this.scrollPanelEventsHandler = scrollPanelEventsHandler;
    }

    /**
     * Sets the button overscroll tolerance in px
     * @param overscrollButtonTolerance
     */
    public void setOverscrollButtonTolerance(int overscrollButtonTolerance) {
        this.overscrollButtonTolerance = overscrollButtonTolerance;
    }

    /**
     * Scrolls to the default position.
     */
    public void reset() {
        Utils.setTransitionDuration(getWidget().getElement(), 0);
        Utils.setTranslateY(getWidget().getElement(), 0);
    }

    /**
     * Scrolls to the top.
     */
    @Override
    public void setPostionToTop() {
        Utils.setTransitionDuration(getWidget().getElement(), 0);
        Utils.setTranslateY(getWidget().getElement(), 0);
    }

    /**
     * Scrolls to the button.
     */
    @Override
    public void setPositionToBottom() {
        Utils.setTransitionDuration(getWidget().getElement(), 0);
        Utils.setTranslateY(getWidget().getElement(),
                this.getElement().getClientHeight() - this.getElement().getScrollHeight());
    }

    /**
     * Sets the croll position
     *
     * @param pos the x axis pos
     */
    @Override
    public void setScrollPosition(int pos) {
        if (myHasTextBox) {
            setStyleTop(pos);
        } else {
            Element element = getWidget().getElement();
            Utils.setTranslateY(element, pos);
        }
    }

    /**
     * Returns the current scroll position.
     *
     * @return the position
     */
    @Override
    public int getScrollPosition() {
        if (myHasTextBox) {
            return getStyleTop();
        } else {
            Element element = getWidget().getElement();
            return Utils.getTranslateY(element);
        }
    }

    /**
     * Returns the next scroll position
     *
     * @return then next scroll position
     */
    @Override
    public int getScrollToPosition() {
        if (myHasTextBox) {
            return getStyleTop();
        } else {
            Element element = getWidget().getElement();
            return Utils.getMatrixY(element);
        }
    }

    @Override
    public void onDragStart(DragEvent e) {
        if (scrollMonitor != null) {
            scrollMonitor.onScrollStart();
        }
        int matrix = getScrollToPosition();
        int current = getScrollPosition();
        Utils.setTransitionDuration(getWidget().getElement(), 0);
        if (current != matrix) { // scroll on going
            int diff = current - matrix;
            int offset = diff > OFFSET ? OFFSET : diff > -OFFSET ? diff : -OFFSET;
            setScrollPosition(matrix + offset);
            DragController.get().suppressNextClick();
        }
    }

    @Override
    public void onDragMove(DragEvent e) {
        Element widgetEle = getWidget().getElement();
        int panelHeight = Utils.getHeight(this.getElement());
        int widgetHeight = calcOffsetHeight(widgetEle);
        int current = getScrollPosition();
        if (current > 0) {
            // exceed top boundary
            if (e.getOffsetY() > 0) { // resist scroll down.
                current += (int) (e.getOffsetY() / OFFSET); // need the cast for production mode.
            } else {
                current += e.getOffsetY() * OFFSET;
            }
        } else if (-current + panelHeight > widgetHeight) {
            //correct only scroll position if overscroll is prohibited
            if (overscrollButtonTolerance == 0) {
                // exceed bottom boundary
                if (e.getOffsetY() < 0) { // resist scroll up.
                    current += (int) (e.getOffsetY() / OFFSET);
                } else {
                    current += e.getOffsetY() * OFFSET;
                }
            } else {
                //if button overscrolling is allowed
                current += e.getOffsetY();
            }
        } else {
            current += e.getOffsetY();
        }
        setScrollPosition(current);
    }

    private int calcOffsetHeight(Element widgetEle) {
        return ((this.offsetHeight > 0) ? this.offsetHeight : widgetEle.getOffsetHeight());
    }

    /**
     * A value greater zero will set the offset height of the panel manually.
     * Otherwise it will be calculated automatically.
     *
     * @param offsetHeight the offset height.
     */
    @Override
    public void setOffsetHeight(int offsetHeight) {
        this.offsetHeight = offsetHeight;
    }

    @Override
    public void onDragEnd(DragEvent e) {
        Element widgetEle = getWidget().getElement();
        if (scrollMonitor != null) {
            scrollMonitor.onScrollEnd();
        }
        int current = getScrollPosition();
        if (current == 0) {
            return;
        }
        int panelHeight = Utils.getHeight(this.getElement());
        int widgetHeight = getHeightOfElementOrChildElements(widgetEle);

        if (current > 0 // exceed top boundary
                || panelHeight > widgetHeight) {

            // fire eventshandler for top boundary
            if (getScrollPanelEventsHandler() != null) {
                getScrollPanelEventsHandler().onTop(e);
            }

            Utils.setTransitionDuration(widgetEle, DEFAULT_TRANSITION_DURATION);
            setScrollPosition(0);
        } else if (-current + panelHeight > widgetHeight) { // exceed bottom boundary

            // fire eventshandler for bottom boundary
            if (getScrollPanelEventsHandler() != null) {
                getScrollPanelEventsHandler().onBottom(e);
            }

            Utils.setTransitionDuration(widgetEle, DEFAULT_TRANSITION_DURATION);
            setScrollPosition((panelHeight - overscrollButtonTolerance) - widgetHeight);
        }
    }

    @Override
    public void onSwipeVertical(SwipeEvent e) {
        Element widgetEle = getWidget().getElement();
        int panelHeight = Utils.getHeight(this.getElement());
        int widgetHeight = widgetEle.getOffsetHeight();
        long current = getScrollPosition();
        if ((current >= 0) // exceed top boundary
                || (-current + panelHeight >= widgetHeight)) { // exceed bottom boundary
            return;
        }

        double speed = e.getSpeed();
        double timeFactor = TIME_FACTOR;
        long time = (long) Math.abs(speed * timeFactor);
        double dicstanceFactor = DISTANCE_FACTOR;
        long distance = (long) (speed * time * dicstanceFactor);
        // Utils.Console("speed " + speed + " time " + time + " distance " + distance + " current " + current);
        current += distance;
        // exceed top boundary?
        if (current > 0) {
            double timeAdj = 1 - (double) current / distance;
            time = (long) (time * timeAdj);
            current = 0;
        } else if (-current + panelHeight > widgetHeight) { // exceed bottom boundary
            long bottom = panelHeight - widgetHeight;
            double timeAdj = 1 - (double) (current - bottom) / distance;
            time = (long) (time * timeAdj);
            current = bottom;
        }
        Utils.setTransitionDuration(widgetEle, time);
        setScrollPosition((int) current);
    }

    @Override
    public void onSwipeHorizontal(SwipeEvent e) {
    }

    @Override
    public void add(Widget w) {
        super.add(w);
        if (SWMMobile.getOsDetection().isIOs()) {
            Utils.setTranslateY(w.getElement(), 0); // anti-flickering on iOS.
        }
    }

    /**
     * Returns the height of the given element. If the element has a height of 0 it searches
     * recursively for the height of the next visible child (e.g. the next visible slide of a
     * SliderPanel).
     *
     * @param element parent element
     * @return height of the element of a visible child
     */
    private int getHeightOfElementOrChildElements(Element element) {
        if (this.offsetHeight > 0) {
            return this.offsetHeight;
        }

        // current element has height? Return this height.
        if (element.getOffsetHeight() != 0) {
            return element.getOffsetHeight();
        }

        // current element is hidden? no height for this element.
        if (element.getStyle().getVisibility().equals("hidden") || element.getStyle().getDisplay().equals("none")) {
            return 0;
        }

        // search children for height
        NodeList<Node> children = element.getChildNodes();
        if (children.getLength() == 0) {
            return 0;
        }

        int height = 0;
        for (int i = 0; i < children.getLength(); i++) {
            Node currentNode = children.getItem(i);
            if (currentNode instanceof Element) {
                height = getHeightOfElementOrChildElements((Element) currentNode);
            }

            if (height != 0) {
                return height;
            }
        }

        return 0;
    }

    /**
     * Returns the top style
     *
     * @return the top style in px
     */
    private int getStyleTop() {
        Style style = getWidget().getElement().getStyle();
        String top = style.getTop();
        if (top.isEmpty()) {
            return 0;
        } else {
            return Integer.parseInt(top.replace("px", ""));
        }
    }

    /**
     * Sets the top stype in pixel
     *
     * @param top the top syle
     */

    private void setStyleTop(int top) {
        Style style = getWidget().getElement().getStyle();
        style.setTop(top, Unit.PX);
    }

}