I need an ImageView that can be touched zoom in/out and pan. Each time when I touch the ImageView, I need to know the touch position of the source image. For example the image resolution is 1280*720, even the image in the ImageView is zoomed in, I still know exactly the touch position of the image(not touch position of the ImageView)
Thanks.
Why don't you skip all that and use an existing library?
Try this: PhotoView works perfectly for me on API 23, with just a few lines of code you will be able to zoom in/out and pan
compile 'com.github.chrisbanes:PhotoView:1.3.0'
repositories {
...
maven { url "https://jitpack.io" }
}
Example
ImageView mImageView;
PhotoViewAttacher mAttacher;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Any implementation of ImageView can be used!
mImageView = (ImageView) findViewById(R.id.iv_photo);
// Set the Drawable displayed
Drawable bitmap = getResources().getDrawable(R.drawable.wallpaper);
mImageView.setImageDrawable(bitmap);
// Attach a PhotoViewAttacher, which takes care of all of the zooming functionality.
// (not needed unless you are going to change the drawable later)
mAttacher = new PhotoViewAttacher(mImageView);
}
// If you later call mImageView.setImageDrawable/setImageBitmap/setImageResource/etc then you just need to call
mAttacher.update();
Finally, I used this open source code
https://github.com/sephiroth74/ImageViewZoom
I modified the code, but there is still a lot of bugs in this open source project, I have fixed several of them.
The following function was added to class ImageViewTouch.GestureListener which is used to transfer touchpoint(in screen) to point in the real image(no matter the image is zoom in/out).
private PointF calculatePositionInSourceImage(PointF touchPointF) {
//point relative to imageRect
PointF touchPointRelativeToImageRect = new PointF();
RectF imageRect = getBitmapRect();
touchPointRelativeToImageRect.set(touchPointF.x - imageRect.left,
touchPointF.y - imageRect.top);
//real image resolution
int imageWidth = getDrawable().getIntrinsicWidth();
int imageHeight = getDrawable().getIntrinsicHeight();
//touch point in image
PointF touchPointRelativeToImage = new PointF();
touchPointRelativeToImage.set(touchPointRelativeToImageRect.x/imageRect.width() * imageWidth,
touchPointRelativeToImageRect.y/imageRect.height() * imageHeight);
if(touchPointRelativeToImage.x < 0 || touchPointRelativeToImage.y < 0 )
touchPointRelativeToImage.set(0,0);
return touchPointRelativeToImage;
}
Related
Am creating a document scanning application in android, am using OpenCV and Scan library in my project for cropping,I have created a rectangle using drawrect in camera view, now I need to capture the images inside that rectangle portion only and display it in another activity.
The image in question:
For me , I will take whole image, then crop.
Your question : "how do I know which part of the image is inside the rectangular portion, then only I can pass it nah, hope u understood". My answer is you can using relativity scaling of whole image dimension and camera display screen dimension. Then you will know which part of rectangular to be cropped.
This is the code example.
Note that you need to fill some codes to make it can save file into jpg, and save it after cropped.
// 1. Save your bitmap to file
public class MyPictureCallback implements Camera.PictureCallback {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
try {
//mPictureFile is a file to save the captured image
FileOutputStream fos = new FileOutputStream(mPictureFile);
fos.write(data);
fos.close();
} catch (FileNotFoundException e) {
Log.d(TAG, "File not found: " + e.getMessage());
}
}
}
// Somewhere in your code
// 2.1 Load bitmap from your .jpg file
Bitmap bitmap = BitmapFactory.decodeFile(path+"/mPictureFile_name.jpg");
// 2.2 Rotate the bitmap to be the same as display, if need.
... Add some bitmap rotate code
// 2.3 Size of rotated bitmap
int bitWidth = bitmap.getWidth();
int bitHeight = bitmap.getHeight();
// 3. Size of camera preview on screen
int preWidth = preview.getWidth();
int preHeight = preview.getHeight();
// 4. Scale it.
// Assume you draw Rect as "canvas.drawRect(60, 50, 210, 297, paint);" command
int startx = 60 * bitWidth / preWidth;
int starty = 50 * bitHeight / preHeight;
int endx = 210 * bitWidth / preWidth;
int endy = 297 * bitHeight / preHeight;
// 5. Crop image
Bitmap blueArea = Bitmap.createBitmap(bitmap, startx, starty, endx, endy);
// 6. Save Crop bitmap to file
This will work for you: How to programmatically take a screenshot in Android?
Make sure that the view (v1 in the code sample's case) passed in Bitmap.createBitmap(v1.getDrawingCache()) is a viewgroup that contains the image you want ot send to the second activity
Edit:
I don't think your intended flow is feasible. As far as I know, camera intents don't take arguments allowing to draw such a rectangle (I could be wrong though).
Instead, I suggest you take a picture, and then edit it with a library such as this one (https://github.com/ArthurHub/Android-Image-Cropper) or programatically as suggested above.
i have little problem.
I'm getting an image,converting to Bitmap and fill imageview with this Bitmap.
So the problem is,how to take real coordinates(X,Y) of this Bitmap?
For better illustration, i attached an image:
As shown on picture, for e.g. i got an image with Custom Resolution,where user can make an perspective , via yellow points.
So i need to know,how to take real coords of bitmap from ImageView.
What i've made at current moment(but this approach isn't correct):
i take width/height from imageView and recreating new bitmap with this resolution.
Bitmap bt = Bitmap.CreateScaledBitmap(BitmapFactory.DecodeResource(Activity.Resources, Resource.Drawable.test),800,1420,false); //800 width 1420 height
imgView.SetImageBitmap(bt);
Via implementation of TouchListener(methods GetX()/GetY()),i get coords of points(yellow circle's that overlayed on Bitmap), and the problem is that these coordinates not correct.
Also its interesting for me case when i'm stretching bitmap via ScaleType.FitXY on imageView(its possible to take real coords of bitmap) ?
So how can i achieve my goal?
use ImageView#getImageMatrix, something like this:
final ImageView iv = new ImageView(this);
setContentView(iv);
// setup your image here by
// calling for example iv.setImageBitmap()
// or iv.setImageDrawable()
// or iv.setImageResource()
View.OnTouchListener otl = new View.OnTouchListener() {
Matrix inverse = new Matrix();
#Override
public boolean onTouch(View v, MotionEvent event) {
iv.getImageMatrix().invert(inverse);
float[] pts = {
event.getX(), event.getY()
};
inverse.mapPoints(pts);
Log.d(TAG, "onTouch x: " + Math.floor(pts[0]) + ", y: " + Math.floor(pts[1]));
return false;
}
};
iv.setOnTouchListener(otl);
I am trying to animate an ImageView using TranslateAnimation from it's current position (0,Yscreensize/2) to the other side of the screen (Xscreensize,imageview.getY()) but I can't manage it to work. here is my code:
dart = (ImageView) findViewById(R.id.dart);
Display disp = getWindowManager().getDefaultDisplay();
Point poi = new Point();
disp.getSize(poi);
sizex = poi.x;
sizey = poi.y;
ViewTreeObserver vto = dart.getViewTreeObserver();
vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
public boolean onPreDraw() {
dart.getViewTreeObserver().removeOnPreDrawListener(this);
finalHeight = dart.getMeasuredHeight();
dart.setY(sizey / 2 - finalHeight);
return true;
}
}); // till now - got screen size and set dart imageview to Y middle.
Now when I am trying to use the animation:
TranslateAnimation animation = new TranslateAnimation(dart.getX(), sizex, dart.getY(), dart.getY());
animation.setDuration(10000);
dart.startAnimation(animation); // from current x to screen size, from currenty to currenty.
this will not work and the dart just dissappear. what can I do?
Make the variable for the animation a class variable (to ensure it won't be garbage collected to soon). In order to make the ImageView stop its movement before it leaves the screen, you need an additional class variable
float finalWidth;
Then change your OnPreDrawListener like this:
vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener()
{
public boolean onPreDraw()
{
dart.getViewTreeObserver().removeOnPreDrawListener(this);
float finalHeight = dart.getMeasuredHeight();
// if you want the view to stop before it leaves the screen
float finalWidth = dart.getMeasuredWidth();
animation = new TranslateAnimation(0, sizex - finalWidth, 0, 0);
animation.setDuration(10000);
// use this if you want the changes to persist
// animation.setFillAfter(true);
dart.setY(sizey / 2 - finalHeight);
return true;
}
});
I used a button to call 'startAnimation()'. The only problem when testing with an emulator is that the ImageView keeps running until it reaches the emulator window egde. So it disappears below the hardware controls region on the right side. In order to make it stop earlier, I tested with
Rect myRect = new Rect();
dart.getWindowVisibleDisplayFrame(myRect);
sizex = myRect.width() * 0.9f;
sizey = myRect.height();
which almost did the trick. It's just an approximation, all right, but getting the exact dimensions of the display - taking into account paddings or insets - seems to be difficult. At least below API 20.
Hope this helps anyway :)
I am using chrisbanes PhotoView to implement pinch zoom..Image zooms on pinching and double tapping but i can't see that my image streched to full screen on zooming..on zooming it looks that image zooms inside a box and part of image disappears on zooming...How can i implement image zoom so that height of Image increases on zooming?I am using NetworkImageView ( of Volley library).
NetworkImageView imageView;
PhotoViewAttacher mAttacher;
imageView = (NetworkImageView) mImgPagerView.findViewById(R.id.imageitem);
mAttacher = new PhotoViewAttacher(imageView);
NetworkImageView.java ( of Volley library)
import android.content.Context;
import android.graphics.Bitmap;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.widget.ImageView;
import com.android.volley.toolbox.ImageLoader.ImageContainer;
public class NetwrokImageView extends ImageView {
/** The URL of the network image to load */
private String mUrl;
/**
* Resource ID of the image to be used as a placeholder until the network image is loaded.
*/
private int mDefaultImageId;
/**
* Resource ID of the image to be used if the network response fails.
*/
private int mErrorImageId;
/** Local copy of the ImageLoader. */
private ImageLoader mImageLoader;
public NetworkImageView(Context context) {
this(context, null);
}
public NetworkImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public NetworkImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/**
* Sets URL of the image that should be loaded into this view. Note that calling this will
* immediately either set the cached image (if available) or the default image specified by
* {#link NetworkImageView#setDefaultImageResId(int)} on the view.
*
* NOTE: If applicable, {#link NetworkImageView#setDefaultImageResId(int)} and
* {#link NetworkImageView#setErrorImageResId(int)} should be called prior to calling
* this function.
*
* #param url The URL that should be loaded into this ImageView.
* #param imageLoader ImageLoader that will be used to make the request.
*/
public void setImageUrl(String url, ImageLoader imageLoader) {
mUrl = url;
mImageLoader = imageLoader;
// The URL has potentially changed. See if we need to load it.
loadImageIfNecessary();
}
/**
* Sets the default image resource ID to be used for this view until the attempt to load it
* completes.
*/
public void setDefaultImageResId(int defaultImage) {
mDefaultImageId = defaultImage;
}
/**
* Sets the error image resource ID to be used for this view in the event that the image
* requested fails to load.
*/
public void setErrorImageResId(int errorImage) {
mErrorImageId = errorImage;
}
/**
* Loads the image for the view if it isn't already loaded.
*/
private void loadImageIfNecessary() {
int width = getWidth();
int height = getHeight();
// if the view's bounds aren't known yet, hold off on loading the image.
if (width == 0 && height == 0) {
return;
}
// if the URL to be loaded in this view is empty, cancel any old requests and clear the
// currently loaded image.
if (TextUtils.isEmpty(mUrl)) {
ImageContainer oldContainer = (ImageContainer) getTag();
if (oldContainer != null) {
oldContainer.cancelRequest();
setImageBitmap(null);
}
return;
}
ImageContainer oldContainer = (ImageContainer) getTag();
// if there was an old request in this view, check if it needs to be canceled.
if (oldContainer != null && oldContainer.getRequestUrl() != null) {
if (oldContainer.getRequestUrl().equals(mUrl)) {
// if the request is from the same URL, return.
return;
} else {
// if there is a pre-existing request, cancel it if it's fetching a different URL.
oldContainer.cancelRequest();
setImageBitmap(null);
}
}
// The pre-existing content of this view didn't match the current URL. Load the new image
// from the network.
ImageContainer newContainer = mImageLoader.get(mUrl,
ImageLoader.getImageListener(this, mDefaultImageId, mErrorImageId));
// update the tag to be the new bitmap container.
setTag(newContainer);
// look at the contents of the new container. if there is a bitmap, load it.
final Bitmap bitmap = newContainer.getBitmap();
if (bitmap != null) {
setImageBitmap(bitmap);
}
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
loadImageIfNecessary();
}
#Override
protected void onDetachedFromWindow() {
ImageContainer oldContainer = (ImageContainer) getTag();
if (oldContainer != null) {
// If the view was bound to an image request, cancel it and clear
// out the image from the view.
oldContainer.cancelRequest();
setImageBitmap(null);
// also clear out the tag so we can reload the image if necessary.
setTag(null);
}
super.onDetachedFromWindow();
}
#Override
protected void drawableStateChanged() {
super.drawableStateChanged();
invalidate();
}
}
build.gradle
compile 'com.github.chrisbanes.photoview:library:+'
xml
<com.xyz.NetworkImageView
android:id="#+id/imageitem"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="matrix"
android:layout_gravity="center"
android:adjustViewBounds="true"
android:background="#drawable/image_loading" />
ImageView is inside FrameLayout
<FrameLayout
android:id="#+id/image_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:background="#color/black"
>
Image before zooming
Image after zooming
Better try a different approach if youre stuck
You can find below a link to a class created by Jason Polites that will allow you to handle pinch zooms on custom ImageViews: https://github.com/jasonpolites/gesture-imageview.
Just include this package into your application and then you will be able to use a custom GestureImaveView in your XML files:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:gesture-image="http://schemas.polites.com/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.polites.android.GestureImageView
android:id="#+id/image"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:src="#drawable/image"
gesture-image:min-scale="0.1"
gesture-image:max-scale="10.0"
gesture-image:strict="false"/>
This class handles pinch zooms, but also double taps.
Answer credit goes to Yoann :)
I will highly recommend having a look at Photo View:
https://github.com/chrisbanes/PhotoView
It was developed by Chris Banes who is one of the actual developers that worked on the Android development team, so you can't go wrong here. This library will save you A LOT of headaches.
Usage is as simple as:
ImageView mImageView;
PhotoViewAttacher mAttacher;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Any implementation of ImageView can be used!
mImageView = (ImageView) findViewById(R.id.iv_photo);
// Set the Drawable displayed
Drawable bitmap = getResources().getDrawable(R.drawable.wallpaper);
mImageView.setImageDrawable(bitmap);
// Attach a PhotoViewAttacher, which takes care of all of the zooming functionality.
mAttacher = new PhotoViewAttacher(mImageView);
}
I created a fully functional project on github. link in the end of the answer.
Problem elements:
1. Getting touch events and use it's variables to set image zoom level and window. (left, top, right, bottom).
Sample Code: only part of image will be showing at a time. therefore setting android:scaleType="fitCenter" will achieve the zooming.
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/imageFrameLayout"
android:background="#android:color/black">
<ImageView android:id="#+id/imageView"
android:layout_width="0px"
android:layout_height="0px"
android:layout_gravity="center"
android:scaleType="fitCenter"
android:background="#android:color/transparent" />
</FrameLayout>
Touch Listener (you can modify this to add your click event)
OnTouchListener MyOnTouchListener = new OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent event) {
float distx, disty;
switch(event.getAction() & MotionEvent.ACTION_MASK){
case MotionEvent.ACTION_DOWN:
//A pressed gesture has started, the motion contains the initial starting location.
touchState = TOUCH;
currentX = (int) event.getRawX();
currentY = (int) event.getRawY();
break;
case MotionEvent.ACTION_POINTER_DOWN:
//A non-primary pointer has gone down.
touchState = PINCH;
//Get the distance when the second pointer touch
distx = event.getX(0) - event.getX(1);
disty = event.getY(0) - event.getY(1);
dist0 = (float) Math.sqrt(distx * distx + disty * disty);
distLast = dist0;
break;
case MotionEvent.ACTION_MOVE:
//A change has happened during a press gesture (between ACTION_DOWN and ACTION_UP).
if(touchState == PINCH){
// pinch started calculate scale step.
//Get the current distance
distx = event.getX(0) - event.getX(1);
disty = event.getY(0) - event.getY(1);
distCurrent = (float) Math.sqrt(distx * distx + disty * disty);
if (Math.abs(distCurrent-distLast) >= 35) // check sensitivity
{
stepScale = distCurrent/dist0;
distLast = distCurrent;
drawMatrix(); // draw new image
}
}
else
{
// move started.
if (currentX==-1 && currentY==-1)
{
// first move after touch down
currentX = (int) event.getRawX();
currentY = (int) event.getRawY();
}
else
{
// calculate move window variable.
int x2 = (int) event.getRawX();
int y2 = (int) event.getRawY();
int dx = (currentX - x2);
int dy = (currentY - y2);
left += dx;
top += dy;
currentX = x2;
currentY = y2;
drawMatrix(); // draw new image window
}
}
break;
case MotionEvent.ACTION_UP:
//A pressed gesture has finished.
touchState = IDLE;
break;
case MotionEvent.ACTION_POINTER_UP:
//A non-primary pointer has gone up.
if (touchState == PINCH)
{
// pinch ended. reset variable.
}
touchState = TOUCH;
break;
}
return true;
}
};
2. Since zooming required. I assume that we are using high quality images.
Therefore, loading the full sized quality image when zoomed out won't benefit, since the user won't recognise the small details. But it will increase memory usage and could crash for very large images.
Solution Trick: On zooming out load larger window with lower quality.
On zoom in load smaller window with higher quality.
The proposed solution checks the current zoom level and image window required and based on that gets only part of the image with specific quality using BitmapRegionDecoder and BitmapFactory.
Sample Code:
Initialise image decoder that is going to be used later to ask for a part of the image:
InputStream is = null;
bitmapRegionDecoder = null;
try {
is = getAssets().open(res_id); // get image stream from assets. only the stream, no mem usage
bitmapRegionDecoder = BitmapRegionDecoder.newInstance(is, false);
} catch (IOException e) {
e.printStackTrace();
}
bounds = new BitmapFactory.Options();
bounds.inJustDecodeBounds = true; // only specs needed. no image yet!
BitmapFactory.decodeStream(is, null, bounds); // get image specs.
try {
is.close(); // close stream no longer needed.
} catch (IOException e) {
e.printStackTrace();
}
Ask for image with quality and window:
Rect pRect = new Rect(left, top, left + newWidth, top + newHeight);
BitmapFactory.Options bounds = new BitmapFactory.Options();
int inSampleSize = 1;
if (tempScale <= 2.75) // you can map scale with quality better than this.
inSampleSize = 2;
bounds.inSampleSize = inSampleSize; // set image quality. takes binary steps only. 1, 2, 4, 8 . .
bm = bitmapRegionDecoder.decodeRegion(pRect, bounds);
imageView.setImageBitmap(bm);
project on github.
I had faced this issue in photo view image zoom library. i was changed the layout_height is "wrap_content" to "match_parent. It's working fine for me.
https://github.com/chrisbanes/PhotoView
<com.xyz.NetworkImageView
android:id="#+id/imageitem"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="matrix"
android:layout_gravity="center"
android:adjustViewBounds="true"
android:background="#drawable/image_loading" />
If you wish to change the ImageView's size, you need to change its layoutParams, as I've demonstrated here. You can also change its scale as of Honeycomb.
However, if you wish to just move a rectangle within the image, which also zooms nicely (like other apps that allow to crop), you can use a library like "cropper" or if you don't need the features there you can use a library like TouchImageView
It is because the ImageView has a view height of "wrap_content". Change it to "match_parent" and set the scaleType of the image to be "centerInside" to fix your problem.
The problem is that when using NetworkImageView does not behave the same as an ImageView, I solve it by creating an xml design for portrait and landscape
PORTRAIT
layout/item_image.xml
height="match_parent" and width="wrap_content", important scaleType="fitCenter"
<com.android.volley.toolbox.NetworkImageView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:scaleType="fitCenter"
android:background="#android:color/transparent"
android:id="#+id/imageitem"/>
LAND
layout/land/item_image.xml
now in the land layout the height="wrap_content" and width="match_parent", always scaleType="fitCenter"
<com.android.volley.toolbox.NetworkImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitCenter"
android:background="#android:color/transparent"
android:id="#+id/imageitem"/>
It's also important to instantiate the PhotoViewAttacher object after assigning the resource to the NetworkImageView control
NetworkImageView imageView;
PhotoViewAttacher mAttacher;
imageView = (NetworkImageView)
mImgPagerView.findViewById(R.id.imageitem);
//firts set the image resources, i'm using Android Volley Request
ImageLoader imageLoader = MySocialMediaSingleton.getInstance(context).getImageLoader();
imageView.setImageUrl(url, imageLoader);
//next create instance of PhotoViewAttecher
mAttacher = new PhotoViewAttacher(imageView);
mAttacher.update();
I use imageviewtouch and have same problem. To solve it you can wrap imageview inside layout without any other children. Example :
<LinearLayout
android:id="#+id/layout"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="200dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/toplayout">
<com.android.volley.toolbox.NetworkImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitCenter"
android:background="#android:color/transparent"
android:id="#+id/imageitem"/>
</LinearLayout>
So I have a bitmap that I have loaded from a resource file (an PNG image):
Bitmap map = BitmapFactory.decodeResource(getResources(), R.drawable.wave);
If I draw this bitmap only once using canvas.drawBitmap(...); then there is no problem. However, If I draw that very same bitmap multiple times, then the picture keeps flashing back and forth, not steady like before.
I suspected that I cannot use the same bitmap more than once so I tried to load the image into a new bitmap every time when I want to draw the same picture, but it does not help, the behavior still persists.
The program is complicated, but basically, I want to draw a ocean wave. I have a image of a small wave. To make the effect of the wave moving from the left edge of the screen to the right edge. I keep track of the position of the left edge of the bitmap.
// The ocean.
private ArrayList<Wave> waves;
// Draw the waves and update their positions.
for (int i = 0; i < this.waves.size(); i++)
{
Wave wave = this.waves.get(i);
// Go through each of the sub-waves of this current wave.
for (int j = 0; j < wave.getSubWaveEdges().size(); j++)
{
// Get the sub wave.
final float subWaveEdge = wave.getSubWaveEdges().get(j);
canvas.drawBitmap( wave.getSubWave(j), subWaveEdge, 40, brush);
wave.setSubWaveEdge(j, subWaveEdge + (float) 0.5);
}
// Update this current wave.
wave.update();
// If the wave has passed the left edge of the screen then add a new sub-wave.
if (wave.getFarthestEdge() >= 0)
wave.addSubWaveEdges(wave.getFarthestEdge() - this.getWidth());
}
If the left edge of a bitmap is inside the screen then I create a new bitmap from the same image file and draw. Here is the class Wave:
private class Wave
{
private Bitmap wave;
private float farthestEdge;
private ArrayList<Float> subWaveEdges;
private ArrayList<Bitmap> subWaves;
public Wave(Bitmap wave)
{
this.wave = wave;
this.farthestEdge = 0;
this.subWaveEdges = new ArrayList<Float>();
this.subWaves = new ArrayList<Bitmap>();
}
public Bitmap getWave ()
{ return this.wave; }
public void setWave (Bitmap wave)
{ this.wave = wave; }
public float getFarthestEdge ()
{ return this.farthestEdge; }
public void setFarthestEdge (final float furthestEdge)
{ this.farthestEdge = furthestEdge; }
public ArrayList<Float> getSubWaveEdges ()
{ return subWaveEdges; }
public void setSubWaveEdge (final int index, final float value)
{
this.subWaveEdges.remove(index);
this.subWaveEdges.add(value);
}
public void addSubWaveEdges (final float edge)
{
this.subWaveEdges.add(edge);
Bitmap newSubWave = BitmapFactory.decodeResource(getResources(), R.drawable.wave);
newSubWave = Bitmap.createScaledBitmap(newSubWave, MasterView.this.getWidth(), newSubWave.getHeight(), true);
this.subWaves.add(newSubWave);
}
public Bitmap getSubWave(final int index)
{ return this.subWaves.get(index); }
public void update ()
{
// Check to see if there is any sub-wave going outside of the screen.
// If there is then remove that wave.
for (int index = 0; index < this.subWaveEdges.size(); index++)
if (this.subWaveEdges.get(index) > MasterView.this.getWidth())
{
this.subWaveEdges.remove(index);
this.subWaves.remove(index);
}
// Set the farthest edge to the other side of the screen.
this.farthestEdge = MasterView.this.getWidth();
// Get the farthest edge of the wave.
for (int index = 0; index < this.subWaveEdges.size(); index++)
if (this.subWaveEdges.get(index) < this.farthestEdge)
this.farthestEdge = this.subWaveEdges.get(index);
}
}
Another suspicion that I have is that may be when I create two bitmaps from the same resource file, the pixels of the image are divided among two bitmaps, meaning that each bitmap only gets part of the pixels, not all. I am suspecting this because when the bitmaps are drawn, the parts where they overlaps are drawn steadily, no flashing.
Anyone has stumbled upon this problem and know how to fix?
Thanks,
Viktor Lannér, Thank you for helping, but I don't think that's the problem. I understand it is hard to read my codes since it is only a small piece of the big program.
However, I found the problem: This is not mentioned in my original question, but in order to simulate the two waves moving after one another, I have to draw the next wave as soon as the first wave enters the screen. However, each wave is longer than the width of the screen. Therefore, I have to draw the next wave from "outside" the screen if you know what I mean. It means that the next wave is drawn from a negative x-coordinate from outside the screen:
// If the wave has passed the left edge of the screen then add a new sub-wave.
if (wave.getFarthestEdge() >= 0)
wave.addSubWaveEdges(wave.getFarthestEdge() - this.getWidth());
And I found out that it does not like this. This is what causes the flashing back and forth.
In order to fix this, instead of drawing the next wave from outside the screen, I use this method:
canvas.drawBitmap (Bitmap bitmap, Rect source, Rect destination, Paint paint)
This method allows you to specify a rectangular region on the bitmap to be drawn to the screen and a rectangular region on the screen where that part of the bitmap will be drawn over. I use this method to draw the next wave. As the next wave moves into the screen, I change the "source" and "destination" appropriately to draw parts of the bitmap.
I just wanted to say that I had an issue where the images on my canvas were flashing back and forth, or, flashing between black and my first frame until I made a movement, almost as if the canvas was rapidly switching between its current and last image.
This might have had something to do with your situation, and to fix it I found out that it was because I was locking the canvas every frame, even when I had nothing to draw. For whatever reason, that lock, I think, created this situation.
I got around it by doing something like this:
if (needToRedraw == true) {
canvas = mSurfaceHolder.lockCanvas(null);
... logic to eventually draw on that canvas ...
}
Before canvas.drawBitmap(...) call; try to use canvas.drawColor(Color.BLACK) to clear the Canvas from previous drawings.
Sample code:
// Stuff.
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(wave.getSubWave(j), subWaveEdge, 40, brush);
// Stuff.