Currently I am working on live wallpaper and I am stuck at a point. Actually, my live wallpaper only works when I touch the screen, but it doesn't change continuously. There is a problem in my loop.
class Diwali extends Engine {
private boolean mVisible;
private final Runnable diwaliImg = new Runnable() {
public void run() {
drawFrame();
}
};
int i=0;
int[] pirates = {
R.drawable.a1, R.drawable.a2,
R.drawable.a3, R.drawable.a4,
R.drawable.a5, R.drawable.a6,
R.drawable.a7, R.drawable.a8,
R.drawable.a9, R.drawable.a10,
R.drawable.a11, R.drawable.a12,
R.drawable.a13, R.drawable.a14
};
#Override
public void onCreate(SurfaceHolder holder){
super.onCreate(holder);
}
#Override
public void onDestroy() {
super.onDestroy();
mHandler.removeCallbacks(diwaliImg);
}
#Override
public void onVisibilityChanged(boolean visible) {
mVisible = visible;
if (visible) {
drawFrame();
} else {
mHandler.removeCallbacks(diwaliImg);
}
}
#Override
public void onSurfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
super.onSurfaceChanged(holder, format, width, height);
drawFrame();
}
#Override
public void onSurfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
super.onSurfaceCreated(holder);
}
#Override
public void onSurfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
super.onSurfaceDestroyed(holder);
mVisible = false;
mHandler.removeCallbacks(diwaliImg);
}
#Override
public void onOffsetsChanged(float xOffset, float yOffset, float xStep,float yStep, int xPixels, int yPixels) {
drawFrame();
}
#Override
public void onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
}
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(diwaliImg);
}
private void drawPirate(Canvas c) {
// TODO Auto-generated method stub
Bitmap icon = BitmapFactory.decodeResource(getResources(),pirates[i]);
i++;
if (i == 13) {
i = 0;
}
Matrix matrix = new Matrix();
c.drawBitmap(icon, matrix, null);
icon.recycle();
}
}
After adding this
if (mVisible) {
Handler.postDelayed(diwaliImg,80);
}
At the end draw frame() my problem was solved.
There is no loop in your code. You need to call drawFrame() repeatedly.
To achieve this you may use separate thread for animation. I recommend you my wallpaper template available on GitHub.
Related
In My application, I want to make one countdown Screen for remaining days (like Countdown the days till Christmas) and I want to set that remaining days (With some animation) in live wallpaper screen as shown in the image.
And I need to play some music when click or double tap in the wallpaper.
I am creating GifLiveWallPaper class for set my custom live wallpaper.
GifLiveWallPaper
public class GifLiveWallPaper extends WallpaperService {
static final String TAG = "LIVE_WALLPAPER";
static final Handler liveHandler = new Handler();
#Override
public Engine onCreateEngine() {
try {
return new WallPaperEngine();
} catch (IOException e) {
Log.w(TAG, "Error creating WallPaperEngine", e);
stopSelf();
return null;
}
}
class WallPaperEngine extends Engine {
private Movie liveMovie;
private int duration;
private Runnable runnable;
float mScaleX;
float mScaleY;
int mWhen;
long mStart;
public WallPaperEngine() throws IOException {
InputStream is = getResources().openRawResource(R.raw.my_gif_image);
if (is != null) {
try {
liveMovie = Movie.decodeStream(is);
duration = liveMovie.duration();
} finally {
is.close();
}
} else {
throw new IOException("Unable to open R.raw.hand");
}
mWhen = -1;
runnable = new Runnable() {
public void run() {
nyan();
}
};
}
#Override
public void onDestroy() {
super.onDestroy();
liveHandler.removeCallbacks(runnable);
}
#Override
public void onVisibilityChanged(boolean visible) {
super.onVisibilityChanged(visible);
if (visible) {
nyan();
} else {
liveHandler.removeCallbacks(runnable);
}
}
#Override
public void onSurfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
super.onSurfaceChanged(holder, format, width, height);
mScaleX = width / (1f * liveMovie.width());
mScaleY = height / (1f * liveMovie.height());
nyan();
}
#Override
public void onOffsetsChanged(float xOffset, float yOffset,
float xOffsetStep, float yOffsetStep, int xPixelOffset,
int yPixelOffset) {
super.onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep,
xPixelOffset, yPixelOffset);
nyan();
}
void nyan() {
tick();
SurfaceHolder surfaceHolder = getSurfaceHolder();
Canvas canvas = null;
try {
canvas = surfaceHolder.lockCanvas();
if (canvas != null) {
drawGif(canvas);
}
} finally {
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
liveHandler.removeCallbacks(runnable);
if (isVisible()) {
liveHandler.postDelayed(runnable, 1000L / 25L);
}
}
void tick() {
if (mWhen == -1L) {
mWhen = 0;
mStart = SystemClock.uptimeMillis();
} else {
long mDiff = SystemClock.uptimeMillis() - mStart;
mWhen = (int) (mDiff % duration);
}
}
void drawGif(Canvas canvas) {
canvas.save();
canvas.scale(mScaleX, mScaleY);
liveMovie.setTime(mWhen);
liveMovie.draw(canvas, 0, 0);
canvas.restore();
}
}
}
How can I set remaining days and music as described in above ?
To play some music when click or double tap in the live wallpaper, add the following code to GifLiveWallPaper class. (Place your_music_file in res--> raw folder)
MediaPlayer mPlayer=new MediaPlayer();
#Override
public void onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
mDoubleTapDetector.onTouchEvent(event);
}
private GestureDetector mDoubleTapDetector = new GestureDetector(GifLiveWallPaper.this,
new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onDoubleTap(MotionEvent e) {
if(!mPlayer.isPlaying())
{
mPlayer= MediaPlayer.create(GifLiveWallPaper.this, R.raw.your_music_file);
mPlayer.start();
}
else
{
mPlayer.stop();
}
return true;
}
});
#Override
public void onVisibilityChanged(boolean visible) {
super.onVisibilityChanged(visible);
if (!visible) {
if(mPlayer.isPlaying())
{
mPlayer.stop();
}
}
}
#Override
public void onDestroy() {
super.onDestroy();
liveHandler.removeCallbacks(runnable);
if(mPlayer.isPlaying())
{
mPlayer.stop();
}
}
What I want to achieve with this program is , when my finger is touching the image and I am moving(dragging) it on the screen , the image should move along , and as soon as I release my finger , the image should remain at the last touched position. But my image keeps moving on the screen from first touched position to last touched position as the loop keeps updating, giving me this effect.
Here is MainGamePanel class which draws image on screen:
public class MainGamePanel extends SurfaceView implements SurfaceHolder.Callback{
private static final String TAG = MainGamePanel.class.getSimpleName();
private MainThread thread;
private Droid droid;
public MainGamePanel(Context context) {
super(context);
getHolder().addCallback(this);
setFocusable(true);
thread = new MainThread(getHolder() , this);
droid = new Droid(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher), 50, 50);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
thread.setRunning(true);
thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
while(retry)
{
try {
thread.join();
retry = false;
} catch (InterruptedException e) { }
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN)
{
droid.handleActionDown((int)event.getX(), (int)event.getY());
if(event.getY() > getHeight() - 50)
{
thread.setRunning(false);
((Activity) getContext()).finish();
}
else
{
Log.d(TAG, "Cords: x = "+event.getX()+" ,y = "+event.getY());
}
}
if(event.getAction() == MotionEvent.ACTION_MOVE)
{
if(droid.isTouched())
{
droid.setX((int)event.getX());
droid.setY((int)event.getY());
}
}
if(event.getAction() == MotionEvent.ACTION_UP)
{
if(droid.isTouched())
{
droid.setTouched(false);
}
}
return true;
}
#Override
protected void onDraw(Canvas canvas) {
droid.draw(canvas);
}
}
And here is the handleActionDown method of Droid class:
public void handleActionDown(int eventX, int eventY)
{
if(eventX >=(x - bitmap.getWidth()/2) && (eventX <= x + (bitmap.getWidth()/2)))
{
if(eventY >=(y - bitmap.getHeight()/2) && (y <= y + (bitmap.getHeight()/2)))
{
setTouched(true);
}else
{
setTouched(false);
}
}
else
{
setTouched(false);
}
}
You just need to clear the canvas before drawing the image.
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
//Just call this line
canvas.drawColor(0, Mode.CLEAR);
canvas.drawBitmap(mBitmap1,mBitmap1Point.x-(mBitmap1.getWidth()/2),mBitmap1Point.y-(mBitmap1.getHeight()/2),null);
}
Worked fine for me.
Hi im having a bit of problems getting my sprite to fade back in. Im using paralell entity modifiers, scaling and fading after which the sprite gets new X and Y cordinates and fades in and scales back up. It doesnt work and i think it might have something to do with the onUpdate method.
XOsprite = new Sprite(x, y, XO, engine.getVertexBufferObjectManager()) {
#Override
public boolean onAreaTouched(final TouchEvent te, final float xVal,
final float yVal) {
XOsprite.registerEntityModifier(downOut); //kill sprite
isKilled = true;
XOsprite.registerUpdateHandler(new IUpdateHandler() {
#Override
public void reset() {
// TODO Auto-generated method stub
}
#Override
public void onUpdate(float pSecondsElapsed) {
// TODO Auto-generated method stub
totalElapsedTime += pSecondsElapsed;
if(totalElapsedTime >= 3.0f){
BaseGameActivity gameActivity = (BaseGameActivity) activity;
gameActivity.runOnUpdateThread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
if(isKilled){
reviveSprite();
isKilled = false;
}
}
});
}
//reset();
}
});
return true;
}
};
XOsprite.registerEntityModifier(inUp);
gameScene.attachChild(XOsprite);
gameScene.registerTouchArea(XOsprite);
return gameScene;
}
-edit- more code
public void reviveSprite(){
setSprites();
XOsprite.unregisterEntityModifier(downOut);
XOsprite.registerEntityModifier(inUp);
}
public void setSprites(){
if (rand.nextInt(2) == 0) {
XO = X;
} else {
XO = O;
}
x = rand.nextInt(MainActivity.CAM_WIDTH);
y = rand.nextInt(MainActivity.CAM_HEIGHT);
}
you are already on the UpdateThread, so no need to do this
gameActivity.runOnUpdateThread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
if(isKilled){
reviveSprite();
isKilled = false;
}
}
});
just do this
if(isKilled){
reviveSprite();
isKilled = false;
}
I try to display one from list of bitmaps during onDraw.
When i'm passing list to the canvas all are display and stay in their places.
When I pass one random bitmaps it's redrawing canvas all the time.
All works when i'm using public void drawEnemy(Canvas canvas) but not exactly like I want when using public void drawEn(Canvas canvas).
I want to display one random bitmap, then after a few seconds, delete it and display other bitmap. I think the problem is how I implemented onDrow() method. It's redrawing canvas all the time.
Activity:
public class NewGameActivity extends Activity{
NewGame newgame;
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
// Landscape mode
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
// no title
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
// content Newgame.java
newgame = new NewGame(this);
setContentView(newgame);
}
Thread:
public class MainThread extends Thread{
private SurfaceHolder surfaceHolder;
private NewGame screen;
public MainThread(SurfaceHolder surfaceHolder, NewGame ekran) {
super();
this.surfaceHolder = surfaceHolder;
this.screen= screen;
}
private boolean running;
public void setRunning(boolean running) {
this.running = running;
}
#Override
public void run() {
Canvas canvas;
while (running) {
canvas = null;
try {
canvas = this.surfaceHolder.lockCanvas();
synchronized (surfaceHolder) {
this.screen.onDraw(canvas);
}
} finally {
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
}
SurfaceView:
public class NewGame extends SurfaceView implements SurfaceHolder.Callback{
private MainThread thread;
private EnemyManager manager;
public NewGame(Context context) {
super(context);
getHolder().addCallback(this);
thread = new MainThread(getHolder(), this);
manager = new EnemyManager();
// TODO Auto-generated constructor stub
//adding enemy
Enemy e1 = new Enemy(BitmapFactory.decodeResource(getResources(), R.drawable.card), 1);
Enemy e2 = new Enemy(BitmapFactory.decodeResource(getResources(), R.drawable.horse), 2);
EnemyLocation l1 = new EnemyLocation(60, 180);
EnemyLocation l2 = new EnemyLocation(60, 50);
manager.AddEnemy(e1, l1);
manager.AddEnemy(e2, l2);
setFocusable(true);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.saloon), 0, 0, null);
manager.drawEn(canvas);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
thread.setRunning(true);
thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
thread.setRunning(false);
thread.stop();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
manager.handleActionDown((int)event.getX(), (int)event.getY());
}
return true;
}
}
EnemyManager:
public class EnemyManager {
private ArrayList<Enemy> enemyList;
private ArrayList<Enemy> suspects;
private Enemy cow;
private String message;
private int suspectID;
private Random rnd;
public String getMessage() {
return message;
}
public EnemyManager(){
enemyList = new ArrayList<Enemy>();
suspects = new ArrayList<Enemy>();
}
public void AddEnemy(Enemy enemy, EnemyLocation loc){
// set x,y enemy localization
enemy.setX(loc.getX());
enemy.setY(loc.getY());
enemyList.add(enemy);
}
public void clearEnemy() {
enemyList.clear();
}
// message if enemy touched
public void handleActionDown(int x, int y) {
for (Enemy enemy: enemyList) {
if (enemy.wasTouched(x, y)) {
message = enemy.getId();
return;
}
}
}
public void PrepareEnemy(){
suspectID = enemyList.get(rnd.nextInt(enemyList.size()+1)).getId();
suspects = new ArrayList<Enemy>();
suspects.add(getSuspectByID(suspectID));
}
private Enemy SingleEnemy(){
Double i = 1 + Math.random() * ((enemyList.size()-1)+1);
cow = getSuspectByID(i.intValue());
return cow;
}
private Enemy getSuspectByID(int suspectID) {
for (Enemy s: enemyList) {
if (s.getId() == suspectID) {
return s;
}
}
return null;
}
public void drawEn(Canvas canvas){
try {
Enemy k = SingleEnemy();
canvas.drawBitmap(cow.picture, cow.x, cow.y, null);
} catch (Exception e) {
// TODO: handle exception
}
}
// draw enemy
public void drawEnemy(Canvas canvas) {
try {
for (Enemy enemy: enemyList) {
canvas.drawBitmap(enemy.picture, enemy.x, enemy.y, null);
}
} catch (Exception e) {
// TODO: handle exception
}
}
}
das
As for as understand you are trying to do something like this (if it's not, please correct me):
This is rendering the canvas with all components:
Draw background
Draw enemy
To "refresh" the canvas you simply do something like this:
Draw background
Update
To pause the rendering you could do something like this:
int lastUpdateTime;
int delayTime = 2000; 2 seconds
if(System.currenttimeMillis() > lastUpdateTime + delayTime) {
// Finished waiting
}
You should only define lastUpdateTime when you want to wait and not in every iteration.
NB: Don't call Thread.sleep() in a rendering thread!
I am have some bitmaps which i want to display serially one after another but my code displays only last bitmap.Can anybody tell me why is it happening?
here is the code
class Panel extends SurfaceView implements SurfaceHolder.Callback {
private boolean _run = false;
public Panel(Context context) {
super(context);
getHolder().addCallback(this);
_run = true;
}
#Override
public void onDraw(Canvas canvas) {
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.i("Read","surfaceChanged is called");
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
Log.i("Read","surfaceCreated is called");
while (_run ) {
display();
}
}
public void display() {
Canvas c;
c = null;
try {
c = getHolder().lockCanvas(null);
synchronized (getHolder()) {
onPreviewFrame();
invalidate();
c.drawColor(Color.BLACK);
c.drawBitmap(bmp, 10, 10, null);
//panel.surfaceDestroyed(panel.getHolder());
}
} finally {
if (c != null) {
getHolder().unlockCanvasAndPost(c);
}
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.i("Read","surfaceDestroyed is called");
_run = false;
}
}
I am not sure what your requirement is but if your are trying to create a continous horizontal image scroll. Take a look at this
https://github.com/blessenm/SlideshowDemo