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.
Related
I'm creating a custom view in which I have a rectangle RectF object that have a specific height. I would like to increase the bottom Y point coordinate to a specific value with a progressive animation.
I've tried the following. I've created a method setBatteryState() that is called on a onclicked method in the activity that holds the custom view:
public class BatteryView extends View {
public int mCanvasWidth;
public int mCanvasHeight;
public RectF mBatteryHead;
public RectF mBatteryBody;
public RectF mBatteryBodyVolume;
public Canvas mCanvas;
public BatteryView(Context context) {
super(context);
}
public BatteryView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void init()
{
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
this.mCanvas = canvas;
float batteryHeadDistanceFromLeft = mCanvasWidth / 3;
float batteryHeadWidth = mCanvasWidth / 3;
float batteryBodyDistanceFromTop = mCanvasHeight / 5;
float batteryHeadHeight = mCanvasHeight / 5;
mBatteryHead = new RectF(batteryHeadDistanceFromLeft,0,2*batteryHeadWidth,batteryHeadHeight+5);
Paint batteryHeadPaint = new Paint();
batteryHeadPaint.setColor(ContextCompat.getColor(getContext(), R.color.batifyColor));
canvas.drawRect(mBatteryHead,batteryHeadPaint);
mBatteryBody = new RectF(0,(int)batteryBodyDistanceFromTop,mCanvasWidth,mCanvasHeight);
Paint batteryBodyPaint = new Paint();
batteryBodyPaint.setStyle(Paint.Style.STROKE);
batteryBodyPaint.setColor(ContextCompat.getColor(getContext(), R.color.batifyColor));
batteryBodyPaint.setStrokeWidth(10);
canvas.drawRect(mBatteryBody,batteryBodyPaint);
mBatteryBodyVolume = new RectF(12,(int)batteryBodyDistanceFromTop + 10,mCanvasWidth-12,mCanvasHeight/2);
Paint volumeBodyPaint = new Paint();
volumeBodyPaint.setColor(ContextCompat.getColor(getContext(), R.color.batifyColor));
canvas.drawRect(mBatteryBodyVolume,volumeBodyPaint);
}
public void setStateOnBattery(){
ObjectAnimator animateBottom = ObjectAnimator.ofFloat(mBatteryBodyVolume, "bottom", mBatteryBodyVolume.bottom, mCanvasHeight);
animateBottom.setDuration(1000).start();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec,heightMeasureSpec);
mCanvasWidth = MeasureSpec.getSize(widthMeasureSpec);
mCanvasHeight = MeasureSpec.getSize(heightMeasureSpec);
}}
ObjectAnimator should translate the rect mBatteryBodyVolume to the size of the canvas but nothing change...
Any Idea ?
Thanks in advance !
Use an asynchronous task with 2 major functions, draw and update. Every time update is called, increase the height variable by a constant. Then, in draw, draw your rectangle with height as a param. If you need code, just ask. :D
UPDATE
Create a 'runner' async task:
public class Runner extends Thread {
public volatile boolean running = true;
private Environment env;
public Runner(Environment E) {
env = E;
}
#Override
public void run() {
long lastTime = System.currentTimeMillis();
while(running) {
long now = System.currentTimeMillis();
long elapsed = now - lastTime;
env.update(elapsed);
env.draw();
lastTime = now;
}
}
public void shutdown() {
running = false;
}
}
In Environment, Do the following:
public void draw() {
Canvas canvas = holder.lockCanvas();
if (canvas != null) {
canvas.drawRect(x-w, y-h, x+w, y+h, myPaint);
holder.unlockCanvasAndPost(canvas);
}
}
and the update method:
public void update(float elapsedTime) {
h+=myKonstant*elpasedTime;
}
Hope I Helped :D
This is my code which use another Sensor class and other activity but
i want only compass to rotate in smooth way i use bitmap image on
canvas bitmap not rotate in smooth way
public class CompassView extends View
{
private int mWidth;
private int mHeight;
private float position = 0;
private Bitmap myCompassPointer;
public CompassView(Context context)
{
super(context);
myCompassPointer=BitmapFactory.decodeResource(getResources(),
R.drawable.pin_finder);
}
#Override
protected void onDraw(Canvas canvas)
{
int cx = (mWidth - myCompassPointer.getWidth()) >> 1;
int cy = (mHeight - myCompassPointer.getHeight()) >> 1;
if (position > 0)
{
canvas.rotate(position, mWidth >> 1, mHeight >> 1);
}
//it set the bitmap to cx,cy position
canvas.drawBitmap(myCompassPointer, cx, cy, null);
canvas.restore();
}
public void updateData(float position)
{
this.position = position;
invalidate();
}
protected void onMeasure(int widthMeasureSpec, int
heightMeasureSpec)
{
// TODO Auto-generated method stub
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = View.MeasureSpec.getSize(widthMeasureSpec);
mHeight = View.MeasureSpec.getSize(heightMeasureSpec);
}
}
You should store the current rotation of the canvas in some field variable and on each update of data use the ObjectAnimator to smoothly update the value of the current rotation of the canvas to the new value. Be sure to invalidate the view on each animation frame. You can read more about animations of properties and ObjectAnimator here: Property Animation
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;
Welcome. I have a problem. I want to do dynamic wallpaper so that every few seconds to change the text. The program works pretty well because the text is changing, but changing so that the previous text is still visible, and after a few seconds I have a lot of the text. I looked everywhere but I am a beginner and do not know how to solve that problem. There is a code:
private class MyWallpaperEngine extends Engine {
private final Handler handler = new Handler();
private final Runnable drawRunner = new Runnable() {
#Override
public void run() {
draw();
}
};
private Paint paint = new Paint();
private int width;
int height;
private boolean visible = true;
public MyWallpaperEngine() {
paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.WHITE);
handler.post(drawRunner);
}
#Override
public void onVisibilityChanged(boolean visible) {
this.visible = visible;
if (visible) {
handler.post(drawRunner);
} else {
handler.removeCallbacks(drawRunner);
}
}
#Override
public void onSurfaceDestroyed(SurfaceHolder holder) {
super.onSurfaceDestroyed(holder);
this.visible = false;
handler.removeCallbacks(drawRunner);
}
#Override
public void onSurfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
this.width = width;
this.height = height;
super.onSurfaceChanged(holder, format, width, height);
}
private void draw() {
SurfaceHolder holder = getSurfaceHolder();
Canvas canvas = null;
try {
canvas = holder.lockCanvas();
if (canvas != null)
drawAnimation(canvas);
} finally {
if (canvas != null)
holder.unlockCanvasAndPost(canvas);
}
if (visible) {
handler.postDelayed(drawRunner, 4000);
}
}
private void drawAnimation(Canvas c){
Random r = new Random();
int i1=r.nextInt(200-50) + 50;
String text = Integer.toString(i1);
c.drawText(text, i1, i1, paint);
}
}
You should clear the canvas before drawing, using something like
c.drawColor(int color);
or draw anything else that covers the entire area, otherwise you will just draw onto what was already on the canvas.
I am developing a simple 2d game. But im stuck at this point where i need to "spawn" unlimited with the same enemy when i click the screen.
So i think the best choose for something with unlimited is an array but i have no idea how to get a bitmapArray and then for each item in BitmapArray do canvas.draw
Someone please help me out!
//Simon
First of all, you will end up with a sort of limited array. Secondly, I'm recommending the Memory Pool pattern when using stuff like this, so you don't create new instances during runtime.
Back to your question, a first implementation would look something like this:
public class BitmapObject {
private Bitmap mBitmap;
private int mPositionX;
private int mPositionY;
private int mBitmapWidth;
private int mBitmapHeight;
private boolean mIsAlive;
public BitmapObject(Bitmap bitmap) {
mBitmap = bitmap;
mBitmapWidth = bitmap.getWidth();
mBitmapHeight = bitmap.getHeight();
mIsAlive = false;
}
public void draw(Canvas canvas) {
if (mIsAlive) {
canvas.drawBitmap(mBitmap, mPositionX, mPositionY, null);
}
}
public void setNewPosition(int touchX, int touchY) {
mPositionX = touchX - mBitmapWidth / 2;
mPositionY = touchY - mBitmapHeight / 2;
}
public void setIsAlive(boolean isAlive) { mIsAlive = isAlive; }
public boolean getIsAlive() { return mIsAlive; }
}
And use it like this in your SurfaceView class:
public class CanvasRenderer extends SurfaceView implements SurfaceHolder.Callback {
private static final int MAX_OBJECTS = 16;
private BitmapObject[] mBitmapObjectsArray;
public CanvasRenderer(Context context) {
super(context);
// Necessary SurfaceView initialization stuff.
Bitmap sprite = BitmapFactory.decodeResource(context.getResources(),
R.drawable.sprite);
mBitmapObjectsArray = new BitmapObject[MAX_OBJECTS];
for (int x = 0; x < MAX_OBJECTS; x++) {
mBitmapObjectsArray[x] = new BitmapObject(sprite);
}
}
#Override
public void onDraw(Canvas canvas) {
canvas.drawColor(Color.BLACK);
for (int x = 0; x < MAX_OBJECTS; x++) {
mBitmapObjectsArray[x].draw(canvas);
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// Stuff.
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
// Stuff.
}
#Override
public void onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
for (int x = 0; x < MAX_OBJECTS; x++) {
boolean isAlive = mBitmapObjectsArray[x].getIsAlive();
if (!isAlive) {
mBitmapObjectsArray[x].setNewPosition((int) event.getX(),
(int) event.getY());
mBitmapObjectsArray[x].setIsAlive(true);
break;
}
}
}
}
}
Ok, this is only the concept of drawing 2 bitmaps on a canvas. Actual implementation is much more serious.
Bitmap renderbmp1 = Bitmap.createBitmap( bitmapWidth,bitmapHeight,Bitmap.Config.RGB_565 );
Bitmap renderbmp2 = Bitmap.createBitmap( bitmapWidth,bitmapHeight,Bitmap.Config.RGB_565 );
Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG);
Canvas c = getHolder().lockCanvas(null);
c.drawBitmap(renderbmp1, left1, top1, paint);
c.drawBitmap(renderbmp2, left2, top2, paint);
getHolder().unlockCanvasAndPost(c);
renderbmp1.recycle();
renderbmp2.recycle();