Scrollable image with ScrollView

edited February 24 in Android Mode

Hi. I want to scroll a large image horizontally, and the code below will do the job, but not as smooth that I'm used to, on my tablet (Samsung Tab E 9.6). It flickers a bit. Is there a way to improve it or better use java ScrollView containing an image?

PGraphics img;
int imgx;
boolean dragging = false;

void setup()  {
  img = createGraphics(4 * width, height);
  for (float i = 200; i <= 4 * width; i += 400) {
    img.ellipse(i, 400, 400, 400);

void draw() {
  imgx = constrain(imgx, -img.width + width, 0); 
  set(imgx, 0, img);
  if (!mousePressed && dragging) dragging = false;
  if (dragging) imgx += -pmouseX + mouseX;

void mousePressed() {
    dragging = true;


  • What's line 21 doing?

    Does set crop to the screen? Or is it drawing up to 3x the width off the right side of the screen?

    Do you need background? Or are you filling the screen with your scrolling image anyway?

  • edited February 24

    Hi @koogs. It's said to be faster than image(img, imgx, 0) Strangely I need to redraw background when image is created with PGraphics. When created as PImage it is not. I am using the APDE app.

  • Transparency, I guess...

    I would still try and think of a way of copying only the pixels you need to fill the screen, not potentially the entire widescreen image, even though only a quarter of it will be visible.

    Actually, that reminds me, someone had problems with starting a draw off the left hand side of the screen, -ve x. I would try replacing what you have with the relevant copy() call

  • Actually the goal is to use a png image. I only used the createGraphics in case of someone wanting to try the sketch.

  • I would use ketai to detect dragging and I bet you will have an improvement in performance if you use only the section of the image you want to display, as koogs suggested. I haven't tested this in Android, but that is what I would do in a Java sketch. As a matter of fact, I know that using larger images just makes the code lag. Something I should comment is that in Java mode, the function set() is preferred over image() if the image pixels are not modified during the drawing procedure (aka. you are not calling loadPixels() ). Not sure if this holds in Android mode though.


  • Hi Kf. Ketai doesn't have a special method for mouseDragged(). If I use this instead of mousePressed() it will react slower. I am trying to give some, let's say, momentum to the image calculating the acceleration with millis() and dist() to slowly deaccelerate, like we see when reading a webpage on Android; but still without success. What I really would like are some guide lines to do it the 'java' way with ScrollView

  • @noel===

    • what do you mean by ScrollView? - ScrollView is an Android kind of view for images or text which adds horizontal or vertical bars for scrolling; i dont know if it is possible to add this kind of view with ketai; what i know is that is of course possible with android native code.

    • as for moving an image (drag) you can use the android onTouchEvent method with MotionEvent API (ActionDown, ActionMove, ActionUp); you can also use the gestureDetector and its onScroll() event.

  • My suggestion about Ketai is for you to access the gesture actions defined there. It is up to you if you want to use them. Related to scrolling your view, I believe you should be able to create the effect using Processing code.

    give some, let's say, momentum to the image calculating the acceleration with millis() and dist() to slowly deaccelerate

    I believe what you are trying to do is called an easing effect. Before you try to implement it, you should explore if you can load you image in an Android element that is made for that so it will save you some work. Follow akenaton's advice an explore the ScrollView component. I am not familiar with it so I do not have a code to share.


  • edited March 5

    Demo showing Scroll View in Action. Notice that mousePressed does not work on top of the Scroll View container. You will need to add a listener to it. For the time being, mousePressed only works on the top section of the demo. I have included a button for clarity. You get the Scroll view effect if the images(elements) you load there are larger than its defined height.


    // IMPORTS:
    import android.content.Context;
    import android.widget.FrameLayout;
    import android.os.Environment;
    import android.widget.Toast;
    import android.os.Looper;
    import android.view.WindowManager;
    import android.os.Bundle;
    import android.view.ViewParent;
    import android.view.ViewGroup;
    import android.view.View;
    import android.widget.RelativeLayout;
    import android.widget.LinearLayout;
    import android.view.LayoutInflater;
    import android.R.string;
    import android.R;
    import android.widget.ImageView;
    import android.util.Log;
    import android.widget.ScrollView;
    import android.widget.Button;
    Activity act;
    Context mC;
    FrameLayout fl;
    ScrollView sv;
    int imageID1=-1;
    int imageID2=-1;
    int imageID3=-1;
    PImage img1, img2, img3;
    int curr=-1;
    void setup() {
      act = this.getActivity();
      textAlign(CENTER, CENTER);
      img1=initImage(300, 900, ARGB);//loadImage("pic40.jpg");
      img2=initImage(600, 900, ARGB); 
      img3=initImage(900, 900, ARGB);
    void draw() {
      text("Scroll View Demo showing now "+curr,width>>1,50);
      text("Size of selected on left "+(300*(curr+1))+"x900",width>>1,125);
    void mouseReleased() {
      //if (mouseY>height>>2)    return;
      rect(40, 40+250*curr, 220, 220);
      image(img1, 50, 50, 200, 200);
      image(img2, 50, 300, 200, 200);
      image(img3, 50, 550, 200, 200);
      act.runOnUiThread(new Runnable() {
        @ Override
          public void run() {
          ImageView imgv1= (ImageView)  act.findViewById(imageID1); 
          ImageView imgv2= (ImageView)  act.findViewById(imageID2); 
          ImageView imgv3= (ImageView)  act.findViewById(imageID3); 
          if (curr==0) {
          } else if (curr==1) {          
          } else if (curr==2) {          
    PImage initImage(int w, int h, int mode) {
      PImage aimg =createImage(w, h, mode);
      int s=w*h;
      float lower=random(225);
      float delta=25;
      for (int i=0; i<s; i++)
        aimg.pixels[i]=color(int(lower+delta*i*1.0/s),255,255);//color(random(upper), random(50, 200), random(upper,255));
      return aimg;
    //  @@@@@@@@@@@@
      public void onCreate(Bundle savedInstanceState) {
      act = this.getActivity();
      mC= act.getApplicationContext();
      // OOOOOOOOOOOOOOOO---------------------------OOOOOOOOOOOOOOOO
      // OOOOOOOOOOOOOOOO---------------------------OOOOOOOOOOOOOOOO
      Button switchButton= new Button(act);
      switchButton.setLayoutParams(new RelativeLayout.LayoutParams(width>>2,150));
      switchButton.setText ("Show next");
      switchButton.setOnClickListener(new View.OnClickListener(){
        public void onClick(View V){
      ImageView imgView1 = new ImageView(act);
      imgView1.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
      ImageView imgView2 = new ImageView(act);
      imgView2.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
      ImageView imgView3 = new ImageView(act);
      imgView3.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
      LinearLayout linLay=new LinearLayout(act);
      linLay.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)); 
      sv = new ScrollView(act);
      sv.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, height-450));  
      // OOOOOOOOOOOOOOOO---------------------------OOOOOOOOOOOOOOOO
      //                 @@@@@@@@@@@@@@@@@@@@@@@@@@@
      // OOOOOOOOOOOOOOOO---------------------------OOOOOOOOOOOOOOOO
      fl = (FrameLayout)act.findViewById(;
  • Thank you Kf, this is exactly what I'm trying to accomplish. I worked this whole weekend frustratingly on it, so I was quite happy with this demo. I still get two errors:

    1: runOnUiThread(new Runnable() {
    The method runOnUiThread(new Runnable(){}) is undefined for the type "sketch name"

    2: imgView1.setId(imageID1 = imgView1.generateViewId())
    The method generateViewId() is undefined for the type ImageView

    (Of course the same error for the othe two images)

    I began to search and found this, but I couldn't wrap my mind arround it yet. It's 2 AM and I didn't sleep until now.

  • @noel====

    • put act.runOnUiThread (fragment)

    • dont use generateViewId which is only for very recent APIS (>=17). Set yourself an id (an integer); of course dont set the same id to the imageViews and choose something like 456789

    • this kind of scrolling (with scrolling view) works only if your content is bigger than the scrollView

  • edited March 5

    Thanks akenaton. You both really do know your stuff. The app does install now, but when running it gives a black screen, and when going back to the APDE it gives the error:

    java.lang.RuntimeException: Unable to start activity ComponentInfo{processing.test.scrollview1/processing.test.scrollview1.MainActivity}: android.util.AndroidRuntimeException: requestFeature() must be called before adding content

    Searching a bit I found this, but I can't imagine the reason. Defenitly trying to learn some java the hard way, I guess.

  • runOnUiThread (fragment)

    That is odd that it ran like that on my machine. I tested this code before posting. Related to generating an ID, try using int(random(PApplet.MAX_INT)); Related to your last error... not sure, can you post all your error trace? What OS, AM and Android API are you working with?


  • edited March 5

    Well, it now starts with a grey screen, the text and the button. When I toutch the screen and hold it or drag it, still nothing happens. When I release the screen the three blocks are drawn with the images, but after a second the program crashes with the error:

    java.lang.NullPointerException at processing.test.scrollview1.scrollview1$ at android.os.Handler.handleCallback( at android.os.Handler.dispatchMessage( at android.os.Looper.loop( at at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke( at$ at at dalvik.system.NativeStart.main(Native Method)

    I am using a Samsung Tab 9.6 (SM - T560) with KitKat 4.4.4 (API 19) Either compiling on my windows 10 pc with Processing 3.3.6 or using The APDE gives the same result.

  • @noel=== i will test the code tomorrow, yet is difficult without the images you are using.

  • @akenaton My code does not use images. I am generating some gradient images by using pixel manipulation. I forgot to mention, you have to click on the button once to load the images and to be able to see the scroll view in action. Tapping on the button again just rearranges the order of the images but the scroll view still serves the same purpose.


  • @akenaton My code does not use images. I am generating some gradient images by using pixel manipulation. I forgot to mention, you have to click on the button once to load the images and to be able to see the scroll view in action. Tapping on the button again just rearranges the order of the images but the scroll view still serves the same purpose.


  • The image I used with the first simple 'Processing code' at the top you can download here :

Sign In or Register to comment.