I've been trying to figure this out for a while now... I need to place marks over top of a seekBar to show the user places that they bookmarked in the past. The data is stored in xml. The problem is making the little ovals appear over the seekBar... It just doesn't work...
Here's my code:
public class seekMark extends View {
private int seekLength; // in pixels
private int seekLeftPad; // in pixels
private int seekBottomPad; // in pixels
private int trackLength; // in ms
private float pxOverMs; // in px/ms
ShapeDrawable lmark;
private seekMark instance;
public seekMark(Context context){
super(context);
instance = this;
seekLength = progressBar.getWidth();
seekLeftPad = progressBar.getPaddingLeft();
seekBottomPad = progressBar.getBottom();
trackLength = player.getDuration();
pxOverMs = pxPerMs();
lmark = new ShapeDrawable(new OvalShape());
}
private float pxPerMs(){
return ((float) seekLength)/((float) trackLength);
}
private int[] markPxList() throws XmlPullParserException, IOException {
int bmStartTime = 0;
String bmNames[] = bmNameList(xmlPath);
int[] bmPos = new int[bmNames.length];
for(int i=0; i < bmNames.length; i++){
bmStartTime = getBookmark(xmlPath, bmNames[i]);
bmPos[i] = (int) (bmStartTime * pxOverMs);
}
return (bmPos);
}
public void markPlace() throws XmlPullParserException, IOException {
int y = seekBottomPad;
int x = 0;
int bmPos[] = markPxList();
for(int i = 0; i < bmPos.length; i++){
x = bmPos[i] + seekLeftPad;
lmark = new ShapeDrawable();
lmark.getPaint().setColor(0xff74AC23);
lmark.setBounds(x, y, x + 1, y + 1);
instance.invalidate();
}
}
protected void onDraw(Canvas canvas) {
lmark.draw(canvas);
}
}
It's called from onCreate using this code. I call it using in another thread to avoid the problem where the dimensions of progressBar aren't yet set in onCreate.
Display display = ((WindowManager) getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
if (display.getRotation() == 1){ // if landscape
final Runnable runner = new Runnable() {
public void run() {
seekMark seekMarks = new seekMark(context);
try {
seekMarks.markPlace();
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// runs in another thread to avoid the problem with calling
// seekMark directly from onCreate
}
};
handler.postDelayed(runner, 1000);
}
The program crashes whenever I try to call seekMark.markPlace()... I'm trying to draw this over top of my layout main.xml.
im not sure if this is what you are trying to do.
Customize Seekbar
this seems to be similar while the approach is different.
Related
Android added notch support on API 28, but how to handle it on devices running API 27 (Honor 10, Huawei P20, etc.) ?
I was trying to use DisplayCutoutCompat but I was not able to create an instance of it since documentation does not really point out how create one.
How to create the constructor parameter values: Rect safeInsets, List<Rect> boundingRects?
I also looked into the source code of the constructor, which is a bit confusing to me:
public DisplayCutoutCompat(Rect safeInsets, List<Rect> boundingRects) {
this(SDK_INT >= 28 ? new DisplayCutout(safeInsets, boundingRects) : null);
}
This will always return null on devices running API < 28.
Thank you in advance.
Google provided notch related APIs in Android P. Devices with notch and API version lower than P implemented their own notch APIs.You can consult the APIs from device specified documentation.
Also I did not see creation of DisplayCutoutCompat instance in official documentation, but you can create DisplayCutout as follow:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
DisplayCutout displayCutout = getWindow().getDecorView().getRootWindowInsets().getDisplayCutout();
}
So you want to handle notch(display cutout) in android API lower than 28. That's horrible because different manufactures has different implementations. Nevertheless, all use Java reflection to get notch information. Factory design pattern should be used here.
interface ICutout {
public boolean hasCutout();
public Rect[] getCutout();
}
Huawei display cutout
private static class HuaweiCutout implements ICutout {
private Context context;
public HuaweiCutout(#NonNull Context context) {
this.context = context;
}
#Override
public boolean hasCutout() {
try {
ClassLoader classLoader = context.getClassLoader();
Class class_HwNotchSizeUtil = classLoader.loadClass("com.huawei.android.util.HwNotchSizeUtil");
Method method_hasNotchInScreen = class_HwNotchSizeUtil.getMethod("hasNotchInScreen");
return (boolean) method_hasNotchInScreen.invoke(class_HwNotchSizeUtil);
} catch (Exception e) {
}
return false;
}
#Override
public Rect[] getCutout() {
try {
ClassLoader classLoader = context.getClassLoader();
Class class_HwNotchSizeUtil = classLoader.loadClass("com.huawei.android.util.HwNotchSizeUtil");
Method method_getNotchSize = class_HwNotchSizeUtil.getMethod("getNotchSize");
int[] size = (int[]) method_getNotchSize.invoke(class_HwNotchSizeUtil);
int notchWidth = size[0];
int notchHeight = size[1];
int screenWidth = DeviceUtil.getScreenWidth(context);
int x = (screenWidth - notchWidth) >> 1;
int y = 0;
Rect rect = new Rect(x, y, x + notchWidth, y + notchHeight);
return new Rect[] {rect};
} catch (Exception e) {
}
return new Rect[0];
}}
Oppo display cutout
private static class OppoCutout implements ICutout {
private Context context;
public OppoCutout(#NonNull Context context) {
this.context = context;
}
#Override
public boolean hasCutout() {
String CutoutFeature = "com.oppo.feature.screen.heteromorphism";
return context.getPackageManager().hasSystemFeature(CutoutFeature);
}
#Override
public Rect[] getCutout() {
String value = SystemProperties.getProperty("ro.oppo.screen.heteromorphism");
String[] texts = value.split("[,:]");
int[] values = new int[texts.length];
try {
for(int i = 0; i < texts.length; ++i)
values[i] = Integer.parseInt(texts[i]);
} catch(NumberFormatException e) {
values = null;
}
if(values != null && values.length == 4) {
Rect rect = new Rect();
rect.left = values[0];
rect.top = values[1];
rect.right = values[2];
rect.bottom = values[3];
return new Rect[] {rect};
}
return new Rect[0];
}}
Vivo display cutout
private static class VivoCutout implements ICutout {
private Context context;
public VivoCutout(#NonNull Context context) {
this.context = context;
}
#Override
public boolean hasCutout() {
try {
ClassLoader clazz = context.getClassLoader();
Class ftFeature = clazz.loadClass("android.util.FtFeature");
Method[] methods = ftFeature.getDeclaredMethods();
for(Method method: methods) {
if (method.getName().equalsIgnoreCase("isFeatureSupport")) {
int NOTCH_IN_SCREEN = 0x00000020; // 表示是否有凹槽
int ROUNDED_IN_SCREEN = 0x00000008; // 表示是否有圆角
return (boolean) method.invoke(ftFeature, NOTCH_IN_SCREEN);
}
}
} catch (Exception e) {
}
return false;
}
#Override
public Rect[] getCutout() {
// throw new RuntimeException(); // not implemented yet.
return new Rect[0];
}}
Xiaomi display cutout of Android Oreo, of Android Pie
private static class XiaomiCutout implements ICutout {
private Context context;
public XiaomiCutout(#NonNull Context context) {
this.context = context;
}
#Override
public boolean hasCutout() {
// `getprop ro.miui.notch` output 1 if it's a notch screen.
String text = SystemProperties.getProperty("ro.miui.notch");
return text.equals("1");
}
#Override
public Rect[] getCutout() {
Resources res = context.getResources();
int widthResId = res.getIdentifier("notch_width", "dimen", "android");
int heightResId = res.getIdentifier("notch_height", "dimen", "android");
if(widthResId > 0 && heightResId > 0) {
int notchWidth = res.getDimensionPixelSize(widthResId);
int notchHeight = res.getDimensionPixelSize(heightResId);
// one notch in screen top
int screenWidth = DeviceUtil.getScreenSize(context).getWidth();
int left = (screenWidth - notchWidth) >> 1;
int right = left + notchWidth;
int top = 0;
int bottom = notchHeight;
Rect rect = new Rect(left, top, right, bottom);
return new Rect[] {rect};
}
return new Rect[0];
}}
For SystemProperties class, you can read this thread, actually it calls adb shell getprop <property_name>.
In case some manufactures' not coming up with a getNotchHeight() method, you can just use the status bar's height. Android has guarantee that notch height is at most the status bar height.
public static int getStatusBarHeight(Context context) {
int statusBarHeight = 0;
Resources res = context.getResources();
int resourceId = res.getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
statusBarHeight = res.getDimensionPixelSize(resourceId);
}
return statusBarHeight;
}
For Android Pie and above (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P), you can use system's API to get notch information. Note the window must be attachedActivity#onAttachedToWindow or you will get null DisplayCutout.
DisplayCutout displayCutout = activity.getWindow().getDecorView().getRootWindowInsets().getDisplayCutout();
I had similar issue, and had to use reflection to access what I need.
My problem was that I had some calculations depending on screen size and while not accessing the notch space, the calculations were wrong and code didn't work well.
public static final String CLASS_DISPLAY_CUTOUT = "android.view.DisplayCutout";
public static final String METHOD_GET_DISPLAY_CUTOUT = "getDisplayCutout";
public static final String FIELD_GET_SAFE_INSET_TOP = "getSafeInsetTop";
public static final String FIELD_GET_SAFE_INSET_LEFT = "getSafeInsetLeft";
public static final String FIELD_GET_SAFE_INSET_RIGHT = "getSafeInsetRight";
public static final String FIELD_GET_SAFE_INSET_BOTTOM = "getSafeInsetBottom";
try {
WindowInsets windowInsets = activity.getWindow().getDecorView().getRootWindowInsets();
if (windowInsets == null) {
return;
}
Method method = WindowInsets.class.getMethod(METHOD_GET_DISPLAY_CUTOUT);
Object displayCutout = method.invoke(windowInsets);
if (displayCutout == null) {
return;
}
Class clz = Class.forName(CLASS_DISPLAY_CUTOUT);
int top = (int) clz.getMethod(FIELD_GET_SAFE_INSET_TOP).invoke(displayCutout);
int left = (int) clz.getMethod(FIELD_GET_SAFE_INSET_LEFT).invoke(displayCutout);
int right = (int) clz.getMethod(FIELD_GET_SAFE_INSET_RIGHT).invoke(displayCutout);
int bottom = (int) clz.getMethod(FIELD_GET_SAFE_INSET_BOTTOM).invoke(displayCutout);
Rect rect = new Rect(left, top, right, bottom);
} catch (Exception e) {
Log.e(TAG, "Error when getting display cutout size");
}
To handle API lower then 28 WindowInsetsCompat can be used. Kotlin example:
WindowInsetsCompat.toWindowInsetsCompat(window.decorView.rootWindowInsets).displayCutout
I have used the following animation class because i have multiple images to make an animation. After the 7th animation i get OutofMemoryError. androidgraphics.Bitmap.createBitmap
public class AnimationsContainer {
public int FPS = 30; // animation FPS
// single instance procedures
private static AnimationsContainer mInstance;
private AnimationsContainer() {
};
public static AnimationsContainer getInstance() {
if (mInstance == null)
mInstance = new AnimationsContainer();
return mInstance;
}
// animation progress dialog frames
private int[] mProgressAnimFrames = {};
// animation splash screen frames
/**
* #param imageView
* #return progress dialog animation
*/
public FramesSequenceAnimation createProgressDialogAnim(ImageView imageView) {
return new FramesSequenceAnimation(imageView, mProgressAnimFrames);
}
/**
* #param imageView
* #return splash screen animation
*/
public FramesSequenceAnimation createSplashAnim(ImageView imageView, int[] n) {
return new FramesSequenceAnimation(imageView, n);
}
/**
* AnimationPlayer. Plays animation frames sequence in loop
*/
public class FramesSequenceAnimation {
private int[] mFrames; // animation frames
private int mIndex; // current frame
private boolean mShouldRun; // true if the animation should continue running. Used to stop the animation
private boolean mIsRunning; // true if the animation currently running. prevents starting the animation twice
private SoftReference<ImageView> mSoftReferenceImageView; // Used to prevent holding ImageView when it should be dead.
private Handler mHandler;
private int mDelayMillis;
private OnAnimationStoppedListener mOnAnimationStoppedListener;
private Bitmap mBitmap = null;
private BitmapFactory.Options mBitmapOptions;
public FramesSequenceAnimation(ImageView imageView, int[] frames) {
mHandler = new Handler();
mFrames = frames;
mIndex = -1;
mSoftReferenceImageView = new SoftReference<ImageView>(imageView);
mShouldRun = false;
mIsRunning = false;
mDelayMillis = 50;
imageView.setImageResource(mFrames[0]);
// use in place bitmap to save GC work (when animation images are the same size & type)
if (Build.VERSION.SDK_INT >= 11) {
Bitmap bmp = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
int width = bmp.getWidth();
int height = bmp.getHeight();
Bitmap.Config config = bmp.getConfig();
mBitmap = Bitmap.createBitmap(width, height, config);
mBitmapOptions = new BitmapFactory.Options();
// setup bitmap reuse options.
mBitmapOptions.inBitmap = mBitmap;
mBitmapOptions.inMutable = true;
mBitmapOptions.inSampleSize = 1;
}
}
private int getNext() {
mIndex++;
if (mIndex == mFrames.length){
mIndex = mIndex - 1;
mShouldRun = false;
}
return mFrames[mIndex];
}
/**
* Starts the animation
*/
public synchronized void start() {
mShouldRun = true;
if (mIsRunning)
return;
Runnable runnable = new Runnable() {
#Override
public void run() {
ImageView imageView = mSoftReferenceImageView.get();
if (!mShouldRun || imageView == null) {
mIsRunning = false;
if (mOnAnimationStoppedListener != null) {
mOnAnimationStoppedListener.onAnimationStopped();
}
return;
}
mIsRunning = true;
mHandler.postDelayed(this, mDelayMillis);
if (imageView.isShown()) {
int imageRes = getNext();
if (mBitmap != null) { // so Build.VERSION.SDK_INT >= 11
Bitmap bitmap = null;
try {
bitmap = BitmapFactory.decodeResource(imageView.getResources(), imageRes, mBitmapOptions);
} catch (Exception e) {
e.printStackTrace();
}
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
} else {
imageView.setImageResource(imageRes);
mBitmap.recycle();
mBitmap = null;
}
} else {
imageView.setImageResource(imageRes);
}
}
}
};
mHandler.post(runnable);
}
/**
* Stops the animation
*/
public synchronized void stop() {
mShouldRun = false;
}
}
}
Can anyone explain me why and tell me how to fix it? I already added
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
to my manifest file.
It might help to be sure that you are not keeping references to unneeded images. This is happening because you probably have the "standard" memory heap of 32MB or something similar (maybe 64MB or maybe 16MB). If you consider that most images are 5MB or more, it's not surprising you are out of memory.
You can increase the heap size using android:largeHeap="true" like this:
How to increase heap size of an android application?
I have really wired problem..I searced a lot in google and here and con't solve my problem.
I have main activity which create and start 3 threads (only "gameThread" is relevant now).
When I click on the back buttom it seems ok , but when I come back to the activity and the thread continue running , it seems it run duplicate - I see only one ContentView on the screen, but there is more one running (maybe behind).
I hope I explain myselg good..
this is the code:
package com.example.newpingpong;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.widget.Toast;
/*
* The main activity class of the Ping Pong game
*/
public class MainActivity extends Activity
{
/*
* The following class variables hold references to the objects the game is composed of -
* PingPongView - main view,
* gameBall - the ball in the game,
* gamePaddle - the paddle in the game,
* gameThread - main thread updating the position of the ball,
* paddleMoverThread - another thread which is responsible for moving the paddle.
*/
private PingPongView gameView;
private Tray gamePaddle;
private GameLevel gameLevel;
private GameLevel [] levels;
//threads
private PingPongGame gameThread;
private PaddleMover paddleMoverThread;
private PresentThread giftThread;
private Messages message;
//balls container
private BallView [] ballViewArr;
private Ball [] ballArr;
//gifts container
private Gift[] giftArr;
private GiftView[] GiftViewArr;
public GameSounds gameSounds;
private int screenWidth;
private int screenHeight;
private TrayView paddleView;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
Toast.makeText(this, "onCreate!", Toast.LENGTH_SHORT).show();
// Get the message from the intent
Intent intent = getIntent();
String levelChosed = intent.getStringExtra(ImageAdapter.EXTRA_MESSAGE);
int levelChosed_int = Integer.parseInt(levelChosed);
Const.currentLevel = levelChosed_int;
Const.isCompleteThisLevel = false;
GameLevelView gameLevelView;
//for sounds
if(gameSounds == null){
gameSounds = new GameSounds();
gameSounds.addSound(Const.CATCH_GIFT, R.raw.cathc_gift, getBaseContext());
gameSounds.addSound(Const.GIFT_START_FALL, R.raw.gift_start_fall, getBaseContext());
gameSounds.addSound(Const.HIT_BLOCK, R.raw.hit_block, getBaseContext());
gameSounds.addSound(Const.HIT_KIR, R.raw.hit_kir, getBaseContext());
gameSounds.addSound(Const.GAME_OVER, R.raw.game_over, getBaseContext());
gameSounds.addSound(Const.HIT_PADDLE, R.raw.hit_paddle, getBaseContext());
}
if(this.ballViewArr == null){
this.ballViewArr = new BallView[Const.MAX_BALLS];
this.ballArr = new Ball[Const.MAX_BALLS];
this.giftArr = new Gift[Const.MAX_GIFTS];
this.GiftViewArr = new GiftView[Const.MAX_GIFTS];;
// Getting the screen width & height
screenWidth = this.getWindowManager().getDefaultDisplay().getWidth();
screenHeight = this.getWindowManager().getDefaultDisplay().getHeight();
for(int i=0 ; i< Const.MAX_BALLS ; i++){
this.ballArr[i] = new Ball(screenWidth / 2, screenHeight / 2, screenWidth, screenHeight);
this.ballViewArr[i] = new BallView(this, this.ballArr[i]);
this.ballArr[i].setMode(Const.NOT_ACTIVE);
}
this.ballArr[0].setMode(Const.ACTIVE);
for(int i=0 ; i< Const.MAX_GIFTS ; i++){
this.giftArr[i] = new Gift(screenWidth / 2, screenHeight / 4,screenWidth, null, Const.NOT_ACTIVE);
this.GiftViewArr[i] = new GiftView(this, this.giftArr[i]);
}
// Creating the paddle
gamePaddle = new Tray(screenWidth / 2, (int)(screenHeight * 0.75), screenWidth);
// Creating the paddle view, and giving it the gameBall as a parameter
paddleView = new TrayView(this, gamePaddle);
levels = new GameLevel[Const.LevelsAmount];
levels[0] = new GameLevel(Const.level0, screenWidth , screenHeight, this.giftArr);
levels[1] = new GameLevel(Const.level1, screenWidth , screenHeight, this.giftArr);
levels[2] = new GameLevel(Const.level2,screenWidth , screenHeight, this.giftArr);
}
gameLevel=levels[levelChosed_int];
gameLevelView = new GameLevelView(this,gameLevel);
// Creating the game view
this.gameView = new PingPongView(this);
message = new Messages(this , screenWidth , screenHeight);
// Give the gameView our ballView & paddleView.
gameView.setViews(paddleView,gameLevelView , message ,this.ballViewArr , this.GiftViewArr);
// Setting the gameView as the main view for the PingPong activity.
setContentView(gameView);
if(gameThread == null){
//create the main thread
gameThread = new PingPongGame(this.getResources(), gamePaddle, gameView, gameLevel , message , ballArr , gameSounds);
//create the thread responsible for moving the paddle
paddleMoverThread = new PaddleMover(gamePaddle, gameView);
//create the thread responsible for present
giftThread = new PresentThread(gamePaddle , gameView , gameLevel, message , giftArr , ballArr,gameSounds );
gameThread.start();
paddleMoverThread.start();
giftThread.start();
}
}
//This method is automatically called when the user touches the screen
#Override
public boolean onTouchEvent(MotionEvent event)
{
float destination;
// Toast.makeText(this, "try!", Toast.LENGTH_SHORT).show();
//get the x coordinate of users' press
destination = event.getX();
//notify the paddle mover thread regarding the new destination
gamePaddle.setPaddleDestination(destination);
return true;
}
#Override
protected void onPause() {
Toast.makeText(this, "onPause!", Toast.LENGTH_SHORT).show();
Const.toStop = true;
while(Const.toStopMessageHasRecieved == false){
} //the thread not see the message that he shoult stop
//here the thrad recived the message and stoped.
super.onPause();
}
#Override
public void onResume()
{
Toast.makeText(this, "onResume!", Toast.LENGTH_SHORT).show();
for(int i=0 ; i< Const.MAX_BALLS ; i++){
this.ballArr[i] = new Ball(screenWidth / 2, screenHeight / 2, screenWidth, screenHeight);
this.ballViewArr[i] = new BallView(this, this.ballArr[i]);
this.ballArr[i].setMode(Const.NOT_ACTIVE);
}
this.ballArr[0].setMode(Const.ACTIVE);
synchronized (Const.mPauseLock) {
Const.toStop = false;
Const.mPauseLock.notifyAll();
}
super.onResume();
}
}
AND THE THREAD:
package com.example.newpingpong;
import android.content.res.Resources;
import android.graphics.Color;
public class PingPongGame extends Thread {
private PingPongView gameView;
private Tray gamePaddle;
private GameLevel gameLevel;
private Messages message;
private Ball [] ballArr;
private GameSounds gameSounds;
private Resources res;
//private int screenWidth;
//private int screenHight;
public PingPongGame(Resources res ,Tray theTray , PingPongView mainView , GameLevel gameLevel ,
Messages message , Ball ballArr[] ,GameSounds gameSounds)
{
this.gameView = mainView;
this.ballArr = ballArr;
this.gamePaddle = theTray;
this.gameLevel=gameLevel;
this.message = message;
this.gameSounds=gameSounds;
this.res=res;
//this.screenWidth = screenWidth;
//this.screenHight = screenHight;
//for stop and resume threads
}
//main method of the current thread
#Override
public void run()
{
try {
PingPongGame.sleep(2000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
this.message.setMessage(res.getString(R.string.lives) + gameLevel.getLives());
gameView.postInvalidate();
//infinitely loop, and move the balls accordingly
while ((Const.isLose == false) && (Const.isCompleteThisLevel==false))
{
for(int i=0; i<Const.MAX_BALLS ; i++){
if(ballArr[i].getMode() != Const.NOT_ACTIVE){
//move the ball one step
if(ballArr[i].move()) //hit kir
gameSounds.play(Const.HIT_KIR,false);
gameView.postInvalidate();
//check if the ball hit some block
if(gameLevel.updateIfHit(ballArr[i])){
ballArr[i].down();
gameSounds.play(Const.HIT_BLOCK,false);
}
gameView.postInvalidate();
if(gameLevel.getLives() == 0){
//gameLevel.restart();
this.message.setMessage(res.getString(R.string.game_over));
gameView.postInvalidate();
gameSounds.play(Const.GAME_OVER,false);
Const.isLose = true;
try
{
PingPongGame.sleep(2000);
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
}
//check if the ball has reached the paddle
if(checkHitPaddle(i,ballArr[i]) == true){ //משתמש פספס and lives --
this.message.setMessage(res.getString(R.string.lives)+"" + gameLevel.getLives());
//send a request to refresh the display
gameView.postInvalidate();
}
}
}
try
{
PingPongGame.sleep(5);
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
//public static boolean toStop = false;
//public static boolean toStopMessageHasRecieved = false;
//for stop and resume
//for stop and resume
synchronized (Const.mPauseLock) {
while (Const.toStop) {
Const.toStopMessageHasRecieved = true;
try {
Const.mPauseLock.wait();
} catch (InterruptedException e) {
}
}
}
}
// Const.toStopMessageHasRecieved = true;
}
//This method is responsible for handling the situation in which the ball has reached the height of the paddle
//return true if lives--
private boolean checkHitPaddle(int i , Ball ball)
{
// Checking if the ball had hit the paddle
if (ball.getBottom() >= gamePaddle.getTop())
{
// check if the paddle is in the right place
if ((ball.getRight() > gamePaddle.getLeft()) &&
(ball.getLeft() < gamePaddle.getRight()))
{
//The ball had hit the paddle - so it should be bounced
gameSounds.play(Const.HIT_PADDLE,false);
ball.bounceUp();
}
else
{
//The ball had missed the paddle - player looses - restart the game
gameSounds.play(Const.GAME_OVER,false); //not really game over - just lose live
boolean isMoreBall = false;
for(int j=0 ; j< Const.MAX_BALLS ; j++)
if( (j != i) && (ballArr[j].getMode() == Const.ACTIVE)){
isMoreBall = true;
break;
}
if(isMoreBall){
ball.setMode(Const.NOT_ACTIVE);
//for(int k=0; k<Const.MAX_BALLS ; k++){
//ballArr[k].downSpeed(0.25);
//}
}
else{
gameLevel.setLives(gameLevel.getLives()-1);
ball.restart();
return true;
}
}
}
return false;
}
}
Everytime the Activity is started you create a new gameThread in onCreate since the Activity is a new object then and gameThread is null. But you never stop them. You should probably insert something that ends your threads in onDestroy.
At the moment both Threads modify your Const object. Which leads me to the main problem:
I think the main problem is that all your communication is handled by some 'Global Variable Collection' in the Const object which can be very confusing the bigger the game gets. For now a solution could be to let the Const object handle the Threads too, but that would possibly only make it worse on the long run.
Try using
Timer rather than Thread.
And cancel timer using timer.cancel() in onBackPressed() method.
Im new to android dev and using andengine and ive just come across a problem when dealing with a large animation that covers more then 1 sprite sheet. Basically I have a large sprite whose animation runs across 2 sprite sheets. Im trying to find a way to load them successfully. I will show you what I am trying and hopefully some one can either show me the correct way or help me finish it my way.
i start off by creating the 2 texture packs from the xml files.
these are created fine
TexturePackTextureRegionLibrary packer1 = null,packer2 = null;
TexturePack spritesheetTexturePack1 = null,spritesheetTexturePack2 = null;
try {
spritesheetTexturePack1 = new TexturePackLoader(activity.getTextureManager(), "Animation/Jack/")
.loadFromAsset(activity.getAssets(), "Jack_walk1" + ".xml");
spritesheetTexturePack1.loadTexture();
packer1 = spritesheetTexturePack1.getTexturePackTextureRegionLibrary();
} catch (final TexturePackParseException e) {
Debug.e(e);
}
TexturePackerTextureRegion textureRegion = packer1.get(Jack_walk1.LOOP_JACK_WALK_TO_SAFE_AREA_00000_ID);
try {
spritesheetTexturePack2 = new TexturePackLoader(activity.getTextureManager(), "Animation/Jack/")
.loadFromAsset(activity.getAssets(), "Jack_walk2" + ".xml");
spritesheetTexturePack2.loadTexture();
packer2 = spritesheetTexturePack2.getTexturePackTextureRegionLibrary();
} catch (final TexturePackParseException e) {
Debug.e(e);
}
TexturePackerTextureRegion textureRegion2 = packer2.get(Jack_walk1.LOOP_JACK_WALK_TO_SAFE_AREA_00000_ID);
ArrayList<SparseArray> animList = new ArrayList<SparseArray>();
animList.add(packer1.getIDMapping());
animList.add(packer2.getIDMapping());
TiledTextureRegion text1 = TiledTextureRegion.create(textureRegion.getTexture(), (int) textureRegion.getTextureX(), (int) textureRegion.getTextureY(), animList);
I then added this function to the tiledtextureregion to take in a list of arrays that hold the frame information and step through adding them to the itexturregion array
public static TiledTextureRegion create(final ITexture pTexture, final int pTextureX, final int pTextureY, final ArrayList<SparseArray> animList) {
ITextureRegion[] textureRegions = null;
int maxFrame = 0;
for(int i = 0; i < animList.size(); i++){
maxFrame += animList.get(i).size();
}
int currentFrame = 0;
textureRegions = new ITextureRegion[maxFrame];
for(int i = 0 ; i < animList.size(); i++){
SparseArray<? extends ITexturePackTextureRegion> packer = animList.get(i);
for(int j = 0; j < packer.size(); j++) {
if (packer.valueAt(j)!= null){
final int x = (int) packer.valueAt(j).getTextureX();
final int y = (int) packer.valueAt(j).getTextureY();
final int width = packer.valueAt(j).getSourceWidth();
final int height = packer.valueAt(j).getSourceHeight();
final Boolean rotated = packer.valueAt(j).isRotated();
textureRegions[currentFrame] = new TextureRegion(pTexture, x, y, width, height, rotated);
currentFrame++;
}
}
}
return new TiledTextureRegion(pTexture, false, textureRegions);
}
but the line return new TiledTextureRegion(pTexture, false, textureRegions); is expecting 1 texture do retrieve the frames from when creating the tiled region. Any ideas where i should go from here or is there a super easy way to handle this that i have over looked. Thanks for any help
This class is designed to work with a single texture.
If you cannot merge your textures into one (which I think is the case), then you can try to write a new class implementing ITextureRegion that will contain 2 or more TiledTextureRegion objects, and which will have a method to select one of these at will.
You will just have to delegate the remaining methods of ITextureRegion to the selected object.
public class MultipleTextureRegion implements ITextureRegion {
private List<TiledTextureRegion> internal;
private int selected=0;
//...
public void add(TiledTextureRegion region) {
internal.add(region);
}
public void select(int index) {
selected=index;
}
//...
// Delegates all ITextureRegion methods ...
public int getWidth() { return internal.get(selected).getWidth(); }
// And so on...
}
I have a custom view with a lot of png images on it(For every three characters an image). but it is too slow on drawing and scrolling.
It is my code for custom view:
public class Textview extends View
{
private String m_szText;
Context ctx;
Paint mTextPaint;
private Canvas canva;
Bitmap b ;
public Textview(Context context)
{
super(context);
ctx = context;
mTextPaint= new Paint();
mTextPaint.setTypeface(m_tTypeface);
mTextPaint.setStyle(Paint.Style.FILL);
}
public void SetText(String newtext) {
m_szText = newtext;
text(newtext);
this.invalidate();
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(text(canvas,m_szText));
}
Canvas text(Canvas canvas,String txt)
{
int left = 400;
int top = 0;
try {
for(int i=0;i<txt.length();i=i+3)
{
String adres = "glyph/" + txt.substring(i, i+3) + ".png";
Bitmap btm = getBitmapFromAsset(adres);
if(left <= 5)
{left = 400;top += btm.getHeight();}
else
left = left - btm.getWidth();
canvas.drawBitmap(btm, left ,top,mTextPaint);
}
} catch (IOException e) {
canvas.drawText(e.toString(), 50, 50, mTextPaint);
}
return canvas;
}
private Bitmap getBitmapFromAsset(String strName) throws IOException
{
AssetManager assetManager = ctx.getAssets();
InputStream istr = assetManager.open(strName);
Bitmap bitmap = BitmapFactory.decodeStream(istr);
return bitmap;
}
}
How can I speed up my custom view? I think I must to create bitmap of all images once. but how to?
thanks in advance!
You are loading and decoding several bitmaps on every draw. You need to load the bitmaps ahead of time, once and then draw them.
You can use Thread to speed up process, and there are two way to use thread
1)Implementing Runnable that override void run(){}
2)or use Thread th=new Thread(new Runnable(){void run(){}
})
The following should help. just outline of what can be done.
static HashMap<String, Bitmap> mBitmaps = new HashMap<String, Bitmap>();
public void SetText(String newtext) {
m_szText = newtext;
makeBitmap();
this.invalidate();
}
void makeBitmap()
{
for(int i=0; i<m_szText.length(); i=i+3)
{
String adres = "glyph/" + m_szText.substring(i, i+3) + ".png";
Bitmap btm = null;
if (!mBitmaps.containsKey(adres)) {
btm = getBitmapFromAsset(adres);
mBitmaps.add(adres , btm);
} else {
btm = (Bitmap)mBitmaps.get(adres);
}
length += btm.getWidth(); // considering only single line.
}
// create a new blank Bitmap of height and 'length' and assign to member.
mTextBitmap = Bitmap.createBitmap(length, height, Bitmap.Config.ARGB_8888);
// in for loop draw all the bitmaps on mTextBitmap just like you did on canvas in ur code.
}