I have an image e.g. the image shown below:
This image has two parts part 1 of size width W and height L and part 2 (smaller part) of width w and height h (call part one as source and part 2 as destination). Let the coordinates of the 1st rectangle be (measured from top left corner): top:100 left: 10 right: 200 bottom: 300 and the coordinates of the 2nd rectangle be (as measured from top left corner): top 50 left: 500 bottom: 100 right: 700
I want to animate from source to destination such that the image translates and zooms in from source to destination.
So my first screen would look like:
and my second image would look like:
How do I tween between these two ?
My code (without animation) looks like as follows:
public class SuperGame extends View implements OnGestureListener
{
int sreenHeight, screenWidth;
int drawCount = 0;
Bitmap bg;
public SuperGame(Context context, Bitmap bmp)
{
this.screenWidth = getContext().getResources().getDisplayMetrics().widthPixels;
this.screenHeight = getContext().getResources().getDisplayMetrics().heightPixels;
bg = BitmapFactory.decodeResource(getResources(),R.drawable.bg_img);
}
#Override
/* this function triggers the image change
*/
public boolean onDown(MotionEvent e) {
invalidate(0,0,screenWidth, screenHeight);
return true;
}
#Override
public void onDraw(Canvas canvas)
{
Rect dest = new Rect(0,0,screenWidth, screenHeight);
if (count==0) //draw the first image part
{
count=1;
Rect src = new Rect(100,10,200,300);//coordinates of rectangle 1
canvas.drawBitmap(bg, src, dest, new Paint());
}
else //draw the second image part - (I'd like to show movement/ transition between these)
{
Rect src = new Rect(50,500,100,700);//coordinates of rectangle 2
count=0;
canvas.drawBitmap(bg, src, dest, new Paint());
}
}
}
How to I animate the transition (which involves zoomin as well as translation)?
You might need to create a custom valueAnimator of type Rect/RectF.
Code example given below:
public class RectFEvaluator
implements TypeEvaluator<RectF>
{
#Override
public RectF evaluate(float fraction, RectF srcRect, RectF destRect) {
RectF betweenRect = new RectF();
betweenRect.bottom = srcRect.bottom + fraction*(destRect.bottom-srcRect.bottom);
betweenRect.top = srcRect.top + fraction*(destRect.top-srcRect.top);
betweenRect.left = srcRect.left + fraction*(destRect.left-srcRect.left);
betweenRect.right = srcRect.right + fraction*(destRect.right-srcRect.right);
return betweenRect;
}
}
The value animator will give you the intermediate values between src Rect and dest Rect.
Once you have these values, updated them using an animation
Related
I'm trying to add a custom Drawable which extends Drawable to my actionBar. This needs to be a custom drawable, since I want to draw on top of a supplied icon (but that's not the issue).
I've added the icon to the menu in my activity like so:
BadgedIconDrawable drawable = new BadgedIconDrawable(getContext())
.setIcon(icon);
mMenu.add(0, menuItemId, 0, "")
.setIcon(drawable)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
In the BadgedIconDrawable I take a bitmap for the Icon which I then draw on the canvas:
#Override
public void draw(#NonNull Canvas canvas) {
canvas.drawBitmap(mIcon.getBitmap(), null, new Rect(0, 0, mWidth, mHeight), mIconPaint);
}
Where the width and height are 24dp, which seems to be the right size for the icon.
The problem is that unlike a regular drawable that's passed to the setIcon for the mMenu, it doesn't seem to align correctly. I can't find how to get it aligned to the center. Image below illustrates the issue. The middle Icon is set through the BadgedIconDrawable.
EDIT
While the below snippet worked, I soon figured this would only work for the toolbar. Not anymore when used in a regular ImageView. So the trick is to use a Rect as to where the bitmap should be drawn. This would have the regular bounds, but when it should be translated, tell the custom Drawable to do so, by modifying the Rect like so:
// On the fragment/activity create the Drawable, translate it and add to the menu.
public void addBadgedActionBarButton(Drawable icon, int color, String badgeLabel, int menuItemId) {
if (mMenu == null) {
throw new IllegalStateException("mMenu = null, Are you sure you are calling addActionBarButton from initMenu()?");
} else {
BadgedIconDrawable drawable = new BadgedIconDrawable(getContext());
drawable.translate(-drawable.getWidth() / 2, -drawable.getHeight() / 2, drawable.getWidth() / 2, drawable.getHeight() / 2)
.setIcon(icon);
mMenu.add(Menu.NONE, menuItemId, Menu.NONE, "")
.setIcon(drawable)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
}
}
// In the BadgedIconDrawable have a function that sets the mDstRect to the translated Rect.
public BadgedIconDrawable translate(int left, int top, int right, int bottom) {
mDstRect = new Rect(left, top, right, bottom);
return this;
}
// In the BadgedIconDrawable draw function use the mDstRect to draw on the correct position.
#Override
public void draw(#NonNull Canvas canvas) {
canvas.drawBitmap(mIcon.getBitmap(), null, mDstRect, mIconPaint);
}
With some wild guessing I managed to try to add a negative top and left, which seems to place it in the correct spot. This seems to do the trick.
#Override
public void draw(#NonNull Canvas canvas) {
int halfWidth = mWidth / 2;
int halfHeight = mHeight / 2;
canvas.drawBitmap(mIcon.getBitmap(), null, new Rect(-halfWidth, -halfHeight, halfWidth, halfHeight), mIconPaint);
}
I have an ImageView and I am trying to fade from one image to the next using this code:
Drawable bgs[] = new Drawable[2];
public void redraw(int[][] grid) {
bgs[0] = bgs[1];
bgs[1] = new GameDrawable(grid, prefs.colors);
if (bgs[0] == null) {
gameField.setImageDrawable(bgs[1]);
} else {
TransitionDrawable crossfader = new TransitionDrawable(bgs);
crossfader.setCrossFadeEnabled(true);
gameField.setImageDrawable(crossfader);
crossfader.startTransition(500);
}
}
gameField is correctly referenced as an ImageView.
gameDrawable simply extends Drawable and draws the grid.
On each move and action the new GameDrawable is being rendered correctly but there is no fading whatsoever. The new image is simply displayed instantaneously. I have tried lengthening the transition time and swapping the order of the drawables with no effect.
Any help on is appreciated.
Update: I have now set my transition to something ridiculously long like 500000. The first drawable shows for a few seconds and then suddenly the second drawable appears. So still no transition.
Update 2:
I think my Drawable might be implemented incorrectly, so I have attached the code.
public class GameDrawable extends Drawable {
private Paint paint = new Paint();
private float blockWidth = 1;
private int[][] myGrid;
private int myColor;
private List<Point> myPoints;
public GameDrawable(int[][] grid) {
super();
this.myGrid = grid;
this.myColor = colors[yourColor];
paint.setStrokeWidth(1);
paint.setStyle(Paint.Style.FILL);
paint.setAlpha(0);
this.myPoints = yourPoints;
}
#Override
public void draw(Canvas canvas) {
float height = getBounds().height();
float width = getBounds().width();
blockWidth = width / myGrid.length;
if (height / myGrid.length < blockWidth) {
blockWidth = height / myGrid.length;
}
for (int x = 0; x < myGrid.length; x++) {
for (int y = 0; y < myGrid[x].length; y++) {
paint.setColor(colors[myGrid[x][y]]);
canvas.drawRect(x * blockWidth, y * blockWidth, (x+1)*blockWidth, (y+1)*blockWidth, paint);
}
}
}
#Override
public void setAlpha(int alpha) {
paint.setAlpha(alpha);
invalidateSelf();
}
#Override
public void setColorFilter(ColorFilter cf) {
paint.setColorFilter(cf);
invalidateSelf();
}
#Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
}
Looking at your code, I see a problem at the line
bgs[0] = bgs[1];
bgs[1] has not yet been defined before this line and so bgs[0] is null for the first method call. Because of this, (bgs[0] == null) is true, and so the later defined bgs[1] is directly set to the gameField ImageView.
Use corrected code below.
Drawable bgs[] = new Drawable[2];
Drawable firstDrawable = getResources().getDrawable(R.drawable.transparent);
public void redraw(int[][] grid) {
bgs[0] = firstDrawable;
bgs[1] = new GameDrawable(grid, prefs.colors);
firstDrawable = bgs[1];
TransitionDrawable crossfader = new TransitionDrawable(bgs);
crossfader.setCrossFadeEnabled(true);
gameField.setImageDrawable(crossfader);
crossfader.startTransition(500);
}
Note that TransitionDrawable does not work properly when the Drawable sizes are different. So you may need to resize firstDrawable beforehand.
EXTRA: I would avoid setCrossFadeEnabled(true) since the whole TransitionDrawable becomes translucent during the transition, revealing the background. Sometimes, this creates a "blinking" effect and destroys the smoothness of the transition.
EDIT: Looking at your custom Drawable implementation, I think the problem lies in the line
canvas.drawColor(Color.WHITE);
in the draw() method.
I looked at TransitionDrawable.java source and found that setAlpha is called on the drawables to get the cross fade effect. However, your canvas has a solid white color and setAlpha() only affects the paint. Hope this is your answer.
EDIT 2: The actual problem, as pointed out by Michael, was that TransitionDrawable's setAlpha() calls on the Drawables were rendered ineffective due to paint.setColor() in the GameDrawable's draw() method overriding the paint's alpha value set by the TransitionDrawable.
Im extending the View class in order to have a "drawable" view witch contains the desired background.
The problem is when this class works fine in other activity (same app).
Here is the class that extends View:
public class BackgroundMap extends View {
private String strmap;
private int totalWidth;
public BackgroundMap(Context context, String map, int w, int h) {
super(context);
super.setLayoutParams(new LayoutParams(w, h));
totalWidth = w;
strmap = map;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Resources res = getResources();
int eachBoxSize = totalWidth/10;
float left = 0;
float top = 0;
Paint paint = new Paint();
for(char c : strmap.toCharArray()){
Bitmap bm = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(res, Terrain.getResource(c)), eachBoxSize, eachBoxSize, false);
canvas.drawBitmap(bm, left, top, paint);
if(left == totalWidth - eachBoxSize){
left = 0;
top += eachBoxSize;
}else{
left += eachBoxSize;
}
}
}
}
This view works like this: somelayout.addView(new BackgroundMap(arg..));
the String map argument means an array of terrains (char) ids that point to several little png images.
and this is how i use it in my Activity:
ly_rbtn_image = (FrameLayout) findViewById(R.id.ly_rbtn_image);
ly_rbtn_image.post(new Runnable() {
#Override
public void run() {
imagewidth = ly_rbtn_image.getWidth();
ly_rbtn_image.addView(new BackgroundMap(getApplicationContext(), str, imagewidth, imagewidth));
}
});
i check with logcat the width of parent layout and it has 225px. As you can see the image below only build the first row of bitmaps... but this IS NOT a loop problem
I check inside the custom view loop at onDraw method if there was a problem here but it iterate the whole string (100 chars, 10x10 bitmaps)
hope u can help me :S
http://deviantsart.com/1afcam8.png
solved. please delete me.
the bitmaps were pushed at right till the end of the loop. So in fact, i didnt know why this happened.
UNTIL I CHECKED THE BOUNDS OF THE RESULT VIEW IN LOGCAT x))) the view had 2000 pixels width and 20 height... there we go:
When i get the total width of the device, casually was a number divisible by 10 (10x10 grid) so in fact was lucky not having this problem before (building the same map in other view).
Then, the problem was the need to print the same map (or grid) in an other view, not big as the device width, so the total width (parent width) was not divisible by the number of columns of the grid (im ashamed x)).
I have created an app that actually uses flood fill algorithm to fill colors in bitmaps. I have created some bitmaps (200x200) but I don't know the exact size of bitmap that I should create, I want bitmaps to cover full screen and when I scale bitmaps, they become blur and flood fill doesn't work on them. I saw an app that used GridView to show images and click on image started new activity with image covering full screen. How can I achieve this. Attached is CustomImage that I've use to show bitmap. Any help will be appreciated.
EDITED: I know that GridView doesn't scale up image to full screen, it use another image. That app behaves same for different screen size, those image fill screen of any size without effecting the quality.
public class CustomImage extends View {
public CustomImage(Context context) {
super(context);
} // end constructor
public CustomImage(Context context, int resource_id) {
super(context);
mPaint = new Paint();
mPaint.setColor(Color.WHITE);
mPoint = new Point();
mProgressIndicator = (ProgressIndicator) context;
mBitmap = BitmapFactory.decodeResource(getResources(), resource_id)
.copy(Bitmap.Config.ARGB_8888, true);
} // end constructor
#Override
protected void onDraw(Canvas canvas) {
// draw image
canvas.drawBitmap(mBitmap, 0, 0, mPaint);
} // end onDraw
#Override
public boolean onTouchEvent(MotionEvent event) {
int bWidth = mBitmap.getWidth();
int bHeight = mBitmap.getHeight();
mPoint.x = (int) event.getX();
mPoint.y = (int) event.getY();
mPoint.x = (mPoint.x > bWidth) ? (bWidth - 5) : mPoint.x;
mPoint.y = (mPoint.y > bHeight) ? (bHeight - 5) : mPoint.y;
switch (event.getAction()) {
// called when screen clicked i.e New touch started
case MotionEvent.ACTION_DOWN:
mProgressIndicator.updateProgress(0);
new Filler(mBitmap, mPoint, mPaint.getColor(), mBitmap.getPixel(
mPoint.x, mPoint.y), this).execute();
invalidate();
} // end Case
return true;
} // end onTouchEvent
public void setColor(int color) {
mPaint.setColor(color);
} // end setColor
} // end Class
The GridView you have seen might be using two versions of the same image. One as a big image and another as a scaled down thumbnail image for the grid.
I have a sprite that is supposed to act like a loadbar. I have tried this by using an example image that has been created like a 9patch-type (http://cdn.dibbus.com/wp-content/uploads/2011/03/btn_black.9.png). It seems okay in the start, but as the width of the sprite increases the sprite starts to look pixeled. Anyone know what the problem could be, or have any solution? The code is shown below.
public Sprite loaded;
public void init()
{
atlas = new TextureAtlas(Gdx.files.
internal("data/misc/menu_button.pack"));
loaded = atlas.createSprite("loadbar");
loaded.setPosition((Misc.WIDTH/2) - unloaded.getWidth()/2,
Misc.HEIGTH - unloaded.getHeight());
}
public void draw_load_bar() //render function
{
if(loaded.getWidth() < 600)
{
loaded.setSize(loaded.getWidth()+ 0.5f, loaded.getHeight());
}
loaded.draw(batch);
}
Dont use a Sprite to stretch it. I'd recommend a real Ninepatch from libgdx for it.
public NinePatch loaded;
private float posX, posY, width, height;
public void init()
{
loaded = new NinePatch(the Texture here,10, 10, 10, 10); //bounds outside
//set right pos here...
}
public void draw_load_bar(SpriteBatch batch) //render function
{
if(loaded.getWidth() < 600)
{
//update the size of it here (guess pos is static)
width++;
}
//need to parse the batch and the right sizes.
loaded.draw(batch, posx, posy, width, height);
}
after that you can handle it like a Sprite or Texture but it does stratch right without issues. If you want to the full Picture to be Stretched simply do net set any bounds at the creation new NinePatch(texture, 0,0,0,0)