My Requirement : I need to animate a 7*16 LED Display by passing frames through the Android App over BluetoothLE. I have created the design of the display on the app and added empty views with gradient drawable background to them. The colour of these views need to change when my touch moves into them. Adding a touch listener to each view isn't going to help in my case.
What I have achieved : I have a large number of views (100+) added programmatically with a tag set to each of them. I have set an OnTouch Event Handler for the parent view in which these views have been added.
By tracking the absolute coordinates of the touch event (x and y) and comparing with the absolute bounds of a few individual views that I am looping in the touch event handler, I am able to detect hover like touch move (out of bounds to in bounds) over 3-4 views properly.
I have referred the solution from https://stackoverflow.com/a/21903640
Where I am stuck : However, when I try to increase the loop size to cover all the added views, the app response slows down and hover detection fails on most of the views. I know this is happening because of heavy computation in the OnTouch Event Handler which I am not supposed to do.
What I need : I need an improvement on this solution in terms of performance or an alternative way to go about reaching my goal.
Code Snippet
void DrawScreen()
{
for (int column = 0; column < 8; column++)
{
for (int row = 0; row < 17; row++)
{
relativeLayout.AddView(DrawRect(row, column));
}
}
}
View DrawRect(int row, int column)
{
View customView = new View(Context);
GradientDrawable shape = new GradientDrawable();
shape.SetShape(ShapeType.Rectangle);
shape.SetCornerRadii(new float[] { 10, 10, 10, 10, 10, 10, 10, 10 });
shape.SetColor(Color.ParseColor("#3F0000"));
RelativeLayout.LayoutParams param = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.WrapContent);
param.LeftMargin = ((column-1) * (width + h_spacing)) + h_spacing;
param.Width = width;
param.Height = height;
param.TopMargin = ((row-1) * (height + v_spacing)) + v_spacing;
customView.Background = shape;
customView.LayoutParameters = param;
customView.Tag = (8 - column).ToString() + "," + (17 - row).ToString();
return customView;
}
private void RelativeLayout_Touch(object sender, View.TouchEventArgs e)
{
if(e.Event.Action == MotionEventActions.Up)
{
out_of_bounds = true;
view_in_bound = null;
}
else
{
for (int row = 1; row < 8; row++)
{
for (int column = 1; column < 17; column++)
{
View view = relativeLayout.FindViewWithTag(row.ToString() + "," + column.ToString());
if (CheckInterSection(view, e.Event.RawX, e.Event.RawY))
{
if (out_of_bounds == true)
{
view_in_bound = view;
out_of_bounds = false;
Log.Debug("Touch", "Inside");
//ToggleViewState(view);
}
else
{
Log.Debug("Touch", "Still Inside");
}
}
else
{
if (view == view_in_bound)
{
out_of_bounds = true;
view_in_bound = null;
Log.Debug("Touch", "Outside");
}
}
}
}
}
}
bool CheckInterSection(View view, float rawX, float rawY)
{
int[] location = new int[2];
view.GetLocationOnScreen(location);
int x = location[0] - h_spacing/2;
int y = location[1] - v_spacing/2;
int width = (view.Width + h_spacing/2);
int height = (view.Height + v_spacing/2);
return (!(rawX < x || rawX > (x + width) || rawY < y || rawY > (y + height)));
}
I tried to use trajectory angles to further reduce the loop size but the performance was never up to my expectations and touch events over views were getting missed regularly.
Then I realized that I was going in the wrong direction and found a much simpler solution. Since my views were being added programmatically and were of same size, I knew the coordinates and bounds of each view in the layout. So I divided the layout into a grid and depending upon the touch coordinates, I was able to identify over which section the touch was on. Below is my solution which has been working perfectly. However, I will wait a while till I mark this as a solution since someone could have a better implementation of my technique or an alternative solution.
void DrawScreen()
{
for (int column = 1; column < 17; column++)
{
for (int row = 1; row < 8; row++)
{
relativeLayout.AddView(DrawRect(row, column));
}
}
}
View DrawRect(int row, int column)
{
View customView = new View(Context);
if (!CheckBit(row - 1, column - 1))
{
customView.SetBackgroundResource(Resource.Drawable.off_rounded_btn);
}
else
{
customView.SetBackgroundResource(Resource.Drawable.rounded_btn);
}
RelativeLayout.LayoutParams param = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.WrapContent);
param.LeftMargin = ((column-1) * (width + h_spacing)) + h_spacing;
param.Width = width;
param.Height = height;
param.TopMargin = ((row-1) * (height + v_spacing)) + v_spacing;
customView.LayoutParameters = param;
customView.Tag = row.ToString() + "," + column.ToString();
return customView;
}
void RelativeLayout_Touch(object sender, View.TouchEventArgs e)
{
if (e.Event.Action == MotionEventActions.Up)
{
view_in_bound = null;
}
else
{
int row = CheckTouchArea(e.Event.RawX, e.Event.RawY)[0];
if (row != 0)
{
int column = CheckTouchArea(e.Event.RawX, e.Event.RawY)[1];
check_view = GetView(row, column);
if (check_view != view_in_bound)
{
ChangeViewState(check_view, Touch_CheckBit(row - 1, column - 1), row - 1, column - 1);
view_in_bound = check_view;
}
}
}
}
int[] CheckTouchArea(float rawX, float rawY)
{
int[] tag = new int[2];
int[] location = new int[2];
relativeLayout.GetLocationOnScreen(location);
float x = location[0] + h_padding / 2;
int y = location[1] + v_padding / 2;
float width = relativeLayout.Width - h_padding;
int height = relativeLayout.Height - v_padding;
if ((!(rawX < x || rawX > (x + width) || rawY < y || rawY > (y + height))))
{
int column = (int)Math.Ceiling((rawX - x) * 16 / width);
int row = (int)(Math.Ceiling((rawY - y) * 7 / height));
tag[0] = row;
tag[1] = column;
}
return tag;
}
Related
I have horizontal RecyclerView with images.
When i scroll it left, i have image order like this.
When i scroll it right, i have order like this.
I need to set central image on top, above left and right ones. How to do it?
I overrided getChildDrawingOrder method:
#Override
protected int getChildDrawingOrder(int childCount, int i) {
int centerChild;
//find center row
if ((childCount % 2) == 0) { //even childCount number
centerChild = childCount / 2; // if childCount 8 (actualy 0 - 7), then 4 and 4-1 = 3 is in centre.
int otherCenterChild = centerChild - 1;
//Which more in center?
View child = this.getChildAt(centerChild);
final int left = child.getLeft();
final int right = child.getRight();
//if this row goes through center then this
final int absParentCenterX = getLeft() + getWidth() / 2;
//Log.i("even", i + " from " + (childCount - 1) + ", while centerChild = " + centerChild);
if ((left < absParentCenterX) && (right > absParentCenterX)) {
//this child is in center line, so it is last
//centerChild is in center, no need to change
} else {
centerChild = otherCenterChild;
}
} else {//not even - done
centerChild = childCount / 2;
//Log.i("not even", i + " from " + (childCount - 1) + ", while centerChild = " + centerChild);
}
int rez;
//find drawIndex by centerChild
if (i > centerChild) {
//below center
rez = (childCount - 1) - i + centerChild;
} else if (i == centerChild) {
//center row
//draw it last
rez = childCount - 1;
} else {
//above center - draw as always
// i < centerChild
rez = i;
}
//Log.i("return", "" + rez);
return rez;
}
Also i set
setChildrenDrawingOrderEnabled(true);
Full code is here.
Fixed with adding ViewCompat.setTranslationZ(view, translationZ);
protected final void updateViews() {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
//setMarginsForChild(child);
if (mAppearAnimationIsWorking)
child.setAlpha(0);
else
child.setAlpha(1);
if (mScaleUnfocusedViews && !mAppearAnimationIsWorking) {
float scale = computeScale(child);
child.setTranslationY(-getMeasuredHeight() * (1 - scaleFactor) / 2);
ViewCompat.setTranslationZ(child, -getPercentageFromCenter(child));
child.setScaleX(scale * scaleFactor * innerScale);
child.setScaleY(scale * scaleFactor * innerScale);
}
}
}
getChildDrawingOrder and setChildrenDrawingOrderEnabled can be removed.
I'm developing a game using SurfaceView which listens to touch events. The onTouchEvent method in SurfaceView works fine for many of the devices, but in some devices, sometimes it doesn't get called (Moto X Style is the one) and my app also stops responding.
I guess that this might be due to the overloading of main thread due to which onTouchEvent is starving.
Could some Android experts over here give me some tips to reduce the load on main thread if it's getting overloaded, or there might be some other reason which may cause this
The code is quite complex but still I'm posting some if you want to go through it
GameLoopThread
public class GameLoopThread extends Thread{
private GameView view;
// desired fps
private final static int MAX_FPS = 120;
// maximum number of frames to be skipped
private final static int MAX_FRAME_SKIPS = 5;
// the frame period
private final static int FRAME_PERIOD = 1000 / MAX_FPS;
private boolean running = false;
public GameLoopThread(GameView view){
this.view = view;
}
public void setRunning(boolean running){
this.running = running;
}
public boolean isRunning() {
return running;
}
#Override
public void run() {
Canvas canvas;
long beginTime; // the time when the cycle begun
long timeDiff; // the time it took for the cycle to execute
int sleepTime; // ms to sleep (<0 if we're behind)
int framesSkipped; // number of frames being skipped
while (running) {
canvas = null;
// try locking the canvas for exclusive pixel editing
// in the surface
try {
canvas = view.getHolder().lockCanvas();
synchronized (view.getHolder()) {
beginTime = System.nanoTime();
framesSkipped = 0; // resetting the frames skipped
// update game state
// render state to the screen
// draws the canvas on the panel
this.view.draw(canvas);
// calculate how long did the cycle take
timeDiff = System.nanoTime() - beginTime;
// calculate sleep time
sleepTime = (int)(FRAME_PERIOD - timeDiff/1000000);
if (sleepTime > 0) {
// if sleepTime > 0 we're OK
try {
// send the thread to sleep for a short period
// very useful for battery saving
Thread.sleep(sleepTime);
} catch (InterruptedException e) {}
}
while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS) {
// update without rendering
// add frame period to check if in next frame
sleepTime += FRAME_PERIOD;
framesSkipped++;
}
}
}
finally {
// in case of an exception the surface is not left in
// an inconsistent state
view.getHolder().unlockCanvasAndPost(canvas);
} // end finally
}
}
}
GameView
public class GameView extends SurfaceView {
ArrayList<Bitmap> circles = new ArrayList<>();
int color;
public static boolean isGameOver;
public GameLoopThread gameLoopThread;
Circle circle; // Code for Circle class is provided below
public static int score = 0;
public static int stars = 0;
final Handler handler = new Handler();
int remainingTime;
boolean oneTimeFlag;
Bitmap replay;
Bitmap home;
Bitmap star;
int highScore;
boolean isLeaving;
public GameView(Context context, ArrayList<Bitmap> circles, int color) {
super(context);
this.circles = circles;
this.color = color;
oneTimeFlag = true;
gameLoopThread = new GameLoopThread(GameView.this);
getHolder().addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceCreated(SurfaceHolder holder) {
if (!gameLoopThread.isRunning()) {
gameLoopThread.setRunning(true);
gameLoopThread.start();
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
gameLoopThread.setRunning(false);
gameLoopThread = new GameLoopThread(GameView.this);
}
});
initializeCircles();
if(!gameLoopThread.isRunning()) {
gameLoopThread.setRunning(true);
gameLoopThread.start();
}
}
public void initializeCircles() {
ArrayList<String> numbers = new ArrayList<>();
for(int i=0;i<10;i++)
numbers.add(i+"");
Random random = new Random();
int position = random.nextInt(4);
numbers.remove(color + "");
int p1 = position;
int r1 = Integer.valueOf(numbers.get(random.nextInt(9)));
numbers.remove(r1+"");
int r2 = Integer.valueOf(numbers.get(random.nextInt(8)));
numbers.remove(r2 + "");
int r3 = Integer.valueOf(numbers.get(random.nextInt(7)));
ArrayList<Bitmap> bitmaps = new ArrayList<>();
if(position == 0) {
bitmaps.add(circles.get(color));
bitmaps.add(circles.get(r1));
bitmaps.add(circles.get(r2));
bitmaps.add(circles.get(r3));
}
else if(position == 1) {
bitmaps.add(circles.get(r1));
bitmaps.add(circles.get(color));
bitmaps.add(circles.get(r2));
bitmaps.add(circles.get(r3));
}
else if(position == 2) {
bitmaps.add(circles.get(r1));
bitmaps.add(circles.get(r2));
bitmaps.add(circles.get(color));
bitmaps.add(circles.get(r3));
}
else {
bitmaps.add(circles.get(r1));
bitmaps.add(circles.get(r2));
bitmaps.add(circles.get(r3));
bitmaps.add(circles.get(color));
}
numbers = new ArrayList<>();
for(int i=0;i<10;i++)
numbers.add(i+"");
position = random.nextInt(4);
numbers.remove(color + "");
r1 = Integer.valueOf(numbers.get(random.nextInt(9)));
numbers.remove(r1 + "");
r2 = Integer.valueOf(numbers.get(random.nextInt(8)));
numbers.remove(r2 + "");
r3 = Integer.valueOf(numbers.get(random.nextInt(7)));
if(position == 0) {
bitmaps.add(circles.get(color));
bitmaps.add(circles.get(r1));
bitmaps.add(circles.get(r2));
bitmaps.add(circles.get(r3));
}
else if(position == 1) {
bitmaps.add(circles.get(r1));
bitmaps.add(circles.get(color));
bitmaps.add(circles.get(r2));
bitmaps.add(circles.get(r3));
}
else if(position == 2) {
bitmaps.add(circles.get(r1));
bitmaps.add(circles.get(r2));
bitmaps.add(circles.get(color));
bitmaps.add(circles.get(r3));
}
else {
bitmaps.add(circles.get(r1));
bitmaps.add(circles.get(r2));
bitmaps.add(circles.get(r3));
bitmaps.add(circles.get(color));
}
circle = new Circle(this, bitmaps, circles, p1, position, color, getContext());
}
#Override
public void draw(Canvas canvas) {
if(canvas != null) {
super.draw(canvas);
canvas.drawColor(Color.WHITE);
if(!isGameOver && timer != null)
stopTimerTask();
try {
circle.draw(canvas);
} catch (GameOverException e) {
isGameOver = true;
if(isLeaving)
gameOver(canvas);
else if(GameActivity.counter > 0) {
gameOver(canvas);
GameActivity.counter++;
} else {
if (oneTimeFlag) {
int size1 = 200 * GameActivity.SCREEN_HEIGHT / 1280;
int size2 = 125 * GameActivity.SCREEN_HEIGHT / 1280;
float ratio = (float) GameActivity.SCREEN_HEIGHT / 1280;
replay = GameActivity.decodeSampledBitmapFromResource(getResources(), R.drawable.replay, size1, size1);
home = GameActivity.decodeSampledBitmapFromResource(getResources(), R.drawable.home, size2, size2);
continueButton = GameActivity.decodeSampledBitmapFromResource(getContext().getResources(), R.drawable.button, (int) (540 * ratio), (int) (100 * ratio));
star = GameActivity.decodeSampledBitmapFromResource(getContext().getResources(), R.drawable.star1, (int) (220 * ratio), (int) (220 * ratio));
int w = (int) ((float) GameActivity.SCREEN_WIDTH * 0.9);
oneTimeFlag = false;
}
if (askPurchaseScreen == 2) {
gameOver(canvas);
} else {
canvas.drawColor(Circle.endColor);
}
}
}
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
circle.onTouch(x, y);
return true;
}
}
Circle
public class Circle {
int x;
int y1;
int y2;
public static float speedY1 = 12.5f*(float)GameActivity.SCREEN_HEIGHT/1280;
public static float speedY2 = 12.5f*(float)GameActivity.SCREEN_HEIGHT/1280;
ArrayList<Bitmap> bitmaps;
GameView gameView;
int p1; // Position of required circle in slot 1
int p2; // Position of required circle in slot 2
int color;
int tempColor;
int width;
Context context;
// Centers of required circle
float centerX1;
float centerX2;
float centerY1;
float centerY2;
ArrayList<Bitmap> circles = new ArrayList<>();
boolean touchedFirst;
boolean touchedSecond;
int count1 = 1; // Slot 1 circle radius animation
int count2 = 1; // Slot 2 circle radius animation
float tempSpeedY1;
float tempSpeedY2;
boolean stopY1;
boolean stopY2;
int barCounter = 1;
int loopCount = 0;
int endGameCount = 0; // Count to move circle upwards
double limit;
float endRadiusSpeed;
int endSlot; // Where you died
int endRadiusCount = 0; // Count to increase circle radius
int barEndCounter = 1;
final Handler handler = new Handler();
boolean exception;
public static int endColor;
public Circle(GameView gameView, ArrayList<Bitmap> bitmaps, ArrayList<Bitmap> circles, int p1, int p2, int color, Context context) {
this.gameView = gameView;
this.bitmaps = bitmaps;
this.circles = circles;
this.p1 = p1;
this.p2 = p2;
this.color = color;
this.context = context;
width = GameActivity.SCREEN_WIDTH / 4 - 10;
x = 10;
y1 = 0;
y2 = -(GameActivity.SCREEN_HEIGHT + width) / 2;
centerX1 = x + p1 * (10 + width) + width / 2;
centerY1 = y1 + width / 2;
centerX2 = x + p2 * (10 + width) + width / 2;
centerY2 = y2 + width / 2;
}
public void update() throws GameOverException {
y1+= speedY1;
y2+= speedY2;
centerY1+= speedY1;
centerY2+= speedY2;
float ratio = (float)GameActivity.SCREEN_HEIGHT/1280;
limit = width/(20*ratio);
if(y1 >= gameView.getHeight()) {
loopCount++;
if(touchedFirst)
touchedFirst = false;
else {
speedY1 = speedY2 = -(12.5f * ratio);
endColor = bitmaps.get(p1).getPixel(width/2, width/2);
endGameCount += 1;
endSlot = 1;
}
if(endGameCount == 0) {
if (stopY1) {
tempSpeedY1 = speedY1;
speedY1 = 0;
ArrayList<Integer> numbers = new ArrayList<>();
for (int i = 0; i < 10; i++) {
if (i != color)
numbers.add(i);
}
tempColor = numbers.get(new Random().nextInt(9));
}
y1 = -(gameView.getWidth() / 4 - 10);
count1 = 1;
setBitmaps(1);
}
}
else if(y2 >= gameView.getHeight()) {
loopCount++;
if(touchedSecond)
touchedSecond = false;
else {
speedY1 = speedY2 = -(12.5f * ratio);
endColor = bitmaps.get(p2 + 4
).getPixel(width/2, width/2);
endGameCount += 1;
endSlot = 2;
}
if(endGameCount == 0) {
if (stopY2) {
tempSpeedY2 = speedY2;
speedY2 = 0;
}
y2 = -(gameView.getWidth() / 4 - 10);
count2 = 1;
setBitmaps(2);
}
}
}
public void setBitmaps(int slot) {
ArrayList<String> numbers = new ArrayList<>();
for(int i=0;i<10;i++)
numbers.add(i+"");
Random random = new Random();
int position = random.nextInt(4);
numbers.remove(color + "");
int r1 = Integer.valueOf(numbers.get(random.nextInt(9)));
numbers.remove(r1+"");
int r2 = Integer.valueOf(numbers.get(random.nextInt(8)));
numbers.remove(r2 + "");
int r3 = Integer.valueOf(numbers.get(random.nextInt(7)));
if(position == 0) {
bitmaps.set((slot - 1)*4, circles.get(color));
bitmaps.set((slot - 1)*4 + 1, circles.get(r1));
bitmaps.set((slot - 1)*4 + 2, circles.get(r2));
bitmaps.set((slot - 1)*4 + 3, circles.get(r3));
}
else if(position == 1) {
bitmaps.set((slot - 1)*4, circles.get(r1));
bitmaps.set((slot - 1)*4 + 1, circles.get(color));
bitmaps.set((slot - 1)*4 + 2, circles.get(r2));
bitmaps.set((slot - 1)*4 + 3, circles.get(r3));
}
else if(position == 2) {
bitmaps.set((slot - 1)*4, circles.get(r1));
bitmaps.set((slot - 1)*4 + 1, circles.get(r2));
bitmaps.set((slot - 1)*4 + 2, circles.get(color));
bitmaps.set((slot - 1)*4 + 3, circles.get(r3));
} else {
bitmaps.set((slot - 1)*4,circles.get(r1));
bitmaps.set((slot - 1)*4 + 1,circles.get(r2));
bitmaps.set((slot - 1)*4 + 2,circles.get(r3));
bitmaps.set((slot - 1)*4 + 3,circles.get(color));
}
if(slot == 1) {
p1 = position;
centerX1 = x+position*(10 + width) + width/2;
centerY1 = y1 + width/2;
}
else if(slot == 2) {
p2 = position;
centerX2 = x+position*(10 + width) + width/2;
centerY2 = y2 + width/2;
}
}
public void onTouch(float X, float Y) {
int radius = (gameView.getWidth() / 4 - 10) / 2;
if(endGameCount == 0) {
if ((X >= centerX1 - radius) && (X <= centerX1 + radius) && (Y >= centerY1 - radius) && (Y <= centerY1 + radius)) {
GameView.score++;
touchedFirst = true;
centerX1 = centerY1 = -1;
if(p1 == (timerCount - 1) && timer != null && starSlot == 1) {
GameView.stars++;
starCollected = true;
timerCount = 0;
stopTimerTask(0);
}
} else if ((X >= centerX2 - radius) && (X <= centerX2 + radius) && (Y >= centerY2 - radius) && (Y <= centerY2 + radius)) {
GameView.score++;
touchedSecond = true;
centerX2 = centerY2 = -1;
if(p2 == (timerCount - 1) && timer != null && starSlot == 2) {
GameView.stars++;
starCollected = true;
timerCount = 0;
stopTimerTask(0);
}
} else {
endSlot = 0;
if ((Y >= centerY1 - radius) && (Y <= centerY1 + radius)) {
endSlot = 1;
if (X >= 10 && X <= 10 + 2 * radius) {
p1 = 0;
centerX1 = 10 + radius;
} else if (X >= 20 + 2 * radius && X <= 20 + 4 * radius) {
p1 = 1;
centerX1 = 20 + 3 * radius;
} else if (X >= 30 + 4 * radius && X <= 30 + 6 * radius) {
p1 = 2;
centerX1 = 30 + 5 * radius;
} else if (X >= 40 + 6 * radius && X <= 40 + 8 * radius) {
p1 = 3;
centerX1 = 40 + 2 * radius;
} else
endSlot = 0;
} else if ((Y >= centerY2 - radius) && (Y <= centerY2 + radius)) {
endSlot = 2;
if (X >= 10 && X <= 10 + 2 * radius) {
p2 = 0;
centerX2 = 10 + radius;
} else if (X >= 20 + 2 * radius && X <= 20 + 4 * radius) {
p2 = 1;
centerX2 = 20 + 3 * radius;
} else if (X >= 30 + 4 * radius && X <= 30 + 6 * radius) {
p2 = 2;
centerX2 = 30 + 5 * radius;
} else if (X >= 40 + 6 * radius && X <= 40 + 8 * radius) {
p2 = 3;
centerX2 = 40 + 2 * radius;
} else
endSlot = 0;
}
if (endSlot != 0) {
speedY1 = speedY2 = 0;
limit = endGameCount = 6;
if (endSlot == 1) {
endColor= bitmaps.get(p1).getPixel(width/2, width/2);
} else {
endColor = bitmaps.get(p2 + 4).getPixel(width/2, width/2);
}
}
}
if (GameView.score % 5 == 0 && GameView.score <= 110 && barCounter == 1) {
float ratio = (float)GameActivity.SCREEN_HEIGHT/1280;
speedY1 += ratio*0.5;
speedY2 += ratio*0.5;
}
if (GameView.score > 0 && GameView.score % 15 == 14) {
if(isOddScore)
stopY1 = true;
else
stopY2 = true;
}
if (GameView.score > 0 && GameView.score % 15 == 0 && barCounter == 1) {
if(isOddScore)
stopY2 = true;
else
stopY1 = true;
}
if (GameView.score % 15 == 1)
barCounter = 1;
}
}
public void draw(Canvas canvas) throws GameOverException {
GameView.isGameOver = false;
if(exception)
throw new GameOverException(color);
update();
for(int i=0;i<bitmaps.size();i++) {
if(i<4) {
Rect rect = new Rect(x+i*(10 + width),y1,(x+width)*(i+1),y1+width);
if(endGameCount == Math.ceil(limit) && endSlot == 1) {
if(i == p1) {
endRadiusCount += 1;
if (endRadiusCount > 23) {
star.recycle();
loopCount = loopCount%starInterval;
Cryptography.saveFile((loopCount + "").getBytes(), context, "interval");
endGameCount = 0;
exception = true;
throw new GameOverException(color);
}
rect = new Rect(x + i * (10 + width) - endRadiusCount*(int)Math.ceil(endRadiusSpeed), y1 - endRadiusCount*(int)Math.ceil(endRadiusSpeed), (x + width) * (i + 1) + endRadiusCount*(int)Math.ceil(endRadiusSpeed), y1 + width + endRadiusCount*(int)Math.ceil(endRadiusSpeed));
canvas.drawBitmap(bitmaps.get(i), null, rect, null);
}
}
// TOUCH ANIMATION : DIMINISH CIRCLE
else if(i==p1 && touchedFirst) {
rect = new Rect(x + i * (10 + width) + 3*count1 + ((int)speedY1-15), y1 + 3*count1 + ((int)speedY1-15), (x + width) * (i + 1) - 3*count1 - ((int)speedY1-15), y1 + width - 3*count1 - ((int)speedY1-15));
canvas.drawBitmap(bitmaps.get(i), null, rect, null);
count1++;
}
else if(endSlot != 2) {
canvas.drawBitmap(bitmaps.get(i), null, rect, null);
if(timerCount > 0 && starSlot == 1) {
int size = width * 30 / 50;
int difference = (width - size) / 2;
Rect starRect = new Rect(x + (timerCount - 1) * (10 + width) + difference, y1 + difference, (x + width) * (timerCount) - difference, y1 + width - difference);
canvas.drawBitmap(star, null, starRect, null);
}
}
}
if(i >= 4) {
Rect rect = new Rect(x + (i % 4) * (10 + width), y2, (x + width) * ((i % 4) + 1), y2 + width);
if(endGameCount == Math.ceil(limit) && endSlot == 2) {
if((i%4)==p2) {
endRadiusCount += 1;
if (endRadiusCount > 23) {
star.recycle();
loopCount = loopCount%starInterval;
Cryptography.saveFile((loopCount + "").getBytes(), context, "interval");
endGameCount = 0;
exception = true;
throw new GameOverException(color);
}
rect = new Rect(x + (i % 4) * (10 + width) - endRadiusCount*(int)Math.ceil(endRadiusSpeed), y2 - endRadiusCount*(int)Math.ceil(endRadiusSpeed), (x + width) * ((i % 4) + 1) + endRadiusCount*(int)Math.ceil(endRadiusSpeed), y2 + width + endRadiusCount*(int)Math.ceil(endRadiusSpeed));
canvas.drawBitmap(bitmaps.get(i), null, rect, null);
}
}
else if((i%4)==p2 && touchedSecond) {
rect = new Rect(x + (i % 4) * (10 + width) + 3*count2 + ((int)speedY1-15), y2 + 3*count2 + ((int)speedY1-15), (x + width) * ((i % 4) + 1) - 3*count2 - ((int)speedY1-15), y2 + width - 3*count2 - ((int)speedY1-15));
canvas.drawBitmap(bitmaps.get(i), null, rect, null);
count2++;
}
else if(endSlot != 1) {
canvas.drawBitmap(bitmaps.get(i), null, rect, null);
if(timerCount > 0 && starSlot == 2) {
int size = width * 30 / 50;
int difference = (width - size) / 2;
Rect starRect = new Rect(x + (timerCount - 1) * (10 + width) + difference, y2 + difference, (x + width) * (timerCount) - difference, y2 + width - difference);
canvas.drawBitmap(star, null, starRect, null);
}
}
}
}
Rect src = new Rect(circles.get(color).getWidth()/2 - 10,circles.get(color).getHeight()/2 - 10,circles.get(color).getWidth()/2 + 10,circles.get(color).getHeight()/2 + 10);
Rect dst;
Paint paint = new Paint();
paint.setColor(Color.WHITE);
paint.setTextAlign(Paint.Align.RIGHT);
paint.setTypeface(Typeface.SANS_SERIF);
paint.setTextSize(72 * ratio);
canvas.drawText(GameView.score + " ", GameActivity.SCREEN_WIDTH, width / 2, paint);
dst = new Rect(5,5, (int) (120 * ratio - 5), (int) (120 * ratio - 5));
canvas.drawBitmap(star,null,dst,null);
paint.setTextAlign(Paint.Align.LEFT);
canvas.drawText("" + GameView.stars, 120 * ratio, width/2, paint);
}
}
Don't override draw(). That's used to render the View, not the Surface, and you generally shouldn't override that method even if you're creating a custom View:
When implementing a view, implement onDraw(android.graphics.Canvas) instead of overriding this method.
SurfaceViews have two parts, the Surface and the View. The View part is handled like any other View, but is generally just a transparent "hole" in the layout. The Surface is a separate layer that, by default, sits behind the View layer. Whatever you draw on the Surface "shows through" the transparent hole.
By overriding draw() you're drawing on the View whenever the View UI is invalidated. You're also calling draw() from the render thread, so you're drawing on the Surface, but with default Z-ordering you can't see that because the View contents are fully opaque. You will reduce your impact on the UI thread by not drawing everything in two different layers.
Unless you're deliberately drawing on the View, it's best to avoid subclassing SurfaceView entirely, and just use it as a member.
Because your draw code is synchronized, the two draw passes will not execute concurrently. That means your View layer draw call will block waiting for the Surface layer rendering to complete. Canvas rendering on a Surface is not hardware-accelerated, so if you're touching a lot of pixels it can get slow, and the UI thread will have to wait for it to run. That wouldn't be so bad, but you're holding on to the mutex while you're sleeping, which means the only opportunity for the main UI thread to run comes during the brief instant when the loop wraps around. The thread scheduler does not guarantee fairness, so it's entirely possible to starve the main UI thread this way.
If you change #override draw() to myDraw() things should get better. You should probably move your sleep call out of the synchronized block just on general principles, or work to eliminate it entirely. You might also want to consider using a custom View instead of SurfaceView.
On an unrelated note, you should probably avoid doing this every update:
Random random = new Random();
for the reasons noted here.
Successfully solved the issue. Can't imagine that the solution would be this much simple as compared to the problem that I was considering that complex. Just reduced the frame rate from 120 to 90 and guess what, it worked like charm!
Due to a high frame rate, the SurfaceView was busy doing all the drawing and onTouchEvent() method had to starve
I am attempting to build a RecyclerView list, with an alphabetical indexer down the right hand side. I have the list displaying perfectly and am able to scroll.
I am wondering if someone could give me some guidance in building the alphabetical indexer. I believe I need to iterate through my list and determine the letters used (I can do this). Then build the list of letters (for the indexer) and then use scrollToPosition(int position) to move to the right item in the list when a letter is selected.
The main issue that I can not work out is not building another list, it is rather the communication between the two lists. Any help would be greatly appreciated.
I believe this is the information you have been looking for as I have
You can check these libraries here:
danoz73/RecyclerViewFastScroller
as far as I remember he is the one who actually made the first alphabet indexer in recyclerview. I never made it work in my app because there was lots of problem customizing it.
AndroidDeveloperLB/LollipopContactsRecyclerViewFastScroller
the readme said hes got lots of optimizations made, So i think this is the best one i could use, but i havent made it work in my app for now.
code-computerlove/FastScrollRecyclerView
I think this is the design you want right? So either of these libs you used it should suffice your needs.
hope it helps
If anyone would like to use the FastScrollRecyclerView mentioned above in Xamarin, I just ported the main classes (you'll still need to create the xml resource and prep your index etc etc - see the original repo for a guide, it's pretty simple).
I included the tweaks suggested in the issues page to improve the ScrollToPosition and index height (which also makes it work in landscape) and also switched out the Java Handler class for an async Task.Delay. It may still have issues, it's a straight port otherwise. Seems to be ok though. Hopefully I have saved someone some time :)
public interface IFastScrollRecyclerViewAdapter
{
Dictionary<string, int> GetMapIndex();
}
//
public class FastScrollRecyclerView : RecyclerView
{
public const int INDWIDTH = 25;
public const int INDHEIGHT = 18;
public float ScaledWidth { get; set; }
public float ScaledHeight { get; set; }
public string[] Sections { get; set; }
public float Sx { get; set; }
public float Sy { get; set; }
public string Section { get; set; }
public bool ShowLetter { get; set; }
private ListHandler _listHandler;
private bool _setupThings = false;
private Context _context;
public FastScrollRecyclerView(Context context) : base(context)
{
_context = context;
}
public FastScrollRecyclerView(Context context, IAttributeSet attrs) : base(context, attrs)
{
_context = context;
}
public FastScrollRecyclerView(Context context, IAttributeSet attrs, int defStyle) : base(context, attrs, defStyle)
{
_context = context;
}
public override void OnDraw(Canvas c)
{
if (!_setupThings && GetAdapter() != null)
SetupThings();
base.OnDraw(c);
}
private void SetupThings()
{
//create az text data
var sectionSet = ((IFastScrollRecyclerViewAdapter)GetAdapter()).GetMapIndex().Keys;
var listSection = new List<string>(sectionSet);
listSection.Sort();
Sections = new string[listSection.Count];
int i = 0;
foreach (var s in listSection)
{
Sections[i++] = s;
}
ScaledWidth = INDWIDTH * _context.Resources.DisplayMetrics.Density;
var divisor = sectionSet.Count == 0 ? 1 : sectionSet.Count;
ScaledHeight = Height / divisor ;// INDHEIGHT * _context.Resources.DisplayMetrics.Density;
Sx = Width - PaddingRight - (float)(1.2 * ScaledWidth);
Sy = (float)((Height - (ScaledHeight * Sections.Length)) / 2.0);
_setupThings = true;
}
public override bool OnTouchEvent(MotionEvent motionEvent)
{
if (_setupThings)
{
var adapter = GetAdapter() as IFastScrollRecyclerViewAdapter;
var x = motionEvent.GetX();
var y = motionEvent.GetY();
switch (motionEvent.Action)
{
case MotionEventActions.Down:
{
if (x < Sx - ScaledWidth || y < Sy || y > (Sy + ScaledHeight * Sections.Length))
{
return base.OnTouchEvent(motionEvent);
}
else
{
//// We touched the index bar
float yy = y - PaddingTop - PaddingBottom - Sy;
int currentPosition = (int)Math.Floor(yy / ScaledHeight);
if (currentPosition < 0) currentPosition = 0;
if (currentPosition >= Sections.Length) currentPosition = Sections.Length - 1;
Section = Sections[currentPosition];
ShowLetter = true;
int positionInData = 0;
if (adapter.GetMapIndex().ContainsKey(Section.ToUpper()))
{
positionInData = adapter.GetMapIndex()[Section.ToUpper()];
}
(GetLayoutManager() as LinearLayoutManager).ScrollToPositionWithOffset(positionInData, 20);
Invalidate();
}
break;
}
case MotionEventActions.Move:
{
if (!ShowLetter && (x < Sx - ScaledWidth || y < Sy || y > (Sy + ScaledHeight * Sections.Length)))
{
return base.OnTouchEvent(motionEvent);
}
else
{
float yy = y - Sy;
int currentPosition = (int)Math.Floor(yy / ScaledHeight);
if (currentPosition < 0) currentPosition = 0;
if (currentPosition >= Sections.Length) currentPosition = Sections.Length - 1;
Section = Sections[currentPosition];
ShowLetter = true;
int positionInData = 0;
if (adapter.GetMapIndex().ContainsKey(Section.ToUpper()))
positionInData = adapter.GetMapIndex()[Section.ToUpper()];
(GetLayoutManager() as LinearLayoutManager).ScrollToPositionWithOffset(positionInData, 20);
Invalidate();
}
break;
}
case MotionEventActions.Up:
{
_listHandler = new ListHandler(this);
_listHandler.DelayClear();
if (x < Sx - ScaledWidth || y < Sy || y > (Sy + ScaledHeight * Sections.Length))
{
return base.OnTouchEvent(motionEvent);
}
else
{
return true;
}
}
}
}
return true;
}
private class ListHandler
{
FastScrollRecyclerView _parent;
public ListHandler (FastScrollRecyclerView parent)
{
_parent = parent;
}
public async void DelayClear()
{
await Task.Delay(100);
_parent.ShowLetter = false;
_parent.Invalidate();
}
}
}
//
public class FastScrollRecyclerViewItemDecoration : ItemDecoration
{
private Context _context;
public FastScrollRecyclerViewItemDecoration(Context context)
{
_context = context;
}
public override void OnDrawOver(Canvas canvas, RecyclerView parent, State state)
{
base.OnDrawOver(canvas, parent, state);
float scaledWidth = ((FastScrollRecyclerView)parent).ScaledWidth;
float sx = ((FastScrollRecyclerView)parent).Sx;
float scaledHeight = ((FastScrollRecyclerView)parent).ScaledHeight;
float sy = ((FastScrollRecyclerView)parent).Sy;
string[] sections = ((FastScrollRecyclerView)parent).Sections;
string section = ((FastScrollRecyclerView)parent).Section;
bool showLetter = ((FastScrollRecyclerView)parent).ShowLetter;
// We draw the letter in the middle
if (showLetter & section != null && !section.Equals(""))
{
//overlay everything when displaying selected index Letter in the middle
Paint overlayDark = new Paint();
overlayDark.Color = Color.Black;
overlayDark.Alpha = 100;
canvas.DrawRect(0, 0, parent.Width, parent.Height, overlayDark);
float middleTextSize = _context.Resources.GetDimension(Resource.Dimension.fast_scroll_overlay_text_size );
Paint middleLetter = new Paint();
middleLetter.Color = new Color(ContextCompat.GetColor(_context, Resource.Color.primary));
middleLetter.TextSize = middleTextSize;
middleLetter.AntiAlias = true;
middleLetter.FakeBoldText = true;
middleLetter.SetStyle(Paint.Style.Fill);
int xPos = (canvas.Width - (int)middleTextSize) / 2;
int yPos = (int)((canvas.Height / 2) - ((middleLetter.Descent() + middleLetter.Ascent()) / 2));
canvas.DrawText(section.ToUpper(), xPos, yPos, middleLetter);
}
// // draw indez A-Z
Paint textPaint = new Paint();
textPaint.AntiAlias = true;
textPaint.SetStyle(Paint.Style.Fill);
for (int i = 0; i < sections.Length; i++)
{
if (showLetter & section != null && !section.Equals("") && section != null
&& sections[i].ToUpper().Equals(section.ToUpper()))
{
textPaint.Color = Color.White;
textPaint.Alpha = 255;
textPaint.FakeBoldText = true;
textPaint.TextSize = scaledWidth / 2;
canvas.DrawText(sections[i].ToUpper(),
sx + textPaint.TextSize / 2, sy + parent.PaddingTop
+ scaledHeight * (i + 1), textPaint);
textPaint.TextSize = scaledWidth;
canvas.DrawText("•",
sx - textPaint.TextSize / 3, sy + parent.PaddingTop
+ scaledHeight * (i + 1) + scaledHeight / 3, textPaint);
}
else
{
textPaint.Color = new Color(ContextCompat.GetColor(_context, Resource.Color.primary));
textPaint.Alpha = 200;
textPaint.FakeBoldText = false;
textPaint.TextSize = scaledWidth / 2;
canvas.DrawText(sections[i].ToUpper(),
sx + textPaint.TextSize / 2, sy + parent.PaddingTop
+ scaledHeight * (i + 1), textPaint);
}
}
}
}
I want to detect motion with Android sensors. For example I hold only bottom of phone and move top of phone to up. I think I need sampling algorithms. I can write a simple application to record data of sensors . For comparing real time data and recorded data ,Is there any libary ? I have suspicious about performace problems if I would make it. Is there a different path for detetion movements ?
These links will help you to start..
http://developer.android.com/guide/topics/sensors/sensors_motion.html
http://code.google.com/p/android-motion-detection/
http://www.helloandroid.com/tutorials/android-image-processing-detecting-motions
http://code.google.com/p/android-motion-detection/ is a good example.
I modified the isDifferent method in RgbMotionDetection class to detect the motion in the center part (25%) of the camera view.
protected static boolean isDifferent(int[] first, int width, int height) {
if (first==null) throw new NullPointerException();
if (mPrevious==null) return false;
if (first.length != mPrevious.length) return true;
if (mPreviousWidth != width || mPreviousHeight != height) return true;
int totDifferentPixels = 0;
int size = height * width;
int startHeight = height / 4;
int endHeight = 3 * (height / 4);
int startWidth = width / 4;
int endWidth = 3 * (width / 4);
int offSet = width / 4;
Log.d("params", "start height " + startHeight + "end height " + endHeight + "start width " + startWidth + "end width " + endWidth);
Boolean offSetApplied;
for (int i = startHeight, ij=0; i < endHeight; i++) {
{
offSetApplied = false;
for (int j = startWidth; j < endWidth; j++, ij++) {
if (!offSetApplied){
offSetApplied = true;
ij = startHeight * width + offSet;
}
int pix = (0xff & ((int)first[ij]));
int otherPix = (0xff & ((int)mPrevious[ij]));
//Catch any pixels that are out of range
if (pix < 0) pix = 0;
if (pix > 255) pix = 255;
if (otherPix < 0) otherPix = 0;
if (otherPix > 255) otherPix = 255;
if (Math.abs(pix - otherPix) >= mPixelThreshold) {
totDifferentPixels++;
//Paint different pixel red
//first[ij] = Color.RED;
}
}
}
}
if (totDifferentPixels <= 0) totDifferentPixels = 1;
//boolean different = totDifferentPixels > mThreshold;
int percent = 100/(size/totDifferentPixels);
//float percent = (float) totDifferentPixels / (float) size;
boolean different = percent > SENSITIVITY;
String output = "Number of different pixels: " + totDifferentPixels + "> " + percent + "%";
if (different) {
Log.e(TAG, output);
} else {
Log.d(TAG, output);
}
return different;
}
I have been developing a board game using hexagon boards in android, but when I move the board, the whole 'map' or dozens of entities starts to lag. I believe that it is the cause of using a for-loop, but I have no idea how to fix it. Im using the method of threaded drawing, dont know if it affects though.
I can clearly see lags on my cell phone when moving through the board.
My Game class:
public class Game extends SurfaceView implements SurfaceHolder.Callback {
// Game Vars
int mapWidth = 150, mapHeight = 150;
public static double hexagonSideLength = 80;
public static double cellWidth = 2 * hexagonSideLength, cellHeight = Math
.sqrt(3) * hexagonSideLength;
public static double downwardShift = (0.5) * (cellHeight);
public static double rightShift = hexagonSideLength / 2;
public static int boundaryWidth = 0, boundaryHeight = 0;
public static int error = (int) ((hexagonSideLength) * (0.06));
public static int buffer = 2;
public static int draws = 0;
// Touch Handle
// Offset to the upper left corner of the map
private int xOffset = 0;
private int yOffset = 0;
// last touch point
private int _xTouch = 0;
private int _yTouch = 0;
// scrolling active?
private boolean _isMoving = false;
public Cell[][] map;
private GameThread thread;
static String TAG = Game.class.getSimpleName();
Bitmap image[] = new Bitmap[100];
Paint paint;
public Game(Context context) {
super(context);
Log.i(TAG, "Loaded Game");
// adding the callback (this) to the surface holder to intercept events
getHolder().addCallback(this);
// make the GamePanel focusable so it can handle events
thread = new GameThread(getHolder(), this);
map = new Cell[mapWidth][mapHeight]; // Create new Map
boolean isShiftedDownwards = false;
for (int i = 0; i < mapWidth; i++) {
for (int j = 0; j < mapHeight; j++) {
map[i][j] = new Cell(j, i, isShiftedDownwards);
}
if (isShiftedDownwards == true)
isShiftedDownwards = false;
else
isShiftedDownwards = true;
}
if (mapWidth % 2 != 0) {
boundaryWidth = (int) ((((mapWidth - 1) / 2) * hexagonSideLength)
+ ((mapWidth / 2) * cellWidth) + (2) * hexagonSideLength);
} else {
boundaryWidth = (int) (((((mapWidth - 1) / 2) * hexagonSideLength) + ((mapWidth / 2) * cellWidth)) + (1.5) * hexagonSideLength);
}
boundaryHeight = (int) (mapHeight * cellHeight + 0.5 * cellHeight);
setFocusable(true);
image[0] = Bitmap.createBitmap(BitmapFactory.decodeResource(
this.getResources(), R.drawable.hexagonrgb));
image[1] = Bitmap.createScaledBitmap(image[0], (int) cellWidth + error,
(int) cellHeight + error, false);
Log.i(TAG, "Got Resources");
paint = new Paint();
Log.i(TAG, "Prepared paint");
}
// called every Frame
#Override
protected void onDraw(Canvas canvas) {
Log.i(TAG, "onDraw Called");
// BG
canvas.drawColor(Color.BLACK);
// Resize
// Redraw Map
draws = 0;
for (int column = updateArea(0); column < updateArea(1); column++) {
for (int row = updateArea(2); row < updateArea(3); row++) {
canvas.drawBitmap(image[1], map[column][row].x - xOffset,
map[column][row].y - yOffset, paint);
paint.setColor(Color.WHITE);
canvas.drawText(row + "," + column, map[column][row].x
- xOffset + 80, map[column][row].y - yOffset + 80,
paint);
draws++;
}
}
/** Log */
paint.setColor(Color.WHITE);
paint.setTextSize(20);
canvas.drawText("xOffset: " + xOffset, 0, 30, paint);
canvas.drawText("yOffset: " + yOffset, 0, 50, paint);
canvas.drawText("activeTitlesX: " + updateArea(0) + " - "
+ updateArea(1), 0, 70, paint);
canvas.drawText("activeTitlesY: " + updateArea(2) + " - "
+ updateArea(3), 0, 90, paint);
canvas.drawText("DimX: " + MainActivity.DimX, 0, 110, paint);
canvas.drawText("DimY: " + MainActivity.DimY, 0, 130, paint);
canvas.drawText("Draws: " + draws, 0, 150, paint);
Log.i(TAG, "Cleared canvas");
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
// called by thread
public static void update() {
Log.i(TAG, "Updated Game");
}
#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();
Log.i(TAG, "Thread Started");
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
// try again shutting down the thread
}
}
Log.i(TAG, "Thread Destroyed");
}
#Override
public boolean onTouchEvent(MotionEvent event) {
// touch down
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// start of a new event, reset the flag
_isMoving = false;
// store the current touch coordinates for scroll calculation
_xTouch = (int) event.getX();
_yTouch = (int) event.getY();
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
// touch starts moving, set the flag
_isMoving = true;
// get the new offset
xOffset += _xTouch - (int) event.getX();
yOffset += _yTouch - (int) event.getY();
// secure, that the offset is never out of view bounds
if (xOffset < 0) {
xOffset = 0;
} else if (xOffset > boundaryWidth - getWidth()) {
xOffset = boundaryWidth - getWidth();
}
if (yOffset < 0) {
yOffset = 0;
} else if (yOffset > boundaryHeight - getHeight()) {
yOffset = boundaryHeight - getHeight();
}
// store the last position
_xTouch = (int) event.getX();
_yTouch = (int) event.getY();
} else if (event.getAction() == MotionEvent.ACTION_UP) {
/*
* // touch released if (!_isMoving) { // calculate the touched cell
* int column = (int) Math.ceil((_xOffset + event.getX()) /
* _cellSize) - 1; int row = (int) Math.ceil((_yOffset +
* event.getY()) / _cellSize) - 1; Cell cell =
* _mapCells.get(row).get(column); // show the id of the touched
* cell Toast.makeText(getContext(), "Cell id #" + cell._id,
* Toast.LENGTH_SHORT).show(); }
*/
}
return true;
}
public int updateArea(int i) {
switch (i) {
case 0: // Left
return Math.max(
(int) (xOffset / (cellWidth - rightShift)) - buffer, 0);
case 1: // Right
return Math
.min((int) (xOffset / (cellWidth - rightShift) + (int) (MainActivity.DimX / (cellWidth - rightShift)))
+ buffer, mapWidth);
case 2: // Up
return Math.max((int) (yOffset / cellHeight) - buffer, 0);
case 3: // Down
return Math.min(((int) (yOffset / cellHeight))
+ (int) (MainActivity.DimY / cellHeight) + buffer,
mapHeight);
}
return 0;
}
/* CELL CLASS */
class Cell {
int x, y;
boolean isShiftedDown;
public Cell(int row, int col, boolean isShifted) {
if (col % 2 != 0) {
isShiftedDown = true;
y = (int) (row * Game.cellHeight + Game.downwardShift);
x = (int) (col * Game.cellWidth - col * Game.rightShift);
Log.i("Shift", "Shifted" + Game.downwardShift);
} else {
isShiftedDown = false;
y = (int) (row * Game.cellHeight);
x = (int) (col * Game.cellWidth - col * Game.rightShift);
Log.i("Shift", "Not Shifted");
}
}
}
}
Thanks in Advance :)
Basic algorithmic analysis shows you are doing this operation:
map[i][j] = new Cell(j, i, isShiftedDownwards);
22500 times. Do you really need to create a new object each time, or can you just change a variable in an existing object?
Update:
for (int column = updateArea(0); column < updateArea(1); column++) {
for (int row = updateArea(2); row < updateArea(3); row++) {
canvas.drawBitmap(image[1], map[column][row].x - xOffset,
map[column][row].y - yOffset, paint);
paint.setColor(Color.WHITE);
canvas.drawText(row + "," + column, map[column][row].x
- xOffset + 80, map[column][row].y - yOffset + 80,
paint);
draws++;
}
}
So this is the double for-loop in question. I'm going to run with the assumption that this needs to be run as many times as it runs. Can the function calls in the loop statements be reduced to variables prior to the loop (they are evaluated with each loop)? This paint.setColor(Color.WHITE); should not be called on each iteration. If you need to change to white after the very first draw, just make a second paint object on startup and and change it before you enter the loop.
If this doesn't help, you need to determine if you can iterate over a smaller area.
Update 2
Rewrite your for loops like so
int minCol = updateArea(0);
int maxCol = updateArea(1);
int minRow = updateArea(2);
int maxRow = updateArea(3);
for(int column = minCol; column < maxCol; column++)
for (int row = minRow; column < maxRow; row++)
....
This will represent a significant reduction in the operations in each iteration.