Create GL_ScrollView (OpenGl ES 2.0)

[ATTACH]89[/ATTACH]
Hi everyone.

I’m currently developing games for Android and IOS with OpenGl ES 2.0 and have some problems realizing my own ScrollView.
I looked around in the Internet for scrolling and panning but just couldn’t find anything to help me. I found a lot about scrolling the whole screen but what I want is just a scrolling function for part of the screen.

The Image I attached shows the example of three buttons in my GL_ScrollView and just two if them should be visible right now. When you scroll down you should be able to see more and more of the top button an less and less of the bottom button as they enter and leave the ScrollView. This scrollview should work not just with textures but also rectangles, circles and other drawable elements I put in there.

My Idea was that I can tell OpenGL somehow to just render everything withing a rectangle (the area of my scrollview) and then render the elements and everything outside won’t be rendered, something like.

// Enable to just render between P(0.0, 0.0) and P2(0.6, 0.5);
button1.draw();
button2.draw();
button3.draw();
//Disable selective rendering

Are there any commands that I can tell opengl that or do you have other Ideas?

thanks
Leo

This is it, I believe. Or stencil buffers for more advanced tricks.
https://www.opengl.org/wiki/Scissor_Test

Thank you that looks exactly like the function I need!

I’ll update after I got a working version :slight_smile:

So I got a working GL_ScrollView for Android using OpenGl ES 2.0

the code for drawing only a part of the screen:


            //Translate the custom OpenGL coordination system to device coordination system
            int scissorWidth = (int) (width * deviceScaleX);
            int scissorHeight = (int) (height * deviceScaleY);
            int scissorPosX = (int) (pos.x * deviceScaleX);
            int scissorPosY = (int) (devicePixelHeight - pos.y * deviceScaleY - scissorHeight); //the y-axis is reversed

            //USe GL_SCISSOR_TEST to limit the drawing to the size of the GL_ScrollView
            GLES20.glEnable(GLES20.GL_SCISSOR_TEST);
            GLES20.glScissor(scissorPosX, scissorPosY, scissorWidth, scissorHeight);

            //Draw every child on the scroll-pane
            for (int i = 0; i<children.size(); i++) {
                children.get(i).draw(movedProjectionMatrix);
            }

            GLES20.glDisable(GLES20.GL_SCISSOR_TEST);

Here is my GL_ScrollView (which is based on my GL_Shape which is attatched) for those who need some inspiration themselves. There are more classes I use which are not attached.
I’d also be greatful for feedback :slight_smile:

package com.madquiz.android.gameelements;

import com.madquiz.android.data.CGPoint;
import com.madquiz.android.objects.GL_Rectangle;
import com.madquiz.android.objects.GL_Shape;
import com.madquiz.android.util.MatrixHelper;
import android.content.res.Resources;
import android.graphics.Color;
import android.opengl.GLES20;
import android.util.DisplayMetrics;
import java.util.ArrayList;
import java.util.Calendar;

/**
 * Created by Leo K., LDK-Productions on 16.12.2015.
 * The GL_ScrollView is a container of GL_Shapes which are placed on a scroll-pane. The scroll-pane can be moved using touch gestures.
 */
public class GL_ScrollView extends GL_Shape {

    //private static String version = "V1.1";

    //Variables needed to distinguish between scroll and click gestures
    private static final int NORMAL_CLICK_DURATION = 200;
    private static float max_click_range = 12.0f;
    private boolean possibleClick = true;
    private boolean possibleScroll = true;

    //Used to convert virtual pixels to device independent pixels
    private float toDpi = 1.0f;

    //The dimensions of the GL_ScrollView
    private int width;
    private int height;

    //The dimensions of the scroll-pane
    private int paneWidth;
    private int paneHeight;

    private int currentOffsetX;
    private int currentOffsetY;

    private int touchDownX = 0;
    private int touchDownY = 0;

    private int tmpMoveX = 0;
    private int tmpMoveY = 0;

    //If true the dimensions of the scroll-pane will be generated automatically
    private boolean autoPaneDimension = true;

    //Variables needed for the use of GL_SCISSOR_TEST and other functions
    private float deviceScaleX;
    private float deviceScaleY;
    private float devicePixelHeight;
    private float virtualScreenHeight;

    private boolean disableVerticalScroll = false;
    private boolean disableHorizontalScroll = false;

    //Variables to determine which scrollbar is to scroll
    private static int SCROLL_NONE = 0;
    private static int SCROLL_HORIZONTAL = 1;
    private static int SCROLL_VERTICAL = 2;
    private static int SCROLL_BOTH = 3;
    private int scrollState = SCROLL_NONE;

    //Variables for the background
    private GL_Rectangle background = null;
    private GL_Rectangle horizontalScrollBar = null;
    private GL_Rectangle verticalScrollBar = null;
    private int scrollBarColor = Color.GRAY;

    //Variables to determine when scrollbars are visible
    private boolean showScrollbarsAlways = false;
    private boolean showScrollbarsWhileScrolling = true;

    //Variables to make scrollbars stay longer after scrolling ad show when first created
    private long startedShowingScrollbars;
    private boolean scrollbarShowtimeOver = false;
    private static int TIME_SHOW_SCROLLBARS_LONGER_MS = 1650;

    //Other constants
    private static final int SCROLLBAR_THICKNESS_PARAMETER =  72; //Increase to make scrollbar smaller, decrease to make it thicker...
    private static final int CLICK_TOLERANCE_PARAMETER = 38; //Increase to make tolerance smaller, decrease to make it wider...
    //The container of GL_Shapes contained in the GL_ScrollView
    private ArrayList<GL_Shape> children = new ArrayList<GL_Shape>();


    /**
     * The constructor to initialize the GL_ScrollView.
     * @param x The x top left position.
     * @param y The y top left position.
     * @param width The width dimension.
     * @param height The height dimension.
     * @param virtualScreenWidth The width of your virtual coordination system.
     * @param virtualScreenHeight The height of your virtual coordination system.
     * @param devicePixelWidth The width of real pixels of the device.
     * @param devicePixelHeight The height of real pixels of the device.
     */
    public GL_ScrollView(int x, int y, int width, int height, float virtualScreenWidth, float virtualScreenHeight, float devicePixelWidth, float devicePixelHeight) {
        super(x, y);
        this.width = width;
        this.height = height;
        initScrollView(virtualScreenWidth, virtualScreenHeight, devicePixelWidth, devicePixelHeight);
    }

    /*
     * Init method for the GL_ScrollView.
     */
    private void initScrollView(float virtualScreenWidth, float virtualScreenHeight, float devicePixelWidth, float devicePixelHeight) {
        this.deviceScaleX = devicePixelWidth/(virtualScreenWidth);
        this.deviceScaleY = devicePixelHeight/(virtualScreenHeight);
        this.devicePixelHeight = devicePixelHeight;
        this.virtualScreenHeight = virtualScreenHeight;
        GL_ScrollView.max_click_range = (int) (virtualScreenHeight/CLICK_TOLERANCE_PARAMETER);
        startedShowingScrollbars = Calendar.getInstance().getTimeInMillis();
        initToDpiValue();
        generateDimension();
    }

    /**
     * Initializes the toDpi value.
     * Note: toDpi may not convert values to real DPI, is just helps make it device independent.
     */
    public void initToDpiValue() {
        DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
        toDpi =  virtualScreenHeight / devicePixelHeight / (metrics.densityDpi / 160f);
    }

    /**
     * Sets the dimensions of the scroll-pane and deactivates automatic pane dimensions.
     * @param paneWidth The width of the scroll-pane where the children are on.
     * @param paneHeight The height of the scroll-pane where the children are on.
     */
    public void setPaneSize(int paneWidth, int paneHeight) {
        this.paneWidth = paneWidth;
        this.paneHeight = paneHeight;
        autoPaneDimension = false;
        generateScrollBars();
    }

    /**
     * Activates automatic pane dimensions.
     */
    public void activateAutoPaneSize() {
        this.autoPaneDimension = true;
        generateDimension();
    }

    /**
     * Adds a view element to the scroll-pane.
     * @param child The view element to be added.
     */
    public void add(GL_Shape child) {
        if (! children.contains(child)) {
            children.add(child);
            if (autoPaneDimension)
                generateDimension();
        }
    }

    /**
     * Removes a view element from the scroll-pane.
     * @param child The view element to be removed.
     */
    public void remove(GL_Shape child) {
        children.remove(child);
    }

    /**
     * This method automatically  generates the dimension of the scroll-pane so that every element is able to be visible.
     */
    private void generateDimension() {
        if (children.size() != 0) {
            int minX = Integer.MAX_VALUE;
            int minY = Integer.MAX_VALUE;
            int maxX = 0;
            int maxY = 0;

            //Find the min and max for both x and y
            for (GL_Shape child: children) {
                CGPoint tl = child.getPosTopLeft();
                CGPoint br = child.getPosBottomRight();
                if (tl.getX() < minX)
                    minX = tl.getX();
                if (tl.getY() < minY)
                    minY = tl.getY();
                if (br.getX() > maxX)
                    maxX = br.getX();
                if (br.getY() > maxY)
                    maxY = br.getY();
            }

            this.paneWidth = maxX - minX;
            this.paneHeight = maxY - minY;
            this.paneWidth += minX - pos.getX();
            this.paneHeight += minY - pos.getY();

            //If scroll-pane is smaller that the GL_ScrollView, extend it
            if (this.paneWidth < width)
                this.paneWidth = width;
            if (this.paneHeight < height)
                this.paneHeight = height;
        } else {
            this.paneWidth = width;
            this.paneHeight = height;
        }

        generateScrollBars();
    }

    @Override
    protected void generateVertexArray() {
        //abstract method which had to be implemented - not used
    }

    @Override
    public boolean checkTouchDown(int x, int y) {
        boolean isInside = super.checkTouchDown(x, y);
        if (isInside) {

            //If not 2D scrolling; scrolling - axis has not been determined
            if (scrollState != SCROLL_BOTH)
                scrollState = SCROLL_NONE;

            //Save start of touch event
            this.touchDownX = x;
            this.touchDownY = y;

            //Init the movement between touch events
            this.tmpMoveX = touchDownX;
            this.tmpMoveY = touchDownY;

            //This touch down can be a click or a scroll gesture
            this.possibleClick = true;
            this.possibleScroll = true;

            //Translate the coordinates to pane coordinates
            int xScroll = x + currentOffsetX;
            int yScroll = y + currentOffsetY;

            //Pass the touch event
            for (int i = 0; i<children.size(); i++) {
                children.get(i).checkTouchDown(xScroll, yScroll);
            }

            //Manage visibility of scrollbars
            scrollbarShowtimeOver = true;
            if(!showScrollbarsAlways && horizontalScrollBar != null)
                horizontalScrollBar.setVisible(true);
            if(!showScrollbarsAlways && verticalScrollBar != null)
                verticalScrollBar.setVisible(true);

        } else {
            //As the touch down was outside the GL_ScrollView it can't be a click or a scroll gesture
            this.possibleScroll = false;
            this.possibleClick = false;
        }
        return isInside;
    }

    @Override
    public boolean checkTouchUp(int x, int y) {
        boolean isInside = super.checkTouchUp(x, y);

        //Manage visibility of scrollbars
        if (!showScrollbarsAlways && showScrollbarsWhileScrolling) {
            //hide scrollbars in TIME_SHOW_SCROLLBARS_LONGER_MS
            scrollbarShowtimeOver = false;
            startedShowingScrollbars = Calendar.getInstance().getTimeInMillis();
        }

        if (isInside) {
            //Translate the coordinates to pane coordinates
            int xScroll = x + currentOffsetX;
            int yScroll = y + currentOffsetY;

            //Pass the touch event
            for (int i = 0; i<children.size(); i++) {
                children.get(i).checkTouchUp(xScroll, yScroll);
            }
        }
        return isInside;
    }

    @Override
    public boolean checkTouchMove(int x, int y) {
        boolean isInside = super.checkTouchMove(x, y);
        if (possibleClick) {
            //Check tolerances to help distinguish between touch and scroll
            if (Math.abs(touchDownX-x) > max_click_range*toDpi ||
                    Math.abs(touchDownY-y) > max_click_range*toDpi) { //If touch is not within click tolerance
                possibleClick = false;
                for (int i = 0; i < children.size(); i++) {
                    children.get(i).cancelTouch(); //Cancel touch so that no events are fired from any child
                }
            }
        }
        if (possibleClick && Calendar.getInstance().getTimeInMillis() - GL_MotionEvent.getTouchDownTime() <= NORMAL_CLICK_DURATION ||
                (disableHorizontalScroll && disableVerticalScroll)) { //click
            int xScroll = x + currentOffsetX;
            int yScroll = y + currentOffsetY;
            for (int i = 0; i<children.size(); i++) {
                children.get(i).checkTouchMove(xScroll, yScroll);
            }
        } else if(possibleScroll){ //scroll

            //If 2D scrolling is deactivated, determine whether to scroll horizontal or vertical
            if (scrollState == SCROLL_NONE) {
                if (Math.abs(tmpMoveX-x) > Math.abs(tmpMoveY-y) || disableVerticalScroll)
                    scrollState = SCROLL_HORIZONTAL;
                else
                    scrollState = SCROLL_VERTICAL;
            }

            //Scroll the distance between the last touch event
            if ((! disableHorizontalScroll) && scrollState != SCROLL_VERTICAL)
                currentOffsetX += tmpMoveX-x;
            if ((! disableVerticalScroll) && scrollState != SCROLL_HORIZONTAL)
                currentOffsetY += tmpMoveY-y;
            tmpMoveX = x;
            tmpMoveY = y;

            //Check boundaries
            if(currentOffsetX > paneWidth-width)
                scrollToRight();
            else if(currentOffsetX < 0)
                scrollToLeft();
            if(currentOffsetY > paneHeight-height)
                scrollToBottom();
            else if(currentOffsetY < 0)
                scrollToTop();

        }
        return isInside;
    }

    /**
     * This method manages the visibility of the scrollbars, after the touch-up event.
     */
    private void updateScrollbarTime() {
        if (!scrollbarShowtimeOver &&
                (Calendar.getInstance().getTimeInMillis() - startedShowingScrollbars > TIME_SHOW_SCROLLBARS_LONGER_MS)) {
            if (horizontalScrollBar != null)
                horizontalScrollBar.setVisible(false);
            if (verticalScrollBar != null)
                verticalScrollBar.setVisible(false);
            scrollbarShowtimeOver = false;
        }
    }

    /**
     * This method is to be called to draw the GL_ScrollView and it's components.
     * @param projectionMatrix The projection matrix for the vertex-shader to translate the custom coordination system to the OpenGl ES coordination system.
     */
    public void draw(float[] projectionMatrix) {

        if (!showScrollbarsAlways && showScrollbarsWhileScrolling) {
            updateScrollbarTime();
        }

        if (visible) {
            if (background != null)
                background.draw(projectionMatrix);

            //Movement matrix, to be multiplied with the projection matrix
            float[] m_matix = {
                    //Order x,      y,      z,      w
                    1.f,    0.f,    0.f,    0.f ,
                    0.f,    1.f,    0.f,    0.f ,
                    0.f,    0.f,    1.f,    0.f,
                    -currentOffsetX,  -currentOffsetY,  0.f,    1.f
            };

            //Multiply the matrices, based on your implementation you may have to change the order
            float[] movedProjectionMatrix = MatrixHelper.gldMultMatrix(projectionMatrix, m_matix);

            //Translate the custom OpenGL coordination system to device coordination system
            int scissorWidth = (int) (width * deviceScaleX);
            int scissorHeight = (int) (height * deviceScaleY);
            int scissorPosX = (int) (pos.x * deviceScaleX);
            int scissorPosY = (int) (devicePixelHeight - pos.y * deviceScaleY - scissorHeight); //the y-axis is reversed

            //USe GL_SCISSOR_TEST to limit the drawing to the size of the GL_ScrollView
            GLES20.glEnable(GLES20.GL_SCISSOR_TEST);
            GLES20.glScissor(scissorPosX, scissorPosY, scissorWidth, scissorHeight);

            //Draw every child on the scroll-pane
            for (int i = 0; i<children.size(); i++) {
                children.get(i).draw(movedProjectionMatrix);
            }

            GLES20.glDisable(GLES20.GL_SCISSOR_TEST);

            //Draw scrollbars if needed
            if (!disableVerticalScroll && verticalScrollBar != null && paneHeight != height) {
                int scrollBarPosition = height*currentOffsetY/paneHeight;
                //Movement matrix
                float[] m_matix2 = {
                        //Order x,      y,      z,      w
                        1.f,    0.f,    0.f,    0.f ,
                        0.f,    1.f,    0.f,    0.f ,
                        0.f,    0.f,    1.f,    0.f,
                        0,  scrollBarPosition,  0.f,    1.f
                };
                verticalScrollBar.draw(MatrixHelper.gldMultMatrix(projectionMatrix, m_matix2));
            }
            if (!disableHorizontalScroll && horizontalScrollBar != null && paneWidth != width) {
                int scrollBarPosition = width*currentOffsetX/paneWidth;
                //Movement matrix
                float[] m_matix2 = {
                        //Order x,      y,      z,      w
                        1.f,    0.f,    0.f,    0.f ,
                        0.f,    1.f,    0.f,    0.f ,
                        0.f,    0.f,    1.f,    0.f,
                        scrollBarPosition,  0,  0.f,    1.f
                };
                horizontalScrollBar.draw(MatrixHelper.gldMultMatrix(projectionMatrix, m_matix2));
            }

        }
    }

    public void scrollToTop() {
        currentOffsetY = 0;
    }
    public void scrollToBottom() {
        currentOffsetY = paneHeight-height;
    }
    public void scrollToLeft() {
        currentOffsetX = 0;
    }
    public void scrollToRight() {
        currentOffsetX = paneWidth-width;
    }

    @Override
    public boolean collidesWithPoint(int x, int y) {
        return ((x >= pos.getX() && x <= pos.getX()+width) && (y >= pos.getY() && y <= pos.getY()+height));
    }

    @Override
    public boolean collidesWithRectangle(int x, int y, int w, int h) {
        return (pos.x <= x + w &&
                pos.x + width > x &&
                pos.y <= y + h &&
                height + pos.y > y);
    }

    @Override
    public CGPoint getPosTopLeft() {
        return pos;
    }

    @Override
    public void setPosX(int x) {
        super.setPosX(x);
        if (background != null)
            background.setPosX(x);
    }

    @Override
    public void setPosY(int y) {
        super.setPosX(y);
        if (background != null)
            background.setPosX(y);
    }

    @Override
    public CGPoint getPosBottomRight() {
        return new CGPoint(pos.x + width, pos.y + height);
    }

    /**
     * Sets the transparency of the background.
     * @param color The color of the background, format #AARRGGBB
     * @param alpha Value between 0.0 (total transparency) and 1.0 (not transparent).
     */
    public void setBackgroundColor(int color, float alpha) {
        if (background == null)
            background = new GL_Rectangle(pos, width, height, color);
        else
            background.setColor(color);

        background.setAlpha(alpha);
    }

    /**
     * Sets the color of the scrollbars.
     * @param scrollBarColor
     */
    public void setScrollBarColor(int scrollBarColor) {
        this.scrollBarColor = scrollBarColor;
        if (horizontalScrollBar != null)
            horizontalScrollBar.setColor(this.scrollBarColor);
        if (verticalScrollBar != null)
            verticalScrollBar.setColor(this.scrollBarColor);
    }

    /**
     * Activates 2D scrolling.
     * This makes it possible to scroll horizontal and vertical at the same time.
     * This is deactivated by default.
     */
    public void enable2Dscrolling() {
        scrollState = SCROLL_BOTH;
    }

    /**
     * Deactivates 2D scrolling.
     * This makes it possible to only scroll horizontal or vertical per touchdown.
     */
    public void disable2Dscrolling() {
        scrollState = SCROLL_NONE;
    }

    public boolean isHorizontalScrollDisabled() {
        return disableHorizontalScroll;
    }

    public void disableHorizontalScroll() {
        this.disableHorizontalScroll = true;
    }

    public void enableHorizontalScroll() {
        this.disableHorizontalScroll = false;
    }

    public boolean isVerticalScrollDisabled() {
        return disableVerticalScroll;
    }

    public void disableVerticalScroll() {
        this.disableVerticalScroll = true;
    }

    public void enableVerticalScroll() {
        this.disableVerticalScroll = false;
    }

    /**
     * Activates/deactivates the visibility of the scrollbars only while scrolling.
     * @param showScrollbars
     */
    public void showScrollbars(boolean showScrollbars) {
        this.showScrollbarsWhileScrolling = showScrollbars;
        this.showScrollbarsAlways = false;
        generateScrollBars();
    }

    /**
     * This method deactivates the visibility of the scrollbars.
     */
    public void alwaysShowScrollbars() {
        this.showScrollbarsAlways = true;
        this.showScrollbarsWhileScrolling = true;
        generateScrollBars();
    }

    /**
     * This method makes the scrollbars always visible.
     */
    public void neverShowScrollbars() {
        this.showScrollbarsAlways = false;
        this.showScrollbarsWhileScrolling = false;
        generateScrollBars();
    }

    /**
     * This method generates the scrollbars based on their visibility.
     */
    private void generateScrollBars() {
        if (showScrollbarsWhileScrolling || showScrollbarsAlways) {
            int scrollBarWidth = width*width/paneWidth;
            int scrollBarHeight = height*height/paneHeight;
            int scrollBarThickness =  (int) (virtualScreenHeight/SCROLLBAR_THICKNESS_PARAMETER/toDpi);
            horizontalScrollBar = new GL_Rectangle(pos.getX(), pos.getY()+height-((int) (scrollBarThickness*1.5)), scrollBarWidth, scrollBarThickness, scrollBarColor);
            verticalScrollBar = new GL_Rectangle(pos.getX()+width- ((int) (scrollBarThickness*1.5)), pos.getY(), scrollBarThickness, scrollBarHeight, scrollBarColor);
            if (scrollbarShowtimeOver && !showScrollbarsAlways) {
                horizontalScrollBar.setVisible(false);
                verticalScrollBar.setVisible(false);
            }
        } else {
            horizontalScrollBar = null;
            verticalScrollBar = null;
        }
    }
}

My newest Version of the ScrollView:

package com.airhockey.android.gameelements;

import android.content.res.Resources;
import android.graphics.Color;
import android.opengl.GLES20;
import android.util.DisplayMetrics;

import com.airhockey.android.data.CGPoint;
import com.airhockey.android.objects.GL_Rectangle;
import com.airhockey.android.objects.GL_Shape;
import com.airhockey.android.util.MatrixHelper;

import java.util.ArrayList;
import java.util.Calendar;

/**
 * Created by Leo K., LDK-Productions on 16.12.2015.
 * The GL_ScrollView is a container of GL_Shapes which are placed on a scroll-pane. The scroll-pane can be moved using touch gestures.
 */
public class GL_ScrollView extends GL_Shape {

    //private static String version = "V1.2";

    //Variables needed to distinguish between scroll and click gestures
    public static final int NORMAL_CLICK_DURATION = 200;
    private static float max_click_range = 12.0f;
    private boolean possibleClick = true;
    private boolean possibleScroll = true;

    //Used to convert virtual pixels to device independent pixels
    private float toDpi = 1.0f;

    //The dimensions of the GL_ScrollView
    private int width;
    private int height;

    //The dimensions of the area to be drawn (usually equals width and height)
    private int drawWidth;
    private int drawHeight;

    //The dimensions of the scroll-pane
    private int paneWidth;
    private int paneHeight;

    private int currentOffsetX;
    private int currentOffsetY;

    private int touchDownX = 0;
    private int touchDownY = 0;

    private int tmpMoveX = 0;
    private int tmpMoveY = 0;

    private int startOffsetX = 0;
    private int startOffsetY = 0;

    //If true the dimensions of the scroll-pane will be generated automatically
    private boolean autoPaneDimension = true;

    //Variables needed for the use of GL_SCISSOR_TEST and other functions
    private float deviceScaleX;
    private float deviceScaleY;
    private float devicePixelHeight;
    private float virtualScreenHeight;

    private boolean disableVerticalScroll = false;
    private boolean disableHorizontalScroll = false;

    //Variables to determine which scrollbar is to scroll
    private static int SCROLL_NONE = 0;
    private static int SCROLL_HORIZONTAL = 1;
    private static int SCROLL_VERTICAL = 2;
    private static int SCROLL_BOTH = 3;
    private int scrollState = SCROLL_NONE;

    //Variables for the background
    private GL_Rectangle background = null;
    private GL_Rectangle horizontalScrollBar = null;
    private GL_Rectangle verticalScrollBar = null;
    private int scrollBarColor = Color.GRAY;

    //Variables to determine when scrollbars are visible
    private boolean showScrollbarsAlways = false;
    private boolean showScrollbarsWhileScrolling = true;

    //Variables to make scrollbars stay longer after scrolling ad show when first created
    private long startedShowingScrollbars;
    private boolean scrollbarShowtimeOver = false;
    private static int TIME_SHOW_SCROLLBARS_LONGER_MS = 1650;

    //Other constants
    private static final int SCROLLBAR_THICKNESS_PARAMETER =  72; //Increase to make scrollbar smaller, decrease to make it thicker...
    public static final int CLICK_TOLERANCE_PARAMETER = 38; //Increase to make tolerance smaller, decrease to make it wider...
    //The container of GL_Shapes contained in the GL_ScrollView
    private ArrayList<GL_Shape> children = new ArrayList<GL_Shape>();

    /**
     * The constructor to initialize the GL_ScrollView.
     * @param pos The top left position.
     * @param width The width dimension.
     * @param height The height dimension.
     * @param virtualScreenWidth The width of your virtual coordination system.
     * @param virtualScreenHeight The height of your virtual coordination system.
     * @param devicePixelWidth The width of real pixels of the device.
     * @param devicePixelHeight The height of real pixels of the device.
     */
    public GL_ScrollView(CGPoint pos, int width, int height, float virtualScreenWidth, float virtualScreenHeight, float devicePixelWidth, float devicePixelHeight) {
        super(pos);
        this.width = width;
        this.height = height;
        this.drawWidth = width;
        this.drawHeight = height;
        this.drawWidth = width;
        this.drawHeight = height;
        initScrollView(virtualScreenWidth, virtualScreenHeight, devicePixelWidth, devicePixelHeight);
    }

    /**
     * The constructor to initialize the GL_ScrollView.
     * @param x The x top left position.
     * @param y The y top left position.
     * @param width The width dimension.
     * @param height The height dimension.
     * @param virtualScreenWidth The width of your virtual coordination system.
     * @param virtualScreenHeight The height of your virtual coordination system.
     * @param devicePixelWidth The width of real pixels of the device.
     * @param devicePixelHeight The height of real pixels of the device.
     */
    public GL_ScrollView(int x, int y, int width, int height, float virtualScreenWidth, float virtualScreenHeight, float devicePixelWidth, float devicePixelHeight) {
        super(x, y);
        this.width = width;
        this.height = height;
        this.drawWidth = width;
        this.drawHeight = height;
        initScrollView(virtualScreenWidth, virtualScreenHeight, devicePixelWidth, devicePixelHeight);
    }

    /*
     * Init method for the GL_ScrollView.
     */
    private void initScrollView(float virtualScreenWidth, float virtualScreenHeight, float devicePixelWidth, float devicePixelHeight) {
        this.deviceScaleX = devicePixelWidth/(virtualScreenWidth);
        this.deviceScaleY = devicePixelHeight/(virtualScreenHeight);
        this.devicePixelHeight = devicePixelHeight;
        this.virtualScreenHeight = virtualScreenHeight;
        GL_ScrollView.max_click_range = (int) (virtualScreenHeight/CLICK_TOLERANCE_PARAMETER);
        startedShowingScrollbars = Calendar.getInstance().getTimeInMillis();
        initToDpiValue();
        generateDimension();
    }

    /**
     * Initializes the toDpi value.
     * Note: toDpi may not convert values to real DPI, is just helps make it device independent.
     */
    public void initToDpiValue() {
        DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
        toDpi =  virtualScreenHeight / devicePixelHeight / (metrics.densityDpi / 160f);
    }

    /**
     * Sets the dimensions of the scroll-pane and deactivates automatic pane dimensions.
     * @param paneWidth The width of the scroll-pane where the children are on.
     * @param paneHeight The height of the scroll-pane where the children are on.
     */
    public void setPaneSize(int paneWidth, int paneHeight) {
        this.paneWidth = paneWidth;
        this.paneHeight = paneHeight;
        autoPaneDimension = false;
        generateScrollBars();

        checkPaneDimensions();
    }

    /**
     * Activates automatic pane dimensions.
     */
    public void activateAutoPaneSize() {
        this.autoPaneDimension = true;
        generateDimension();
    }

    /**
     * Adds a view element to the scroll-pane.
     * @param child The view element to be added.
     */
    public void add(GL_Shape child) {
        if (! children.contains(child)) {
            children.add(child);
            if (autoPaneDimension)
                generateDimension();
        }
    }

    /**
     * Removes a view element from the scroll-pane.
     * @param child The view element to be removed.
     */
    public void remove(GL_Shape child) {
        children.remove(child);
    }

    /**
     * This method automatically  generates the dimension of the scroll-pane so that every element is able to be visible.
     */
    private void generateDimension() {
        if (children.size() != 0) {
            int minX = Integer.MAX_VALUE;
            int minY = Integer.MAX_VALUE;
            int maxX = Integer.MIN_VALUE;
            int maxY = Integer.MIN_VALUE;

            //Find the min and max for both x and y
            for (GL_Shape child: children) {
                CGPoint tl = child.getPosTopLeft();
                CGPoint br = child.getPosBottomRight();
                if (tl.getX() < minX)
                    minX = tl.getX();
                if (tl.getY() < minY)
                    minY = tl.getY();
                if (br.getX() > maxX)
                    maxX = br.getX();
                if (br.getY() > maxY)
                    maxY = br.getY();
            }

            this.paneWidth = maxX - minX;
            this.paneHeight = maxY - minY;

            if (pos.getX() > minX) {
                currentOffsetX = pos.getX() - minX;//scroll
                startOffsetX = currentOffsetX;
            } else {
                startOffsetX = 0;
                if (minX > pos.getX())
                    this.paneWidth += minX - pos.getX(); //extend pane
            }


            if (pos.getY() > minY) {
                currentOffsetY = pos.getY() - minY;//scroll
                startOffsetY = currentOffsetY;
            } else {
                startOffsetY = 0;
                if (minY > pos.getY())
                    this.paneHeight += minY - pos.getY(); //extend pane
            }

            this.paneHeight += minY - pos.getY();

            checkPaneDimensions();
        } else {
            this.paneWidth = drawWidth;
            this.paneHeight = drawHeight;
        }

        generateScrollBars();
    }

    /**
     * If scroll-pane is smaller that the GL_ScrollView, extend it
     */
    private void checkPaneDimensions() {
        if (this.paneWidth < drawWidth)
            this.paneWidth = drawWidth;
        if (this.paneHeight < drawHeight)
            this.paneHeight = drawHeight;
    }

    @Override
    protected void generateVertexArray() {
        //abstract method which had to be implemented - not used
    }

    @Override
    public boolean checkTouchDown(int x, int y) {
        boolean isInside = super.checkTouchDown(x, y);
        if (isInside) {

            //If not 2D scrolling; scrolling - axis has not been determined
            if (scrollState != SCROLL_BOTH)
                scrollState = SCROLL_NONE;

            //Save start of touch event
            this.touchDownX = x;
            this.touchDownY = y;

            //Init the movement between touch events
            this.tmpMoveX = touchDownX;
            this.tmpMoveY = touchDownY;

            //This touch down can be a click or a scroll gesture
            this.possibleClick = true;
            this.possibleScroll = true;

            //Translate the coordinates to pane coordinates
            int xScroll = x + currentOffsetX-startOffsetX;
            int yScroll = y + currentOffsetY-startOffsetY;

            //Pass the touch event
            for (int i = 0; i<children.size(); i++) {
                children.get(i).checkTouchDown(xScroll, yScroll);
            }

            //Manage visibility of scrollbars
            scrollbarShowtimeOver = true;
            if(!showScrollbarsAlways && horizontalScrollBar != null)
                horizontalScrollBar.setVisible(true);
            if(!showScrollbarsAlways && verticalScrollBar != null)
                verticalScrollBar.setVisible(true);

        } else {
            //As the touch down was outside the GL_ScrollView it can't be a click or a scroll gesture
            this.possibleScroll = false;
            this.possibleClick = false;
        }
        return isInside;
    }

    @Override
    public boolean checkTouchUp(int x, int y) {
        boolean isInside = super.checkTouchUp(x, y);

        //Manage visibility of scrollbars
        if (!showScrollbarsAlways && showScrollbarsWhileScrolling) {
            //hide scrollbars in TIME_SHOW_SCROLLBARS_LONGER_MS
            scrollbarShowtimeOver = false;
            startedShowingScrollbars = Calendar.getInstance().getTimeInMillis();
        }

        if (isInside) {
            //Translate the coordinates to pane coordinates
            int xScroll = x + currentOffsetX-startOffsetX;
            int yScroll = y + currentOffsetY-startOffsetY;

            //Pass the touch event
            for (int i = 0; i<children.size(); i++) {
                children.get(i).checkTouchUp(xScroll, yScroll);
            }
        }
        return isInside;
    }

    @Override
    public boolean checkTouchMove(int x, int y) {
        boolean isInside = super.checkTouchMove(x, y);
        if (possibleClick) {
            //Check tolerances to help distinguish between touch and scroll
            if (Math.abs(touchDownX-x) > max_click_range*toDpi ||
                    Math.abs(touchDownY-y) > max_click_range*toDpi) { //If touch is not within click tolerance
                possibleClick = false;
                for (int i = 0; i < children.size(); i++) {
                    children.get(i).cancelTouch(); //Cancel touch so that no events are fired from any child
                }
            }
        }
        if (possibleClick && Calendar.getInstance().getTimeInMillis() - GL_MotionEvent.getTouchDownTime() <= NORMAL_CLICK_DURATION ||
                (disableHorizontalScroll && disableVerticalScroll)) { //click
            int xScroll = x + currentOffsetX-startOffsetX;
            int yScroll = y + currentOffsetY-startOffsetY;
            for (int i = 0; i<children.size(); i++) {
                children.get(i).checkTouchMove(xScroll, yScroll);
            }
        } else if(possibleScroll){ //scroll

            //If 2D scrolling is deactivated, determine whether to scroll horizontal or vertical
            if (scrollState == SCROLL_NONE) {
                if (!disableHorizontalScroll && (disableVerticalScroll || Math.abs(tmpMoveX-x) > Math.abs(tmpMoveY-y)))
                    scrollState = SCROLL_HORIZONTAL;
                else
                    scrollState = SCROLL_VERTICAL;
            }

            //Scroll the distance between the last touch event
            if ((! disableHorizontalScroll) && scrollState != SCROLL_VERTICAL)
                currentOffsetX += tmpMoveX-x;
            if ((! disableVerticalScroll) && scrollState != SCROLL_HORIZONTAL)
                currentOffsetY += tmpMoveY-y;
            tmpMoveX = x;
            tmpMoveY = y;

            //Check boundaries //Todo try replace it witch functions below
            if(currentOffsetX > paneWidth-drawWidth)
                scrollToRight();
            else if(currentOffsetX < 0)
                scrollToLeft();
            if(currentOffsetY > paneHeight-drawHeight)
                scrollToBottom();
            else if(currentOffsetY < 0)
                scrollToTop();

        }
        return isInside;
    }

    public boolean isScrolledToLeft() {
        return currentOffsetX <= 0;
    }

    public boolean isScrolledToRight() {
        return currentOffsetX >= paneWidth-drawWidth;
    }

    public boolean isScrolledToTop() {
        return currentOffsetY <= 0;
    }

    public boolean isScrolledToBottom() {
        return currentOffsetY >= paneHeight-drawHeight;
    }

    /**
     * This method manages the visibility of the scrollbars, after the touch-up event.
     */
    private void updateScrollbarTime() {
        if (!scrollbarShowtimeOver &&
                (Calendar.getInstance().getTimeInMillis() - startedShowingScrollbars > TIME_SHOW_SCROLLBARS_LONGER_MS)) {
            if (horizontalScrollBar != null)
                horizontalScrollBar.setVisible(false);
            if (verticalScrollBar != null)
                verticalScrollBar.setVisible(false);
            scrollbarShowtimeOver = false;
        }
    }

    /**
     * This method is to be called to draw the GL_ScrollView and it's components.
     * @param projectionMatrix The projection matrix for the vertex-shader to translate the custom coordination system to the OpenGl ES coordination system.
     */
    public void draw(float[] projectionMatrix) {

        if (!showScrollbarsAlways && showScrollbarsWhileScrolling) {
            updateScrollbarTime();
        }

        if (visible) {
            if (background != null)
                background.draw(projectionMatrix);

            //Movement matrix, to be multiplied with the projection matrix
            float[] m_matix = {
                    //Order x,      y,      z,      w
                    1.f,    0.f,    0.f,    0.f ,
                    0.f,    1.f,    0.f,    0.f ,
                    0.f,    0.f,    1.f,    0.f,
                    -currentOffsetX+startOffsetX,  -currentOffsetY+startOffsetY,  0.f,    1.f
            };

            //Multiply the matrices, based on your implementation you may have to change the order
            float[] movedProjectionMatrix = MatrixHelper.gldMultMatrix(projectionMatrix, m_matix);

            //Translate the custom OpenGL coordination system to device coordination system
            int scissorWidth = (int) (drawWidth * deviceScaleX);
            int scissorHeight = (int) (drawHeight * deviceScaleY);
            int scissorPosX = (int) (pos.x * deviceScaleX);
            int scissorPosY = (int) (devicePixelHeight - pos.y * deviceScaleY - scissorHeight); //the y-axis is reversed

            //USe GL_SCISSOR_TEST to limit the drawing to the size of the GL_ScrollView
            GLES20.glEnable(GLES20.GL_SCISSOR_TEST);
            GLES20.glScissor(scissorPosX, scissorPosY, scissorWidth, scissorHeight);

            //Draw every child on the scroll-pane
            for (int i = 0; i<children.size(); i++) {
                children.get(i).draw(movedProjectionMatrix);
            }

            GLES20.glDisable(GLES20.GL_SCISSOR_TEST);

            //Draw scrollbars if needed
            if (!disableVerticalScroll && verticalScrollBar != null && paneHeight != height) {
                int scrollBarPosition = drawHeight*currentOffsetY/paneHeight;
                //Movement matrix
                float[] m_matix2 = {
                        //Order x,      y,      z,      w
                        1.f,    0.f,    0.f,    0.f ,
                        0.f,    1.f,    0.f,    0.f ,
                        0.f,    0.f,    1.f,    0.f,
                        0,  scrollBarPosition,  0.f,    1.f
                };
                verticalScrollBar.draw(MatrixHelper.gldMultMatrix(projectionMatrix, m_matix2));
            }
            if (!disableHorizontalScroll && horizontalScrollBar != null && paneWidth != width) {
                int scrollBarPosition = drawWidth*currentOffsetX/paneWidth;
                //Movement matrix
                float[] m_matix2 = {
                        //Order x,      y,      z,      w
                        1.f,    0.f,    0.f,    0.f ,
                        0.f,    1.f,    0.f,    0.f ,
                        0.f,    0.f,    1.f,    0.f,
                        scrollBarPosition,  0,  0.f,    1.f
                };
                horizontalScrollBar.draw(MatrixHelper.gldMultMatrix(projectionMatrix, m_matix2));
            }

        }
    }

    public void scrollToTop() {
        currentOffsetY = 0;
    }
    public void scrollToBottom() {
        currentOffsetY = paneHeight-drawHeight;
    }
    public void scrollToLeft() {
        currentOffsetX = 0;
    }
    public void scrollToRight() {
        currentOffsetX = paneWidth-drawWidth;
    }

    @Override
    public boolean collidesWithPoint(int x, int y) {
        return ((x >= pos.getX() && x <= pos.getX()+width) && (y >= pos.getY() && y <= pos.getY()+height));
    }

    @Override
    public boolean collidesWithRectangle(int x, int y, int w, int h) {
        return (pos.x <= x + w &&
                pos.x + width > x &&
                pos.y <= y + h &&
                height + pos.y > y);
    }

    @Override
    public CGPoint getPosTopLeft() {
        return pos;
    }

    @Override
    public void setPosX(int x) {
        super.setPosX(x);
        if (background != null)
            background.setPosX(x);
    }

    @Override
    public void setPosY(int y) {
        super.setPosX(y);
        if (background != null)
            background.setPosX(y);
    }

    @Override
    public CGPoint getPosBottomRight() {
        return new CGPoint(pos.x + width, pos.y + height);
    }

    /**
     * Sets the transparency of the background.
     * @param color The color of the background, format #AARRGGBB
     * @param alpha Value between 0.0 (total transparency) and 1.0 (not transparent).
     */
    public void setBackgroundColor(int color, float alpha) {
        if (background == null)
            background = new GL_Rectangle(pos, width, height, color);
        else
            background.setColor(color);

        background.setAlpha(alpha);
    }

    /**
     * Sets the color of the scrollbars.
     * @param scrollBarColor
     */
    public void setScrollBarColor(int scrollBarColor) {
        this.scrollBarColor = scrollBarColor;
        if (horizontalScrollBar != null)
            horizontalScrollBar.setColor(this.scrollBarColor);
        if (verticalScrollBar != null)
            verticalScrollBar.setColor(this.scrollBarColor);
    }

    /**
     * Activates 2D scrolling.
     * This makes it possible to scroll horizontal and vertical at the same time.
     * This is deactivated by default.
     */
    public void enable2Dscrolling() {
        scrollState = SCROLL_BOTH;
    }

    /**
     * Deactivates 2D scrolling.
     * This makes it possible to only scroll horizontal or vertical per touchdown.
     */
    public void disable2Dscrolling() {
        scrollState = SCROLL_NONE;
    }

    public boolean isHorizontalScrollDisabled() {
        return disableHorizontalScroll;
    }

    public void disableHorizontalScroll() {
        this.disableHorizontalScroll = true;
    }

    public void enableHorizontalScroll() {
        this.disableHorizontalScroll = false;
    }

    public boolean isVerticalScrollDisabled() {
        return disableVerticalScroll;
    }

    public void disableVerticalScroll() {
        this.disableVerticalScroll = true;
    }

    public void enableVerticalScroll() {
        this.disableVerticalScroll = false;
    }

    /**
     * Activates/deactivates the visibility of the scrollbars only while scrolling.
     * @param showScrollbars
     */
    public void showScrollbars(boolean showScrollbars) {
        this.showScrollbarsWhileScrolling = showScrollbars;
        this.showScrollbarsAlways = false;
        generateScrollBars();
    }

    /**
     * This method deactivates the visibility of the scrollbars.
     */
    public void alwaysShowScrollbars() {
        this.showScrollbarsAlways = true;
        this.showScrollbarsWhileScrolling = true;
        generateScrollBars();
    }

    /**
     * This method makes the scrollbars always visible.
     */
    public void neverShowScrollbars() {
        this.showScrollbarsAlways = false;
        this.showScrollbarsWhileScrolling = false;
        generateScrollBars();
    }

    /**
     * This method generates the scrollbars based on their visibility.
     */
    private void generateScrollBars() {
        if (showScrollbarsWhileScrolling || showScrollbarsAlways) {
            int scrollBarWidth = drawWidth*drawWidth/paneWidth;
            int scrollBarHeight = drawHeight*drawHeight/paneHeight;
            int scrollBarThickness =  (int) (virtualScreenHeight/SCROLLBAR_THICKNESS_PARAMETER/toDpi);
            horizontalScrollBar = new GL_Rectangle(pos.getX(), pos.getY()+drawHeight-((int) (scrollBarThickness*1.5)), scrollBarWidth, scrollBarThickness, scrollBarColor);
            verticalScrollBar = new GL_Rectangle(pos.getX()+drawWidth- ((int) (scrollBarThickness*1.5)), pos.getY(), scrollBarThickness, scrollBarHeight, scrollBarColor);
            if (scrollbarShowtimeOver && !showScrollbarsAlways) {
                horizontalScrollBar.setVisible(false);
                verticalScrollBar.setVisible(false);
            }
        } else {
            horizontalScrollBar = null;
            verticalScrollBar = null;
        }
    }

    public int getWidth() {
        return width;
    }

    public int getHeight() {
        return height;
    }

    public int getDrawWidth() {
        return drawWidth;
    }

    /**
     * Sets the width of the area of the scroll-pane to be drawn.
     * @param drawWidth The width of the area of the scroll-pane to be drawn.
     */
    public void setDrawWidth(int drawWidth) {
        this.drawWidth = drawWidth;
        if (autoPaneDimension)
            generateDimension();
        else
            checkPaneDimensions();
        generateScrollBars();
    }

    public int getDrawHeight() {
        return drawHeight;
    }

    /**
     * Sets the height of the area of the scroll-pane to be drawn.
     * @param drawHeight The height of the area of the scroll-pane to be drawn.
     */
    public void setDrawHeight(int drawHeight) {
        this.drawHeight = drawHeight;
        if (autoPaneDimension)
            generateDimension();
        else
            checkPaneDimensions();
        generateScrollBars();
    }

    public int getScrollOffsetX() {
        return currentOffsetX;
    }

    public int getScrollOffsetY() {
        return currentOffsetY;
    }

    /**
     * Returns the background of the ScrollView. Returns null if no background color is set
     *
     * @return Returns the background of the ScrollView.
     */
    public GL_Rectangle getBackground() {
        return background;
    }
}

[ATTACH]102[/ATTACH]

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.