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()  {
  fullScreen();
  orientation(LANDSCAPE);
  img = createGraphics(4 * width, height);
  img.beginDraw();
  img.fill(150);
  for (float i = 200; i <= 4 * width; i += 400) {
    img.ellipse(i, 400, 400, 400);
  }
  img.endDraw();
  frameRate(1000);
}

void draw() {
  background(255);
  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;
}

Answers

  • 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.

    Kf

  • 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.

    Kf

  • 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.

    Kf

    //===========================================================================
    // IMPORTS:
    
    import android.app.Activity;
    import android.content.Context;
    import android.widget.FrameLayout;
    //import android.app.Fragment;
    
    import android.os.Environment;
    import android.graphics.Color;
    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.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.net.Uri;
    import android.util.Log;
    import android.widget.ScrollView;
    import android.widget.Button;
    
    
    //===========================================================================
    // FINAL FIELDS:
    
    
    //===========================================================================
    // GLOBAL VARIABLES:
    
    Activity act;
    Context mC;
    
    FrameLayout fl;
    ScrollView sv;
    int imageID1=-1;
    int imageID2=-1;
    int imageID3=-1;
    
    PImage img1, img2, img3;
    int curr=-1;
    
    
    //===========================================================================
    // PROCESSING DEFAULT FUNCTIONS:
    
    void setup() {
      fullScreen();
      orientation(PORTRAIT);
      //orientation(LANDSCAPE;)
    
      act = this.getActivity();
      Looper.prepare();
    
      textAlign(CENTER, CENTER);
      //rectMode(CENTER);
    
      fill(255);
      strokeWeight(2);
      textSize(32);
    
    
    
      img1=initImage(300, 900, ARGB);//loadImage("pic40.jpg");
      img2=initImage(600, 900, ARGB); 
      img3=initImage(900, 900, ARGB);
    
      curr=0;
    }
    
    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;
    
      curr=(curr+1)%3;
    
      background(88);
    
      fill(255);
      noStroke();
      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) {
            imgv1.setImageBitmap((Bitmap)img1.getNative()); 
            imgv2.setImageBitmap((Bitmap)img2.getNative()); 
            imgv3.setImageBitmap((Bitmap)img3.getNative());
          } else if (curr==1) {          
            imgv1.setImageBitmap((Bitmap)img2.getNative()); 
            imgv2.setImageBitmap((Bitmap)img3.getNative()); 
            imgv3.setImageBitmap((Bitmap)img1.getNative());
          } else if (curr==2) {          
            imgv1.setImageBitmap((Bitmap)img3.getNative());  
            imgv2.setImageBitmap((Bitmap)img1.getNative()); 
            imgv3.setImageBitmap((Bitmap)img2.getNative());
          }
        }
      }
      );
    }
    
    
    //===========================================================================
    // OTHER FUNCTIONS:
    
    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;
      colorMode(HSB,255);
    
      aimg.loadPixels();
      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));
      aimg.updatePixels();
      return aimg;
    }
    
    
    
    //===========================================================================
    // ANDROID ACTIVITY LIFECYCLE'S FUNCTIONS:
    
    
    //  @@@@@@@@@@@@
    @Override 
      public void onCreate(Bundle savedInstanceState) {
      super.onCreate(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.setTextSize(10);
      //switchButton.setTextColor(Color.rgb(220,220,220));
      switchButton.setText ("Show next");
      switchButton.setX(width*0.60);
      switchButton.setY(200);
      switchButton.setOnClickListener(new View.OnClickListener(){
        public void onClick(View V){
    
          mouseReleased();
    
        }
      });
    
      ImageView imgView1 = new ImageView(act);
      imgView1.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
      imgView1.setId(imageID1=imgView1.generateViewId());
    
    
      ImageView imgView2 = new ImageView(act);
      imgView2.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
      imgView2.setId(imageID2=imgView2.generateViewId());
    
    
      ImageView imgView3 = new ImageView(act);
      imgView3.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
      imgView3.setId(imageID3=imgView3.generateViewId());
    
    
      LinearLayout linLay=new LinearLayout(act);
      linLay.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)); 
      linLay.setOrientation(LinearLayout.VERTICAL);  
      linLay.addView(imgView1);
      linLay.addView(imgView2);
      linLay.addView(imgView3);
    
      sv = new ScrollView(act);
      sv.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, height-450));  
      sv.setVerticalScrollBarEnabled(true);  
      sv.setY(450);  
      sv.addView(linLay);
    
    
      // OOOOOOOOOOOOOOOO---------------------------OOOOOOOOOOOOOOOO
    
      //                 @@@@@@@@@@@@@@@@@@@@@@@@@@@
    
      // OOOOOOOOOOOOOOOO---------------------------OOOOOOOOOOOOOOOO
    
    
      fl = (FrameLayout)act.findViewById(R.id.content);
      fl.addView(switchButton);
      fl.addView(sv);
    }
    
  • 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?

    Kf

  • 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$1.run(scrollview1.java:198) at android.os.Handler.handleCallback(Handler.java:733) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:136) at android.app.ActivityThread.main(ActivityThread.java:5590) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:515) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1268) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1084) 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.

    Kf

  • @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.

    Kf

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

    https://drive.google.com/file/d/0B8HXpZALHgx-ZF9lRWVCX0dDbEZ5Q0hJYVFocWpFN2I5ZWtr/view?usp=drivesdk

  • @noel===

    sorry to answer only now but too much things to do; i have downloaded your image and tried to move it with a code i have already for that; no success because i get not an error but some kind of warning (working with AS): "this bitmap exceeds the max size for openGLES" - which is 4096/4096; if i "cut" your image, the code runs normally. Not tried till now with P5 (i have to adapt my code) but i am now sure that you get exactly the same problem.

  • edited March 19

    @akenaton

    I am not sure what you mean by "getting exactly the same problem". Using PImage with first code above scrolls the image reasonable well, but I could't give it an easing effect. Or are you refering to kfrajer's code? Do you want me to try yours?

  • edited March 19

    @kfrayer

    Your code now shows the following error after compliling an then crashing.

    java.lang.NullPointerException
        at processing.test.scrollview1.scrollview1$1.run(scrollview1.java:124)
        at android.app.Activity.runOnUiThread(Activity.java:4916)
        at processing.test.scrollview1.scrollview1.mouseReleased(scrollview1.java:113)
        at processing.test.scrollview1.scrollview1$2.onClick(scrollview1.java:168)
        at android.view.View.performClick(View.java:4508)
        at android.view.View$PerformClick.run(View.java:18675)
        at android.os.Handler.handleCallback(Handler.java:733)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:136)
        at android.app.ActivityThread.main(ActivityThread.java:5590)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:515)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1268)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1084)
        at dalvik.system.NativeStart.main(Native Method)
    
  • Not sure what is causing the problem in your case. I just tested the app in an emulator and it worked. I have to say that my code was not adapted to different screen resolutions. I modified the code to do so. I notice there is also a bug/undesirable features that if you press on the upper section of your screen, it will trigger the button. Not a big deal in this demo, but it is still a pain the the place where everybody knows.

    Below it shows the scrolling action.

    Kf

    Screenshot_1521531922

    Screenshot_1521531925

    Screenshot_1521531928

    //===========================================================================
    // IMPORTS:
    
    import android.app.Activity;
    import android.content.Context;
    import android.widget.FrameLayout;
    //import android.app.Fragment;
    
    import android.os.Environment;
    import android.graphics.Color;
    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.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.net.Uri;
    import android.util.Log;
    import android.widget.ScrollView;
    import android.widget.Button;
    
    
    //===========================================================================
    // FINAL FIELDS:
    
    
    //===========================================================================
    // GLOBAL VARIABLES:
    
    Activity act;
    Context mC;
    
    FrameLayout fl;
    ScrollView sv;
    int imageID1=-1;
    int imageID2=-1;
    int imageID3=-1;
    
    PImage img1, img2, img3;
    int curr=-1;
    
    int basicImageSize=0;
    int thumbNailSize=0;
    
    //===========================================================================
    // PROCESSING DEFAULT FUNCTIONS:
    
    void setup() {
      fullScreen();
      orientation(PORTRAIT);
      //orientation(LANDSCAPE;)
    
      act = this.getActivity();
      Looper.prepare();
    
      textAlign(CENTER, CENTER);
      //rectMode(CENTER);
    
      fill(255);
      strokeWeight(2);
      textSize(max(16,width/30.0));
    
      basicImageSize=int(min(width,height)/4.0);
      thumbNailSize= int(basicImageSize/6.0);
    
    
    
      img1=initImage(basicImageSize*1, basicImageSize*3, ARGB);//loadImage("pic40.jpg");
      img2=initImage(basicImageSize*2, basicImageSize*3, ARGB); 
      img3=initImage(basicImageSize*3, basicImageSize*3, ARGB);
    
      curr=0;
    }
    
    void draw() {
    
      text("Scroll View Demo showing now "+curr, width>>1, thumbNailSize);
      text("Size of selected on left "+(basicImageSize*(curr+1))+"x900", width>>1, thumbNailSize*3);
    }
    
    void mouseReleased() {
    
      //if (mouseY>height>>2)    return;
    
      curr=(curr+1)%3;
    
      background(88);
    
      fill(255);
      noStroke();
      rect(0.80*thumbNailSize, thumbNailSize*0.80+thumbNailSize*5*curr, thumbNailSize*4.5, thumbNailSize*4.5);
    
      image(img1, thumbNailSize, thumbNailSize, thumbNailSize*4, thumbNailSize*4);
      image(img2, thumbNailSize, thumbNailSize*6, thumbNailSize*4, thumbNailSize*4);
      image(img3, thumbNailSize, thumbNailSize*11, thumbNailSize*4, thumbNailSize*4);
    
    
    
      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) {
            imgv1.setImageBitmap((Bitmap)img1.getNative()); 
            imgv2.setImageBitmap((Bitmap)img2.getNative()); 
            imgv3.setImageBitmap((Bitmap)img3.getNative());
          } else if (curr==1) {          
            imgv1.setImageBitmap((Bitmap)img2.getNative()); 
            imgv2.setImageBitmap((Bitmap)img3.getNative()); 
            imgv3.setImageBitmap((Bitmap)img1.getNative());
          } else if (curr==2) {          
            imgv1.setImageBitmap((Bitmap)img3.getNative());  
            imgv2.setImageBitmap((Bitmap)img1.getNative()); 
            imgv3.setImageBitmap((Bitmap)img2.getNative());
          }
        }
      }
      );
    }
    
    
    //===========================================================================
    // OTHER FUNCTIONS:
    
    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;
      colorMode(HSB, 255);
    
      aimg.loadPixels();
      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));
      aimg.updatePixels();
      return aimg;
    }
    
    
    
    //===========================================================================
    // ANDROID ACTIVITY LIFECYCLE'S FUNCTIONS:
    
    
    @Override
      public void onCreate(Bundle savedInstanceState) {
      super.onCreate(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.setTextSize(10);
      //switchButton.setTextColor(Color.rgb(220,220,220));
      switchButton.setText ("Show next");
      switchButton.setX(width*0.60);
      switchButton.setY(200);
      switchButton.setOnClickListener(new View.OnClickListener(){
        public void onClick(View V){
    
          mouseReleased();
    
        }
      });
    
      ImageView imgView1 = new ImageView(act);
      imgView1.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
      imgView1.setId(imageID1=imgView1.generateViewId());
    
    
      ImageView imgView2 = new ImageView(act);
      imgView2.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
      imgView2.setId(imageID2=imgView2.generateViewId());
    
    
      ImageView imgView3 = new ImageView(act);
      imgView3.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
      imgView3.setId(imageID3=imgView3.generateViewId());
    
    
      LinearLayout linLay=new LinearLayout(act);
      linLay.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)); 
      linLay.setOrientation(LinearLayout.VERTICAL);  
      linLay.addView(imgView1);
      linLay.addView(imgView2);
      linLay.addView(imgView3);
    
      sv = new ScrollView(act);
      sv.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,height));  //, LinearLayout.LayoutParams.WRAP_CONTENT)); // ,height-int(displayHeight/8.0)));  
      sv.setVerticalScrollBarEnabled(true);  
      sv.setY(int(displayHeight/8.0));  
      sv.addView(linLay);
    
    
      // OOOOOOOOOOOOOOOO---------------------------OOOOOOOOOOOOOOOO
    
      //                 @@@@@@@@@@@@@@@@@@@@@@@@@@@
    
      // OOOOOOOOOOOOOOOO---------------------------OOOOOOOOOOOOOOOO
    
    
      fl = (FrameLayout)act.findViewById(R.id.content);
      fl.addView(switchButton);
      fl.addView(sv);
    }
    
    //  @@@@@@@@@@@@
    @Override 
      public void onStart() {
      super.onStart();
    }
    
    //  @@@@@@@@@@@@
    @Override 
      public void onResume() {
      super.onResume();
    
      act = this.getActivity();
      mC= act.getApplicationContext();
    
    }
    
  • @Kfrajes Thanks for continuing. My kitkat api 19 doesn't accept imgView.generateViewId(), so I gave it some random number, and now it doesn't crash anymore. After some slight modifications to let the button to show up, I have the resulting image below. It doesn't scroll however, and the thumbnail switches wherever you click on the screen.Screenshot_2018-03-20-07-53-06

  • Sorry I can't ttest more at the moment.

    My kitkat api 19 doesn't accept imgView.generateViewId()

    Replace generate for a random and unique number. You need to check the docs and see if that call is available in that API. I wouldn't be surprise if it is not.

    Related my demo... I will have to rewrite it again. My initial demo was fully functional in my device but the dimensions were wrong in the emulator. My second attempt might need more carefull design so it can work with any screen dimensions. I am afraid my demo is a demo in the right platform but not a good generic demo. I can rewrite it and generate a better one tonight, as time permits. You are also welcome to share yours and I could test it on my end.

    Regarding:

    and the thumbnail switches wherever you click on the screen.

    Notice I mentioned that in my previous post:

    [...] there is also a bug/undesirable features that if you press on the upper section of your screen, it will trigger the button

    Kf

  • edited March 20

    @noel===

    there is a limit size for OPENGL and images with imageView in android here the error code that i get using your image: W/OpenGLRenderer: Bitmap too large to be uploaded into a texture (16108x1600, max=4096x4096) As for my code i have 2 solutions, first one using scrollView, second one using motionEvent and Action_Move. Twos are done using AS and work fine with images of width 4000px; as i told you as soon as possible i will try to adapt them (or one of them) to P5. For images that exceed this limit i think that it could be a solution to use the same technique than with maps, cuting the image in several tiles.

  • Answer ✓

    A new demo. If you load images from internet, you need the internet permission.

    Kf

    //===========================================================================
        // IMPORTS:
    
    import android.app.Activity;
    import android.content.Context;
    import android.widget.FrameLayout;
    //import android.app.Fragment;
    
    import android.os.Environment;
    import android.graphics.Color;
    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.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.net.Uri;
    import android.util.Log;
    import android.widget.ScrollView;
    import android.widget.Button;
    
    
        //===========================================================================
    // FINAL FIELDS:
    final int imageID=12345;
    
    //===========================================================================
    // GLOBAL VARIABLES:
    
    Activity act;
    Context mC;
    
    FrameLayout fl;
    ScrollView sv;
    int imageID1=-1;
    int imageID2=-1;
    int imageID3=-1;
    
    
    ArrayList<PImage> imgCon;
    
    PImage img1, img2, img3;
    int currIdx=-1;
    
    
    //===========================================================================
    // PROCESSING DEFAULT FUNCTIONS:
    
    void setup() {
      fullScreen();
      orientation(PORTRAIT);
    
      act = this.getActivity();
      Looper.prepare();
    
      textAlign(CENTER, CENTER);
    
      fill(255);
      strokeWeight(2);
      textSize(32);
    
      imgCon = new ArrayList<PImage>();
    
      imgCon.add(initImage(width, int(height*0.80), "KlEo"));
      imgCon.add(initImage(width, int(height*1.25), "xSiNe")); 
      imgCon.add(initImage(width, int(height*1.50), "DRAkeN")); 
    
      //  *****  FOR the next images one needs to enable INTERNET permissions
    
      //PImage extra1=requestImage("https://vignette.wikia.nocookie.net/vsbattles/images/a/ad/Alduin.jpg/revision/latest?cb=20140811032839");  //dragon
      PImage extra2=requestImage("https://upload.wikimedia.org/wikipedia/commons/c/ce/Black-headed_oriole,_Oriolus_larvatus,_at_Mapungubwe_National_Park,_Limpopo,_South_Africa_(18201359415).jpg");  //Yellow bird
      PImage extra3=requestImage("http://78.media.tumblr.com/ff9912bcaa6963631a6a205ae7be46a5/tumblr_mxarraaVgz1shaozko1_1280.jpg");
      PImage extra4=requestImage("http://www.shorpy.com/files/images/SHORPY_4a08044a.jpg");  //NY building
    
      while ( extra2.width<=1 || extra3.width<=1 || extra4.width<=1 ) {
        if(frameCount%90==0) println(extra2.width,extra3.width,extra4.width);
        delay(50);
      }
    
    
      extra2.loadPixels();
      extra2=extra2.get(0, 0, min(width, extra2.width), extra2.height);
      extra2.updatePixels();
    
      extra3.loadPixels();
      extra3=extra3.get(0, 0, width, extra3.height);
      //extra3.resize(width, extra3.height);
      extra3.updatePixels();
    
      //imgCon.add(extra1);
      imgCon.add(extra2);
      imgCon.add(extra3);
      imgCon.add(extra4);
      //imgCon.add(extra5);
    
      for (PImage q : imgCon) {
        println(q.width, q.height);
      }
    
    
      currIdx=0;
      swapImage();
    }
    
    void draw() {
    
      text("Scroll View Demo showing now "+currIdx, width>>1, height/40.0);
      text("Size of selected iamge "+imgCon.get(currIdx).width+"x"+imgCon.get(currIdx).height, width>>1, height/16.0);
    }
    
    void mouseReleased() {
      //swapImage();
    }
    
    void swapImage() {
    
      background(random(70, 144));
      currIdx=(currIdx+1)%imgCon.size();
    
      act.runOnUiThread(new Runnable() {
        @ Override
          public void run() {
    
          ImageView imgv= (ImageView)  act.findViewById(imageID); 
          imgv.setImageBitmap((Bitmap)imgCon.get(currIdx).getNative());
        }
      }
      );
    }
    
    
    //===========================================================================
    // OTHER FUNCTIONS:
    
    //Creates an image with a color gradient as background and a ellipse of random size
    //placed at a random location. The label is placed inside the ellipse.
    PImage initImage(int w, int h, String label) {
      PImage aimg =createImage(w, h, ARGB);
    
      int s=w*h;
      float lower=random(225);
      float delta=25;
      colorMode(HSB, 255);
    
      aimg.loadPixels();
      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));
      aimg.updatePixels();
    
      int diameter=int(w*random(0.35, 0.70));
      float px=random(diameter, width-diameter);
      float py=random(diameter, height-diameter);
    
      PGraphics con =createGraphics(w, h, JAVA2D);
      con.beginDraw();
      con.image(aimg, 0, 0);
    
      con.fill(255-lower, 250, 250);
      con.noStroke();
      con.ellipse(w-px, h-py, diameter/2.0, diameter/2.0);
      con.ellipse(px, py, diameter, diameter);
    
      con.textAlign(CENTER, CENTER);
      con.colorMode(HSB, 255);
      con.fill(lower, 255, 255);
      con.textSize(max(36, w/80.0));
      con.text(label, px, py);
      con.text(label, px+1, py+1);  //Bold effect
    
      con.endDraw();
      return con.get();
    }
    
    //===========================================================================
    // ANDROID ACTIVITY LIFECYCLE'S FUNCTIONS:
    
    
    //  @@@@@@@@@@@@
    @Override
      public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
    
      act = this.getActivity();
      mC= act.getApplicationContext();
    
      // OOOOOOOOOOOOOOOO---------------------------OOOOOOOOOOOOOOOO
    
      // OOOOOOOOOOOOOOOO---------------------------OOOOOOOOOOOOOOOO
    
    
      Button switchButton= new Button(act);
      switchButton.setLayoutParams(new RelativeLayout.LayoutParams(width>>2,int(height/13.0)));
      switchButton.setTextSize(10);
      //switchButton.setTextColor(Color.rgb(220,220,220));
      switchButton.setText ("Next Image");
      switchButton.setX(width*0.60);
      switchButton.setY(height*0.1);
      switchButton.setOnClickListener(new View.OnClickListener(){    
        public void onClick(View V){ 
          swapImage(); 
        }
      });
    
      ImageView imgView = new ImageView(act);
      imgView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
      imgView.setId(imageID); //imgView1.generateViewId()
    
    
      LinearLayout linLay=new LinearLayout(act);
      linLay.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)); 
      linLay.setOrientation(LinearLayout.VERTICAL);  
      linLay.addView(imgView);
    
     int svHeight=int(height*0.70);
      sv = new ScrollView(act);
      sv.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, svHeight));  
      sv.setVerticalScrollBarEnabled(true);  
      sv.setY(height-svHeight);  
      sv.addView(linLay);
    
    
      // OOOOOOOOOOOOOOOO---------------------------OOOOOOOOOOOOOOOO
    
      //                 @@@@@@@@@@@@@@@@@@@@@@@@@@@
    
      // OOOOOOOOOOOOOOOO---------------------------OOOOOOOOOOOOOOOO
    
    
      fl = (FrameLayout)act.findViewById(R.id.content);
      fl.addView(switchButton);
      fl.addView(sv);
    }
    
  • @noel,@kfrajer ===

    the limit for image size depends of the phone itself, though for most of them it is 2048X2048; in my case it is twice this basic limit (4096): anyway i dont think that it is possible to use the image provided by @noel, which is 8054 (width); now if you look to my last post you can see that the warning speaks about 16108, which is twice the image width; at the beginning i have not noticed this point but now i know why: i have put the image in a drawable folder which by default is considered as a mdpi folder and as the used phone is xhdpi then the system "adapts" the mdpi and creates this 16108 monster .... As for the "scroll" code one of the twos i have used (with AS) is very similar to @kfrajer - yet it fails (AS) for the provided image (size limit) and what i cannot understand is why it could run with P5: i have to look at that; as i have told also some solution could be to "cut" the image in tiles (2048 at most) and perhaps use them in an imageGallery.

  • Kfrajer Thanks for your effort, could not run right away, but will look into it tonight.

  • @akenaton I never thought this would be problematic in AS Good point for Processing. But even the android's photos app, when you open and amplify the image and then scroll it, you see a 'Google Earth update effect'. I wonder if WebView with a local file could be used.

  • @akenaton

    i have put the image in a drawable folder which by default is considered as a mdpi folder and as the used phone is xhdpi then the system "adapts" the mdpi

    You are running your sample code from AS? If you are running from Proceesing, do you still setup your drawable as a folder within the data folder?

    Kf

  • edited March 21

    @kfrajer===

    my code is a class written with AS and so i can add drawable folder (or others with qualifiers)

  • @noel===

    yes, using a webview is probably the simplest solution; as i have a class for that i have tried with your image & it works fine with AS.

  • edited March 23 Answer ✓

    @noel===

    code snippet for android webview from local files and your image "panorama" - (put the.html+ the image in the data folder or change the code as it is explained if it is in a subfolder)::

        import android.content.res.AssetManager;
        import android.webkit.WebViewClient;
        import android.os.Looper;
        import android.content.Context;
        import android.view.ViewGroup.LayoutParams;
        import android.webkit.WebView;
        import android.view.ViewGroup;
        import android.app.Activity;
        import android.widget.RelativeLayout;
        import android.widget.FrameLayout;
        import java.io.File;
        import android.os.Bundle;
        import android.os.Environment;
    
    
    
        FrameLayout fl;
        Activity act;
        WebView web;
        Context context;
        WebViewClient wbc;///if you want to use chrome
        File[] files;//if you want to list files
        String[] filesPath;//if you want to list subfolders
    
    
    
        void settings(){
          fullScreen();
        };
    
        public void onStart() {
          super.onStart();
    
          act = this.getActivity();
          wbc = new WebViewClient();
          web = new WebView(act);
          web.setLayoutParams(new RelativeLayout.LayoutParams( RelativeLayout.LayoutParams.WRAP_CONTENT,RelativeLayout.LayoutParams.WRAP_CONTENT));
          web.setWebViewClient(wbc);
          web.getSettings().setJavaScriptEnabled(true);
          //loadFiles("autredos");//au cas où il y aurait un sous dossier "autredos" dans data
          web.loadUrl("file:///android_asset/grandeimage.html");//change that for one of the result from filesPath for subfolders; else this file must be in the data folder
          fl = (FrameLayout)act.getWindow().getDecorView().getRootView();
          fl.addView(web);
        };
    
        void setup(){
        background(255,0,0);
        };
    
        void draw(){
           background(255,0,0);
        };
    
        String[] loadFiles(String folderName_){//useless in most of cases
    
    
          AssetManager assetManager = act.getAssets();
          try{
            filesPath = assetManager.list(folderName_);//for files path in subfolder "autredos"
    
           files = new File[filesPath.length];//for files
    
    
             for(int i=0; i<filesPath.length; i++){
    
             files[i] = new File(folderName_+"/" + filesPath[i]);
             println("les fichiers d'autredos sont====" +files[i]);
            }
    
    
          }
          catch(IOException ex){
               System.out.println (ex.toString());   
          }
    
          return filesPath;
        };
    
  • @kfraier Thanks for the code, although I'm not using scrollview it is stored as template for another project. I have it working with webview in a smoothly way with the easing effect I wanted. @akenaton You are right, the image doesn't have to be contained within a html file, but can be displayed directly in web.loadUrl(). Now it fits even better without borders. Thanks both of you.

Sign In or Register to comment.