I was working on my first ever android project, which is a live wallpaper, and I managed to get ti work through tutorials, examples, and even codes found in here, but I still have an issue that is keeping from finishing it. I want my live wallpaper using images to parallax scroll when he user switches home screens.
Here is my code::
package com.livewallpaper.mw3lwp;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.os.Handler;
import android.service.wallpaper.WallpaperService;
import android.view.Display;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.WindowManager;
public class ModernWarfare3LiveWallpaper extends WallpaperService {
private final Handler mHandler = new Handler();
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Override
public Engine onCreateEngine() {
return new CubeEngine();
}
class CubeEngine extends Engine {
private final Paint mPaint = new Paint();
private float mPosY;
private float mPosX;
//private float mPosYBackup;
private boolean mAnime = true;
private Matrix mMatrix = new Matrix();
public int bgcycle = 0;
public Bitmap myBg;
public int idx = 0;
private float mPixels;
private final Runnable mDrawAnim = new Runnable() {
public void run() {
drawFrame();
}
};
private boolean mVisible;
private static final int NUM_RES = 50;
//private final Bitmap[] mPics = new Bitmap[NUM_RES];
public int getScreenOrientation() {
Display screen= ((WindowManager) getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
int orientation = getResources().getConfiguration().orientation;
// UNDEFINED
if(orientation==Configuration.ORIENTATION_UNDEFINED){
//Configuration config = getResources().getConfiguration();
if(orientation==Configuration.ORIENTATION_UNDEFINED){
//if height and widht of screen are equal then
// it is square orientation
if(screen.getWidth()==screen.getHeight()){
orientation = Configuration.ORIENTATION_SQUARE;
}else{ //if widht is less than height than it is portrait
if(screen.getWidth() < screen.getHeight()){
orientation = Configuration.ORIENTATION_PORTRAIT;
}else{ // if it is not any of the above it will defineitly be landscape
orientation = Configuration.ORIENTATION_LANDSCAPE;
}
}
}
}
//
// Query what the orientation currently really is.
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
// The following message is only displayed once.
return orientation/*= 1*/; // Portrait Mode
}else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
// The following message is only displayed once.
return orientation/*= 2*/; // Landscape mode
}
return orientation;
}
public void updateBG() {
idx += 1;
if (idx == NUM_RES) {idx = 0;}
Resources res = getResources();
int id = res.getIdentifier("n" + (idx + 1), "drawable", "com.livewallpaper.mw3lwp");
myBg = BitmapFactory.decodeResource(res, id);
}
CubeEngine() {
Resources res = getResources();
//for (int i = 0; i< NUM_RES; i++) {
int id = res.getIdentifier("n" + (idx + 1), "drawable", "com.livewallpaper.mw3lwp");
myBg = BitmapFactory.decodeResource(res, id);
// if (i==NUM_RES) i=0;
// }
}
#Override
public void onCreate(SurfaceHolder surfaceHolder) {
super.onCreate(surfaceHolder);
setTouchEventsEnabled(false);
}
#Override
public void onDestroy() {
super.onDestroy();
mHandler.removeCallbacks(mDrawAnim);
}
#Override
public void onVisibilityChanged(boolean visible) {
mVisible = visible;
if (visible) {
drawFrame();
} else {
mHandler.removeCallbacks(mDrawAnim);
}
}
#Override
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
//if landscape
if (getScreenOrientation() == 2){
super.onSurfaceChanged(holder, format, width, height);
float w = myBg.getWidth();
float h = myBg.getHeight();
float s = width / (float)w;
mMatrix.reset();
mMatrix.setScale(s, s);
mPosY = (height - (h * s)) / 2f;
//mPixels= 0;
//mPosYBackup= mPosY;
drawFrame();
}
//
//if portrait
else {
super.onSurfaceChanged(holder, format, width, height);
float w = myBg.getWidth();
float h = myBg.getHeight();
float s = height / (float)h;
mMatrix.reset();
mMatrix.setScale(s, s);
//mMatrix.postScale(s, s, 0, 0);
// mPosY = 0f;
mPosX= (width - (w * s)) / 2f;
drawFrame();
}
//
}
#Override
public void onSurfaceCreated(SurfaceHolder holder) {
super.onSurfaceCreated(holder);
}
#Override
public void onSurfaceDestroyed(SurfaceHolder holder) {
super.onSurfaceDestroyed(holder);
mVisible = false;
mHandler.removeCallbacks(mDrawAnim);
}
#Override
public void onOffsetsChanged(float xOffset, float yOffset,
float xStep, float yStep, int xPixels, int yPixels) {
// Agregado recien
//if landscape
if (getScreenOrientation() == 2){
super.onOffsetsChanged(xOffset, yOffset, xStep, yStep, xPixels, yPixels);
//mPosY= mPosYBackup;
drawFrame();
}
//if portrait
else{
super.onOffsetsChanged(xOffset, yOffset, xStep, yStep, xPixels, yPixels);
mPixels = xPixels;
//mPosY=0f;
drawFrame();
}
// Fin Agregado
}
#Override
public void onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_MOVE) {
mAnime = !mAnime;
}
super.onTouchEvent(event);
}
void drawFrame() {
final SurfaceHolder holder = getSurfaceHolder();
Canvas c = null;
try {
c = holder.lockCanvas();
if (c != null) {
// draw something
drawAnim(c);
//drawTouchPoint(c);
}
} finally {
if (c != null) holder.unlockCanvasAndPost(c);
}
// Reschedule the next redraw
mHandler.removeCallbacks(mDrawAnim);
if (mVisible && mAnime) {
mHandler.postDelayed(mDrawAnim, 0);
}
}
void drawAnim(Canvas c) {
// if portrait
if(getScreenOrientation() == 1){
c.save();
//if (this.isPreview()) {
//c.translate(/*(float)*/mPosX, 0f);
//}
// MY PROBLEM HEREEEEE!!!! IM NOT USING BOTH
c.translate(/*(float)*/mPosX, 0f);
c.translate((float)mPixels, 0f);
updateBG();
c.drawBitmap(myBg, mMatrix, mPaint);
//c.drawBitmap(myBg, 0, 0, mPaint);
if (mAnime) ++idx;
if (idx == NUM_RES) idx = 0;
c.restore();
}
//end if portrait
// if landscape
if(getScreenOrientation() == 2){
c.save();
c.translate(0, mPosY);
updateBG();
c.drawBitmap(myBg, mMatrix, mPaint);
if (mAnime) ++idx;
if (idx == NUM_RES) idx = 0;
c.restore();
}
// end if landscape
//c.drawBitmap(mPics[idx], mMatrix, mPaint);
}
}
}
}
I pointed where I believe the error is with a "MY PROBLEM IS HEREEEE!!!" in the code. Thing is on the canvas.translate();
If I use c.translate(mPosX, 0f); mPosX gotten from onSurfacedChanged, the center part of the images in the wallpaper are showed, the way I want, but it won't scroll through the whole background.
If I use c.translate((float)mPixels, 0f); where mPixels is gotten from onOffSetChanged, it only shows the left part/zone of the wallpaper.
ANd finally if I change mPosX name to mPixels, c.translate((float)mPixels, 0f); gets the value from both, onSurfacedChanged and from onOffSetChanged. It does not work on the first execution. You can see the wallpaper centered first and then back to only showing the left part of the wallpaper. If I run it a second time on eclipse and set it again on my phone, then it would work how i want, the center part of the image on the middle home screen, and scrolling the background as switching home screens. But the thing is it does not work at first execution, which leads to not working when exported as the apk.
So can anybody help me to get my live wallpaper images shown centered and scrolling when switching home screens please. Thank you in advance.
Well, I still do not know why sometimes the xPixelOffset value (xPixel on my code) from onOffsetChanged is returning 0, but I managed to get an alternative solution for devices where this anomality may occur.
If this is happening to someone else I realized that the xPixelOffset can also be obtained by getting the device screen width and multiply it time the xOffset from onOffsetChanged. Width can be obtained and stored in a new variable from OnSurfaceChanged, where the device screen width and height can be obtained.
EX:
mPixels = (0- screenWidth)*xOffset;
Where mPixels should be getting the value from xPixelOffset (xPixel on my code), and screenWidth is obtained from OnSurfaceChanged through:
super.onSurfaceChanged(holder, format, width, height);
screenWidth= width;
Related
So I am a near noob at programming android, and i am still learning it.
I have created a small 2D spaceship game, where you have to dodge the oncoming stars by moving your finger. This game works almost perfectly on my emulator, but when played on other phones (like my friend's), it is messed up.
First of all the spaceship looks too big on his phone, and so do the stars, (not like on my emulator). And my written text coordinates seem to be off as well.
I believe the problem has something to do with pixels, and adjusting them with each phone but how?
Thank you for helping me,
Below is my long code, if you want to go through it, though it's unnecessary...
Please if you can, show me an example of how you do it, so show me what you put in the coordinates when drawing a simple rectangle for example...Thanks A lot
package com.zunairgames.zunair;
import java.util.Random;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.Window;
import android.view.WindowManager;
public class GFXSurface extends Activity implements OnTouchListener {
MyBringBackSurface ourSurfaceView;
float x, y;
boolean testingFinger=false;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
ourSurfaceView = new MyBringBackSurface(this);
ourSurfaceView.setOnTouchListener(this);
x=0;
y=0;
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(ourSurfaceView);//ourSurfaceView
}
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
ourSurfaceView.pause();
}
#Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
ourSurfaceView.resume();
}
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
x= event.getX();
y= event.getY();
if (event.getAction()==MotionEvent.ACTION_DOWN) {
testingFinger=true;
return true;
}
if(event.getAction()==MotionEvent.ACTION_UP){
testingFinger=false;
return false;
}
return false;
}
public class MyBringBackSurface extends SurfaceView implements Runnable{
//vertical
SurfaceHolder ourHolder;
Canvas canvas = (Canvas) ourHolder;
Thread ourThread = null;
boolean isRunning=false;
int screenHeight;
int screenWidth;
Random random = new Random ();
boolean loadStuff = false;
int posX=0;
int posY=0;
int posWidth=100;
int posHeight=100;
int numStars=4;
int starX[]=new int[numStars];
int starY[]=new int[numStars];
int starSpeed[]=new int[numStars];
int score=0;
int backgroundY=0;
Bitmap spaceship;
Bitmap background;
Bitmap starPic;
public MyBringBackSurface(Context context) {
// TODO Auto-generated constructor stub
super(context);
ourHolder= getHolder();
}
public void pause(){
isRunning=false;
while(true){
try {
ourThread.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
}
ourThread=null;
}
public void resume(){
isRunning=true;
ourThread=new Thread(this);
ourThread.start();
}
public int round(double d){
double dAbs = Math.abs(d);
int i = (int) dAbs;
double result = dAbs - (double) i;
if(result<0.5){
return d<0 ? -i : i;
}else{
return d<0 ? -(i+1) : i+1;
}
}
public Bitmap getResizedBitmap(Bitmap bm, int newHeight, int newWidth) {
int width = bm.getWidth();
int height = bm.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// create a matrix for the manipulation
Matrix matrix = new Matrix();
// resize the bit map
matrix.postScale(scaleWidth, scaleHeight);
// recreate the new Bitmap
Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false);
return resizedBitmap;
}
public void run() {
// TODO Auto-generated method stub
while(isRunning){
if(!ourHolder.getSurface().isValid())continue;
canvas = ourHolder.lockCanvas();
if(loadStuff==false){
for (int i=0; i <numStars; i++){
starY[i]=-random.nextInt(200);
starX[i]=random.nextInt(canvas.getWidth()-50);
starSpeed[i]=1+random.nextInt(5);
}
spaceship = BitmapFactory.decodeResource(getResources(),R.drawable.spaceship);
background = BitmapFactory.decodeResource(getResources(),R.drawable.background);
starPic = BitmapFactory.decodeResource(getResources(),R.drawable.tile);
spaceship = getResizedBitmap(spaceship,100,100);
background= getResizedBitmap(background,(canvas.getHeight())*2,(canvas.getWidth()));
starPic= getResizedBitmap(starPic,50,50);
backgroundY=-(canvas.getHeight());
x=canvas.getWidth()/2;
y=canvas.getHeight()/2;
loadStuff=true;
}
Paint paint = new Paint();
paint.setColor(Color.GREEN);
paint.setTextSize(40);
for (int i=0; i <numStars; i++){
starY[i]+=starSpeed[i];
if(starY[i]>canvas.getHeight()){
starY[i]=-random.nextInt(200);
starX[i]=random.nextInt(canvas.getWidth()-50);
starSpeed[i]=starSpeed[i]+random.nextInt(2);
}
if(x+posWidth>starX[i]&&x<starX[i]+50 && y+posHeight>starY[i]&&y<starY[i]+50){
starY[i]=-random.nextInt(200);
starX[i]=random.nextInt(canvas.getWidth()-50);
starSpeed[i]=1+random.nextInt(10);
score++;
}
}
backgroundY++;
if(backgroundY>-10){
backgroundY=-canvas.getHeight();
}
canvas.drawRGB( 3, 120, 12);
canvas.drawBitmap(background, 0,backgroundY, null);
for (int i=0; i <numStars; i++){
canvas.drawBitmap(starPic,starX[i] , starY[i],null);
}
canvas.drawText("SCORE : "+ score + "DENSITY :"+ canvas.getDensity(),0, 100, paint);
canvas.drawBitmap(spaceship, x,y, null);
ourHolder.unlockCanvasAndPost(canvas);
//72
}
When programatically setting widths and pixel locations you need to make sure you take into account the screen density which varies from device to device.
So you figure you do your calculations in "Density Independent Pixels" (DIP or DP) and then convert them to real pixels using a formula like below before setting it on the component.
public int dpToPx(int dp) {
DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
int px = Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
return px;
}
Further reading at http://developer.android.com/guide/practices/screens_support.html#dips-pels
you can override this method and the devices width and height will go to it when you start the activity and scale properly
#Override
public void onSizeChanged (int w, int h, int oldw, int oldh)
{
super.onSizeChanged(w, h, oldw, oldh);
screenW = w;
screenH = h;
}
I have a SurfaceView that i need to know the width and height of, the code looks like this:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
screenWidth = MeasureSpec.getSize(widthMeasureSpec);
screenHeight = MeasureSpec.getSize(heightMeasureSpec);
}
For some reason it manages to measure the width of this view but not the height of it, I've tried placing the height measurement before the width one to see if its just not calling the onMeasure() method quick enough but it didn't work.
Edit:
Heres all the code:
public class GameView extends SurfaceView {
GameLoopThread gameLoopThread;
Integer screenWidth, screenHeight;
static Integer score;
long lastTime = 0;
byte speed = 5;
static Boolean mute;
static String textureUsed = "space";
public GameView(Context context) {
super(context);
mute = MainMenu.getMute();
score = 0;
gameLoopThread = new GameLoopThread(this);
SurfaceHolder holder = getHolder();
holder.addCallback(new Callback() {
public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
}
public void surfaceCreated(SurfaceHolder holder) {
gameLoopThread.setRunning(true);
gameLoopThread.start();
}
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
gameLoopThread.setRunning(false);
while (retry) {
try {
gameLoopThread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
});
}
// graphics are drawn here
#Override
protected void onDraw(Canvas canvas) {
//covers the whole canvas in white to clear previouslly draw graphics
canvas.drawColor(Color.WHITE);
//set the location of the players selected texture pack
int resID = getResources().getIdentifier(textureUsed, "drawable", "com.unreturnablestudios.FlipSide");
//loads this texture pack
Bitmap graphics = BitmapFactory.decodeResource(getResources(), resID);
//src and dst are used to detarmine which parts of the image are
//going to be used and where abouts on the screen they are going
//to be displayed
Rect src = new Rect(0, 0, 640, 480);
Rect dst = new Rect(0, 0, screenWidth, screenHeight);
canvas.drawBitmap(graphics, src, dst, null);
// sets the style of the text up
String scoreString = Integer.toString(score);
Paint paint = new Paint();
paint.setColor(Color.RED);//red font
paint.setTypeface(Typeface.DEFAULT);//Uses the players default font
paint.setTextSize(screenWidth / 10);//font size
//draws the text
canvas.drawText(scoreString,0, 0, paint);
score -= speed;
}
// deals with user touching the screen
#Override
public boolean onTouchEvent(MotionEvent event) {
double X = event.getX();
long currentTime = System.currentTimeMillis();
if (currentTime > lastTime + 100) {
if (X < (screenWidth / 2) && X != 0) {
} else {
}
}
lastTime = System.currentTimeMillis();
return super.onTouchEvent(event);
}
public void launchEnd() {
Context context = getContext();
Intent openEnd = new Intent("com.unreturnablestudios.FlipSide.END");
context.startActivity(openEnd);
gameLoopThread.setRunning(false);
}
public static Integer getScore() {
// called by the End class and returns the score.
return score;
}
public static boolean getMute() {
// called by the End class and returns the boolean that is in control of
// sound
return mute;
}
// gets the measurements of the screen (width and height)
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
screenWidth = MeasureSpec.getSize(widthMeasureSpec);
screenHeight = MeasureSpec.getSize(heightMeasureSpec);
}
}
The weird thing is i have used onMeasure() before in a previous version of this application and it worked fine, but i decided to alter most of the code to try and make it run faster and now it won't work.
OnMeasure is often called multiple times by the layout process, have you checked that it's not called more than once?
Usually what I have seen is that for some layouts it's called the first time to measure the width of the view, and then once all of the view widths have been locked it's called a second time to measure the height.
I'm trying to write some code that let me draw an array of pixels to the screen. However I get very louzy performance on my Arm cortex A8 600mhz at 320x240 pixels.
Can someone explain to me where the bottleneck is and/or how to fix it? Changing the height and width barely helps too. I noticed similar behaviour with Swing in Java. I'm using code below:
package com.pixeldraw;
import java.util.ArrayList;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Debug;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.Window;
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(new Panel(this));
Debug.startMethodTracing("pixeldraw");
}
#Override
protected void onDestroy() {
super.onDestroy();
Debug.stopMethodTracing();
}
class Panel extends SurfaceView implements SurfaceHolder.Callback {
private TutorialThread _thread;
private Bitmap _buffer;
private ArrayList<GraphicObject> _graphics = new ArrayList<GraphicObject>();
private int width = 40;
private int height = 40;
public Panel(Context context) {
super(context);
getHolder().addCallback(this);
_thread = new TutorialThread(getHolder(), this);
setFocusable(true);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
synchronized (_thread.getSurfaceHolder()) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
GraphicObject graphic = new GraphicObject(BitmapFactory.decodeResource(getResources(), R.drawable.icon));
graphic.getCoordinates().setX((int) event.getX() - graphic.getGraphic().getWidth() / 2);
graphic.getCoordinates().setY((int) event.getY() - graphic.getGraphic().getHeight() / 2);
_graphics.add(graphic);
}
return true;
}
}
long lastTime = System.currentTimeMillis();
int CLOCKS_PER_SEC = 500;
float timeCnt = 0;
#Override
public void onDraw(Canvas canvas) {
canvas.drawColor(Color.BLACK);
width = getWidth()/2;
height = getHeight()/2;
long frameTime = System.currentTimeMillis();
// The elapsed seconds per frame will almost always be less than 1.0.
float elapsedSeconds = (float)(frameTime - lastTime) / CLOCKS_PER_SEC;
int colors[] = new int[width*height];
for (int x = 0; x<width ; x++) {
for (int y = 0; y<height ; y++){
int r = (int)(timeCnt/5*255);
int b = 0;
int g = 0;
int a = 255;
colors[x + y * width] = (a << 24) | (r << 16) | (g << 8) | b;
}
}
for (int x = 0; x<10 ; x++) {
for (int y = 0; y<10; y++){
int r = 0;
int b = 255;
int g = 0;
int a = 255;
colors[(int)(timeCnt*32)+x + y * width] = (a << 24) | (r << 16) | (g << 8) | b;
}
}
_buffer = Bitmap.createBitmap(colors, width, height, Bitmap.Config.RGB_565);
canvas.drawBitmap(_buffer, 0, 0, null);
timeCnt += elapsedSeconds;
if (timeCnt > 5) timeCnt = 0;
// Update the last time counter so that we can use it next frame.
lastTime = frameTime;
}
#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) {
// simply copied from sample application LunarLander:
// we have to tell thread to shut down & wait for it to finish, or else
// it might touch the Surface after we return and explode
boolean retry = true;
_thread.setRunning(false);
while (retry) {
try {
_thread.join();
retry = false;
} catch (InterruptedException e) {
// we will try it again and again...
}
}
}
}
class TutorialThread extends Thread {
private SurfaceHolder _surfaceHolder;
private Panel _panel;
private boolean _run = false;
public TutorialThread(SurfaceHolder surfaceHolder, Panel panel) {
_surfaceHolder = surfaceHolder;
_panel = panel;
}
public void setRunning(boolean run) {
_run = run;
}
public SurfaceHolder getSurfaceHolder() {
return _surfaceHolder;
}
#Override
public void run() {
Canvas c;
while (_run) {
c = null;
try {
c = _surfaceHolder.lockCanvas(null);
synchronized (_surfaceHolder) {
_panel.onDraw(c);
}
} finally {
// do this in a finally so that if an exception is thrown
// during the above, we don't leave the Surface in an
// inconsistent state
if (c != null) {
_surfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}
class GraphicObject {
/**
* Contains the coordinates of the graphic.
*/
public class Coordinates {
private int _x = 100;
private int _y = 0;
public int getX() {
return _x + _bitmap.getWidth() / 2;
}
public void setX(int value) {
_x = value - _bitmap.getWidth() / 2;
}
public int getY() {
return _y + _bitmap.getHeight() / 2;
}
public void setY(int value) {
_y = value - _bitmap.getHeight() / 2;
}
public String toString() {
return "Coordinates: (" + _x + "/" + _y + ")";
}
}
private Bitmap _bitmap;
private Coordinates _coordinates;
public GraphicObject(Bitmap bitmap) {
_bitmap = bitmap;
_coordinates = new Coordinates();
}
public Bitmap getGraphic() {
return _bitmap;
}
public Coordinates getCoordinates() {
return _coordinates;
}
}
}
From our company experience - pixel-by-pixel operations on near-full-screen bitmaps are extremely slow on android. One of the slowest operations we had on a custom animation of some sort was to make the background black by drawColor() which is exactly doing pixel-by-pixel filling of the screen...
So in your case probably the culprit is
canvas.drawColor(Color.BLACK);
But indeed - do profiling and you will know. With profiling you can drill down onDraw and see which method takes most time. This is how we found out.
Be precise... tell some numbers instead of very louzy
Move expensive operations outside of your drawing code
int colors[] = new int[width*height];
_buffer = Bitmap.createBitmap(colors, width, height, Bitmap.Config.RGB_565);
Use profiling http://developer.android.com/guide/developing/debugging/debugging-tracing.html
I currently have something working where I can drag a box horizontally on the screen (what I want it to do). However, it works when you click ANYWHERE on the screen, and I want it to work only when the box has been clicked. I have tried implementing this in different ways, and I've looked all over the place and still remain lost. Can anybody help? I also can't figure out how a bitmap is placed (I'm using a bitmap right now as I can't for the life of me figure out how to implement the ImageView inside my SurfaceView). If I say my bitmap is placed at 0,0 will that place the bitmap according to its top left corner at 0,0? I also had an algorithm for stopping the box when it reached an edge, but I'll just have to rewrite that as I must have deleted it. Please if you can offer your knowledge I would GREATLY appreciate it
public class BoardView extends SurfaceView implements SurfaceHolder.Callback{
Context mContext;
private BoardThread thread;
private float box_x = 140;
private float box_y = 378;
ImageView i = (ImageView) findViewById(R.id.box_view);
Bitmap box =
(BitmapFactory.decodeResource
(getResources(), R.drawable.box));
private float boxWidth = box.getWidth();
private float boxHeight = box.getHeight();
public BoardView(Context context){
super(context);
//surfaceHolder provides canvas that we draw on
getHolder().addCallback(this);
// controls drawings
thread = new BoardThread(getHolder(),this);
//intercepts touch events
setFocusable(true);
}
#Override
public void onDraw(Canvas canvas){
canvas.drawColor(Color.WHITE);
//draw box and set start location
canvas.drawBitmap(box, box_x - (boxWidth/2),
box_y - (boxHeight/2), null);
}
#Override
public boolean onTouchEvent(MotionEvent event){
//boolean mode = false;
if(event.getAction() == MotionEvent.ACTION_DOWN){
//int x = (int)event.getX();
//int y = (int)event.getY();
//if (x > box_x && x < box_x + 29 && y > box_y && y < box_y + 30){
//mode = true;
box_x = (int)event.getX();
//}
}
if(event.getAction() == MotionEvent.ACTION_MOVE) {
//int x = (int)event.getX();
//int y = (int)event.getY();
//if (mode == true){
box_x = (int)event.getX();
//}
}
invalidate();
return true;
}
#Override
public void surfaceChanged(SurfaceHolder holder,
int format, int width, int height ){
}
#Override
public void surfaceCreated(SurfaceHolder holder){
thread.startRunning(true);
thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder){
thread.startRunning(false);
thread.stop();
}
}
Simply check the touch event x and y coordinate, and check if this x,y-combination is within the box's bounds. I can see you are on the right path by looking at your commented out code.
Do something like this:
RectF rect = new RectF(x,y, x + box.getWidth(), y+box.geHeight());
if(rect.contains(touchX, touchY)) {
// You hit the box, allow dragging...
}
Try this:
package teste.com.br.teste;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.view.MotionEvent;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Tacila on 03/07
*/
public class Game extends View {
private Context context;
private BitmaptArrastavel bmArrastavel;
private BitmaptArrastavel bmArrastavel2;
private BitmaptArrastavel bmArrastavelTeste;
private List<BitmaptArrastavel> btms;
private BitmaptArrastavel[] btmsAtivas;
private BitmaptArrastavel[] lake;
private BitmaptArrastavel[] ativos;
private int qntDeItens = 5;
public Game(Context context) {
super(context);
this.context = context;
init();
}
public void init() {
btms = new ArrayList<BitmaptArrastavel>();
btmsAtivas = new BitmaptArrastavel[1];
ativos=new BitmaptArrastavel[1];
lake = new BitmaptArrastavel[3];
lake[0] = new BitmaptArrastavel(escalaBitmap(BitmapFactory.decodeResource(context.getResources(), R.drawable.cartadc)), 200);
lake[1] = new BitmaptArrastavel(escalaBitmap(BitmapFactory.decodeResource(context.getResources(), R.drawable.cartaao)), 210);
lake[2] = new BitmaptArrastavel(escalaBitmap(BitmapFactory.decodeResource(context.getResources(), R.drawable.cartake)), 220);
btms.add(bmArrastavel);
btms.add(bmArrastavelTeste);
btms.add(bmArrastavel2);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRGB(0,0,139);
for(int i = 0; i<=2; i++){
lake[i].drawOnCanvas(canvas);
}
}
public void travarArr(float x, float y ){
for(int i = 0; i<=2 ; i++){
if(lake[i].isDentro(x,y)){
ativos[0]=lake[i];
}
}
}
public void destravarBitmap() {
ativos[0]=null;
}
public Bitmap escalaBitmap(Bitmap bm) {
return Bitmap.createScaledBitmap(bm, 200, 300, false);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
final int action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_DOWN:
System.out.println("Dentro do MotionEvent.ActionDown");
travarArr(event.getX(),event.getY());
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
System.out.println("Dentro do MotionEvent.ActionUP e ActionCancel tenho q entrar no destrava ");
destravarBitmap();
break;
case MotionEvent.ACTION_MOVE:
System.out.println("Dentro do MotionEvent.ActionMove");
for(int i = 0; i<ativos.length;i++){
if(ativos[i]!=null){
ativos[i].mover((int)event.getX(),(int)event.getY());
invalidate();
}
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
System.out.println("Dentro do MotionEvent.ActionPointerDown");
travarArr(event.getX(), event.getY());
break;
case MotionEvent.ACTION_POINTER_UP:
System.out.println("Dentro do MotionEvent.ActionPointerUp");
destravarBitmap();
break;
default:
return super.onTouchEvent(event);
}
return true;
}
// int rotation = 0;
//
// public Bitmap vertical() {
// Matrix matrix = new Matrix();
// matrix.postRotate(90);
// Bitmap bm = escalaBitmap(BitmapFactory.decodeResource(context.getResources(), R.drawable.carta4c));
// return Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true);
// }
}
The class BitmapArrastavel
package teste.com.br.teste;
import android.graphics.Bitmap;
import android.graphics.Canvas;
/**
* Created by Tacila on 05/07/2017.
*/
public class BitmaptArrastavel {
public int x, y, altura, largura;
private Bitmap bitmap;
public BitmaptArrastavel(Bitmap b, int x) {
bitmap = b;
this.x = x;
init();
}
public void init() {
largura = 200;
altura = 350;
}
public Bitmap escalaBitmap(Bitmap bm) {
return Bitmap.createScaledBitmap(bm, largura, altura, false);
}
public boolean isDentro(float x, float y) {
return (x >= this.x && x <= this.x + largura && y >= this.y && y <= this.y + altura);
}
public void drawOnCanvas(Canvas canvas) {
canvas.drawBitmap(bitmap, x, y, null);
}
public void mover(int x, int y) {
this.x = x;
this.y = y;
}
}
I found this class:
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.Display;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
// borrowed from https://sites.google.com/site/androidhowto/how-to-1/custom-scrollable-image-view
public class ScrollImageView extends View {
private final int DEFAULT_PADDING = 10;
private Display mDisplay;
private Bitmap mImage;
/* Current x and y of the touch */
private float mCurrentX = 0;
private float mCurrentY = 0;
private float mTotalX = 0;
private float mTotalY = 0;
/* The touch distance change from the current touch */
private float mDeltaX = 0;
private float mDeltaY = 0;
int mDisplayWidth;
int mDisplayHeight;
int mPadding;
public ScrollImageView(Context context) {
super(context);
initScrollImageView(context);
}
public ScrollImageView(Context context, AttributeSet attributeSet) {
super(context);
initScrollImageView(context);
}
private void initScrollImageView(Context context) {
mDisplay = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
mPadding = DEFAULT_PADDING;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = measureDim(widthMeasureSpec, mDisplay.getWidth());
int height = measureDim(heightMeasureSpec, mDisplay.getHeight());
setMeasuredDimension(width, height);
}
private int measureDim(int measureSpec, int size) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else {
result = size;
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
}
return result;
}
public Bitmap getImage() {
return mImage;
}
public void setImage(Bitmap image) {
mImage = image;
}
public int getPadding() {
return mPadding;
}
public void setPadding(int padding) {
this.mPadding = padding;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
mCurrentX = event.getRawX();
mCurrentY = event.getRawY();
}
else if (event.getAction() == MotionEvent.ACTION_MOVE) {
float x = event.getRawX();
float y = event.getRawY();
// Update how much the touch moved
mDeltaX = x - mCurrentX;
mDeltaY = y - mCurrentY;
mCurrentX = x;
mCurrentY = y;
invalidate();
}
// Consume event
return true;
}
#Override
protected void onDraw(Canvas canvas) {
if (mImage == null) {
return;
}
float newTotalX = mTotalX + mDeltaX;
// Don't scroll off the left or right edges of the bitmap.
if (mPadding > newTotalX && newTotalX > getMeasuredWidth() - mImage.getWidth() - mPadding)
mTotalX += mDeltaX;
float newTotalY = mTotalY + mDeltaY;
// Don't scroll off the top or bottom edges of the bitmap.
if (mPadding > newTotalY && newTotalY > getMeasuredHeight() - mImage.getHeight() - mPadding)
mTotalY += mDeltaY;
Paint paint = new Paint();
canvas.drawBitmap(mImage, mTotalX, mTotalY, paint);
}
}
and am stumped how to use it with my application.
I want my application when in landscape orientation to display a bitmap image that is scrollable vertically and horizontally. I pull my image from a URL and programatically make it a bitmap. I call a method that returns my bitmap image.
Any ideas?
The setImage methods takes a Bitmap. So, you should be able to programmatically create this view in your activity, set the image with your Bitmap and place the view into your layout.
I've never seen this class before, so your mileage may vary ;)