my live wallpaper doesn't slide smoothly most of the time on home screen change.
there is not a complicate coding for the position of background. it just use the xpixles that located on the onoffsetschanged method to adjust the x position of the wallpaper.
sometime it slide well same as in the sun rise live wallpaper app. but most of the other time it slide.. dunno how to describe it but its like a static sharp move. is there any idea about that guys.
best regards.
private void drawS() {
final SurfaceHolder holder = getSurfaceHolder();
canvas = null;
try {
canvas = holder.lockCanvas();
if (canvas != null) {
background = BitmapFactory.decodeResource(getResources(),
R.drawable.background);
canvas.drawBitmap(background, mXPixels, mYPixels, null);
}
}
finally {
if (canvas != null) {
holder.unlockCanvasAndPost(canvas);
}
}
handler.removeCallbacks(runnableS);
if (visible) {
handler.postDelayed(runnableS, 25);
}
}
try to avoid resource creation in draw routine; remove this from drawS() function:
background = BitmapFactory.decodeResource(getResources(),
R.drawable.background);
and put it wherever you initialize your stuff and just use reference to background Bitmap later on.
If you take a look at logcat you'll see that there's a lot or resource shuffling and memory management going on (allocating memory for your resource, decoding bitmap image in it etc) every time you call your drawS(); ie every time you draw your background...
Cheers,
Ivica
I encountered the same problem and solved by following Ivica suggestion.
I moved the bitmap creation line inside the surface creation sub:
public void onSurfaceCreated(SurfaceHolder holder) {
super.onSurfaceCreated(holder);
mybackground = BitmapFactory.decodeResource(getResources(), R.drawable.mybg);
}
In this way the sliding is very fluent
Related
I'm trying to develop a game using a transparent canvas and a surfaceView, I use the canvas to paint and it works fine.
Now I want to add objects that will move on the screen, for example bouncing balls.
I want to use a new thread for the balls, (because otherwise if it's the same thread of the surfaceView it doesn't move smoothly ).
So I'm having trouble of doing this.
Can you please advise me how and when should I send the same canvas to a new ball object and then return it back to the surfaceview.
I'm not sure what the best way to handle this is,
I tried using a ball as a view in the xml layout, and it worked perfect on another thread but it doesn't make sense to create 1000 views when I can just draw them on a canvas.
Any insight will be helpful!
This is my code:
public class SurfaceMyPaint extends SurfaceView implements Runnable
{
Thread t;
SurfaceHolder holder;
Bitmap brush;
boolean isItOk = false;
public SurfaceMyPaint(Context context)
{
super(context);
holder = getHolder();
initial();
}
public void initial()
{
brush= BitmapFactory.decodeResource(getResources(), R.drawable.brush2);
}
public boolean onTouchEvent(MotionEvent event)
{
if(event.getAction()== MotionEvent.ACTION_DOWN)
{
x= event.getX();
y= event.getY();
}
if(event.getAction()== MotionEvent.ACTION_MOVE)
{
x = event.getX();
y= event.getY();
}
return true;
}
public void run()
{
while (isItOk == true)
{
if (!holder.getSurface().isValid())
continue;
myCanvas_w = getWidth();
myCanvas_h = getHeight();
if (result == null)
result = Bitmap.createBitmap(myCanvas_w, myCanvas_h, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(result);
canvas = holder.lockCanvas(null);
c.drawBitmap(brush, x - (brush.getWidth() / 2), y - (brush.getWidth() / 2), null);
canvas.drawBitmap(result, 0, 0, null);
// How can I add this here: Ball ball = new Ball (canvas)????
holder.unlockCanvasAndPost(canvas);
}
}
public void pause(){
isItOk = false;
while (true){
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
break;
}
t=null;
}
public void resume()
{
isItOk = true;
t = new Thread(this);
t.start();
}
}
Summarizing your rendering code:
Create a screen-sized off-screen bitmap, which you hold in a variable called "result" that isn't declared in the code you show. I assume this is a field, and the allocation only happens once?
Each time, you create a new Canvas for that Bitmap.
You draw on the Bitmap, with drawBitmap().
You copy the Bitmap to the Surface, with drawBitmap().
You want to add a step #5, "draw Ball".
The obvious place to do the ball drawing is in step #3, but I'm going to assume you're not doing it there because that has some relatively static content that you update infrequently but want to blend in every frame.
That being the case, you should just draw the balls onto the Canvas you got back from SurfaceHolder. What you must not do is store a reference to the Surface's Canvas in the Ball object itself, which it appears you want to do (based on new Ball(canvas)). Once you call unlockCanvasAndPost() you must not use that Canvas anymore.
I don't really see an advantage to using multiple threads with your current code. If the "background" Bitmap were also changing you could render that with a different thread, and merge in the current version when you draw, but I suspect that will be more trouble than it's worth -- you will have to deal with the synchronization issues, and I don't think it'll solve your non-smoothness.
I'm trying to add different animations using matrix.setRotate (45), etc. to the Live Wallpaper, but it didn't work. All it did was showing the pictures in 45 degrees. I thought it would be rotating to 45 degrees?
I would like to add different animations to the slideshow live wallpaper like rotation, fading, translation, transformation, scaling, etc.
I even tried
matrix.setRotate(90);
matrix.setTranslate(100, 100);
but the pictures showed up very weird. Perhaps, there was any animation....
I know how to apply the animations from res/anim/animation.xml to NONE live wallpaper, but I can't seem to find a way to apply this animation.xml to the Live Wallpaper.
Is possible and easy way to apply animations to Live Wallpaper?
Thank you very much for your help in advance.
Java Code:
......
.....
....
private void drawFrame() {
// TODO Auto-generated method stub
final SurfaceHolder holder = getSurfaceHolder();
Canvas c = null;
try {
c = holder.lockCanvas();
if (c != null) {
drawPirate(c);
}
} finally {
if (c != null)
holder.unlockCanvasAndPost(c);
}
mhandler.removeCallbacks(drawrunnable);
if (mVisible) {
mhandler.postDelayed(drawrunnable);
}
}
private void drawPirate(Canvas c) {
// TODO Auto-generated method stub
Bitmap icon;
Matrix matrix = new Matrix();
matrix.setRotate(45);
icon = BitmapFactory.decodeResource(getResources(),pirates[i]);
c.drawBitmap(icon, matrix, null);
icon.recycle();
}
}
I'm not familiar with live wallpapers, but hopefully I can answer some of your other questions.
setRotate is not an animator, it rotates to its setting all at once
Good way to do this would be to create a thread for your drawFrame() and have it loop calling drawPirate(c), while incrementing the rotation value each time, so that you draw your image at setRotate(1), setRotate(2), etc until 45. You might want to look at the JetBoy android sample code for this kind of an implementation.
It is not good practice to allocate new things and decodeResources in the draw method. When you make multiple calls to it, it will have to create the new things every time and will slow things down a lot.
I'm working on an android app of mine, but there is a small problem I'm having that is limiting its functionality. I'm using the android canvas to draw an animation to the screen (for a live wallpaper), but whenever I try drawing using Canvas.drawColor() with a color like #01ffffff (the last two digits are the alpha level), the canvas gets darker! How can that be? I'm drawing white over the canvas, albeit a very transparent white, but still a white. How can it be getting darker?
The color is getting drawn every frame. It is used to provide a fade to white animation, but it doesn't fade to white.
My code:
public class MyWallpaperService extends WallpaperService {
...
private void draw() {
handler.removeCallbacks(drawRunner);
SurfaceHolder holder = getSurfaceHolder();
Canvas canvas = null;
if (holder.getSurface().isValid()) {
if (visible) {
handler.postDelayed(drawRunner, refreshRate);
}
try {
canvas = holder.lockCanvas();
if (canvas != null) {
canvas.drawColor(backgroundColor);
}
} finally {
if (canvas != null)
try{
holder.unlockCanvasAndPost(canvas);
} catch (Exception e) {
e.printStackTrace();
}
}
}
...
}
The draw() method is used to draw my wallpaper to the screen and within it has a call to canvas.drawColor(). The variable named backgroundColor is initialized to the color #01ffffff. So does anyone know why this is happening and what I can do to fix this? Thanks! (And any syntax errors in the code, like missing brackets, are probably due to my poor copy and pasting skills.)
Android color resources are defined with the first two digits as the transparency values. That may be your problem (as retailcoder suspected).
There is an ImageView and a GLSurfaceView setup with 'match_parent' parameters on the screen. The ImageView gets populated quickly and shows up on the screen. Then, the GLSurfaceView gets populated on top of it with modifications to the image. So far so good.
However, these views also live inside of a ViewPager with images to the left and right of them. Before introducing the OpenGL Surface View on top of the ImageView, the views would transition out to the left and right as expected. But after introducing the GLSurfaceView on top of it, as soon as the user starts to swipe their fingers to move to the next image, the SurfaceView becomes transparent causing the image to disappear. It even forces the image below it to disappear in the ImageView. Showing the background of the parent view.
I am unclear on how to approach this issue. It would be nice if the texture slid out to the side just like the ImageView or even be transparent but leave the ImageView behind it visible would be fine.
Even GLSurfaceView.RENDERMODE_CONTINUOSLY doesn't keep the texture around during the transition.
It would seem that the reason that I cannot get the effect that I am trying to achieve is because the GLSurfaceView is always placed behind other views causing transparency on the foreground views. Thus, the GLSurfaceView would have priority over being drawn. In order to achieve the effect that I would want, I probably need to be sure to use View.setVisibility( ) for invisible when swiping and set it back to visible after the ViewPager settles. That is the current conclusion that I have come across after seeing a couple other links as a by-product of different research.
z-order for GLSurfaceViews
scrolling in GLSurfaceViews
It doesn't seem as though the GLSurfaceView class really performs as similar to a view as I had been originally expecting. So a little extra care will need to be taken in order to progress further to keep the ImageView visible transitioning between images in the ViewPager.
Within a viewPager I managed to accomplish something along the lines of the following code. This is still in a rough state of course since it was in the middle of testing, but it accomplishes what I am looking for. Which is for a texture to start loading when we begin to settle on an image, and then disappear revealing the original image below it once the user begins to leave the image. And this seems to work without tearing or flickers which is how I would expect for it to behave.
#Override
public void onPageScrollStateChanged(int state)
{
switch(state)
{
case ViewPager.SCROLL_STATE_DRAGGING:
Log.i("photoViewer:","dragging ViewPager");
if(photoSurfaceView_ != null && photoViewFrameLayout != null)
{
photoSurfaceView_.setAlpha(0);
photoSurfaceView_.invalidate();
}
break;
case ViewPager.SCROLL_STATE_IDLE:
photoSurfaceView_.setVisibility(View.VISIBLE);
photoSurfaceView_.setAlpha(1);
Log.i("photoViewer:","idle ViewPager");
break;
case ViewPager.SCROLL_STATE_SETTLING:
int childCount = photoViewFrameLayout.getChildCount();
if(childCount >= 2)
{
photoViewFrameLayout.removeViews(1, childCount-1);
}
new Thread(new Runnable() {
#Override
public void run()
{
Looper.prepare();
final Bitmap openGlBitmap = BitmapFactoryUtils.resizeAsNecessaryForOpenGLtexture(photoPaths[photoViewPager_.getCurrentItem()], new BitmapFactory.Options());
Rect bounds = BitmapFactoryUtils.calculateBoundsForBitmap(activityContext, openGlBitmap);
int scaledHeight = bounds.height();
int scaledWidth = bounds.width();
photoSurfaceView_ = new PhotoViewSurfaceView(activityContext, openGlBitmap);
photoSurfaceView_.setLayoutParams(new LayoutParams(scaledWidth, scaledHeight, Gravity.CENTER));
photoSurfaceView_.setVisibility(View.VISIBLE);
photoSurfaceView_.setAlpha(0);
runOnUiThread(new Runnable() {
#Override
public void run()
{
photoViewFrameLayout.addView(photoSurfaceView_);
}
});
}
}).start();
Log.i("photoViewer:","settling ViewPager");
break;
}
Edit: As requested I wanted to provide some additional input as to how to resize the texture for an OpenGL SurfaceView that will use a Bitmap for input.
The following is a quick snippet of some of the code available for the first step in the process in checking against whether or not the texture size of the current Bitmap will fit within a single texture. If you want to tile the texture to keep the image larger, then this gets more complicated and this was not what was needed for my resolution.
The texture size can be retrieved with the following code:
private void assignTextureSize()
{
/* The maximum texture size can be found within the OpenGL context and then shared amongst other applications. */
int[] maxTextureSize = new int[1];
GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, maxTextureSize, 0);
maxTextureSize = maxTextureSize[0];
}
but only from within the Renderer class and for my case I waited until within the onDrawFrame of the Texture to do the assignment; before a Bitmap has been assigned to it.
Then, the goal of the Bitmap being loaded for me was to simply load what I could without running out of memory. For this, I simply modified the Options of the Bitmap that I was about to load and try and load it within a try catch block. If it ran out of memory, I would try it again, but with a smaller footprint.
try
{
final int sizeFactors[] = { 1, 2, 4, 8, 16, 32 };
if (new File(bitmapFilePath).exists())
{
BitmapFactory.Options options = new BitmapFactory.Options();
for (int i = 0; i < sizeFactors.length; ++i)
{
try
{
options.inSampleSize = sizeFactors[i];
Bitmap bmp = BitmapFactory.decodeFile(bitmapFilePath, options);
if(bmp.getHeight() > maximumTextureSize ||
bmp.getWidth() > maximumTextureSize)
{
continue;
}
/*
* #category Check against EXIF data if the image needs to be rotated or not for viewing.
*/
Matrix matrix = new Matrix ();
ExifInterface exif = new ExifInterface(bitmapFilePath);
int orientation = exif.getAttributeInt (ExifInterface.TAG_ORIENTATION, 1);
switch(orientation)
{
case ExifInterface.ORIENTATION_NORMAL:
break;
case ExifInterface.ORIENTATION_ROTATE_90:
matrix.postRotate (90);
break;
case ExifInterface.ORIENTATION_ROTATE_180:
matrix.postRotate (180);
break;
case ExifInterface.ORIENTATION_ROTATE_270:
matrix.postRotate (270);
break;
default:
}
bmp = Bitmap.createBitmap (bmp, 0, 0, bmp.getWidth (), bmp.getHeight (), matrix, true);
return bmp;
} catch (OutOfMemoryError outOfMemory)
{
//Do nothing, it will return when one that doesn't run out of memory is loaded. Or you could do additional checking to bail if needed, etc.
}
}
throw new Exception("Not enough memory for loading image");
}
} catch (Exception exception)
{
Log.e("CustomLogTag", "Exception for loading the image that was selected for extraction.");
}
There are many ways to accomplish this, but this is one approach to take and I wanted to add clarity and additional information to this previous post as requested since it did not contain as much information on my accepted answer as I would have like either.
How to avoid image flickering when drawing on canvas.
Below code keep on running for images.The next image comes sliding from top but the previous image keep on flickering at back.I just want the previous image to become static.
void drawscreen()
{
SurfaceHolder holder = getSurfaceHolder();
try
{
Backgroundcanvas = holder.lockCanvas();
if (Backgroundcanvas != null)
{
if (top == true)
{ slide_from_top(); }
}
}
finally
{
if (Backgroundcanvas != null)
holder.unlockCanvasAndPost(Backgroundcanvas);
}
mHandler.removeCallbacks(mUpdateDisplay);
if (mVisible && count)
{
mHandler.postDelayed(mUpdateDisplay, interval);
count = false;
}
else
{
mHandler.postDelayed(mUpdateDisplay, 100);
}
}
I was also getting same problem, image flickering when drawing on canvas .
What I have done is making my worker thread(Not UI Thread) sleep for 10mlsec every time after using canvas drawing, and this time flickering is gone,
You can try this trick...
Clear canvas after each thread ends.
Just call Canvas.drawColor(Color.BLACK), or whatever color you want to clear your Canvas with.