ShowCaseView Incorrectly rendering - android

I am using showCaseView legacy(which has the animation of the hand) in my android app. But, the gesture doesnt seem to start 'relative' to the view. Instead, it seems to be absolute to the screen. This is the following which i am using:
final ShowcaseView sv;
ShowcaseView.ConfigOptions co = new ShowcaseView.ConfigOptions();
co.hideOnClickOutside = false;
co.block=true;
sv = ShowcaseView.insertShowcaseView(R.id.pen, this," R.string.showcase_title"," R.string.showcase_message", co);
View v=(View)findViewById(R.id.pen);
sv.animateGesture(0, 0, 0, -500, false);
This is the top of my emulator, the animation STARTS from here:
This shows a hand at the top left corner of the screen. (my guess is that the view's locations are being returned 0.
What is the problem ?

Your best bet is probably going to be to use DisplayMetrics to place the hand, that's what I've done and it seems to work well.
DisplayMetrics displaymetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
int height = displaymetrics.heightPixels;
int width = displaymetrics.widthPixels;
float offsetEndY = height / 2 + 50;
float offsetEndX = -width;
float offsetStartY = height / 2 + 50;
float offsetStartX = -width / 2;
if (sv == null) {
sv = ShowcaseView.insertShowcaseView(width, 0, activity, "Menu", "Swipe to access more options", mOptions)
.setTextColors(Color.BLUE, Color.BLACK);
sv.animateGesture(offsetStartX, offsetStartY, offsetEndX, offsetEndY);

I had the same problem and I from logging showcase positions at different stages I found that proper position is strangely calculated some (very short) time after show() is called.
When I was calling
showcaseView.show();
showcaseView.animateGesture(0, 0, 0, -400);
this DIDN'T work.
Easy solution is show hand gesture with some delay (200 ms was enough in my case - tested on many devices):
showcaseView.show();
showcaseView.postDelayed(new Runnable() {
#Override
public void run() {
showcaseView.animateGesture(0, 0, 0, -400);
}
}, 200);

Related

Use x/y coordinates to set bounds of a moving particle

I have two bitmaps that I draw onto the center of a canvas:
One is only a background, it's a spirit level in top view which doesnt move. The second one is a bitmap looking like a "air bubble". When the user tilts the phone, the sensors read the tilt and the air bubble moves according to the sensor values along the x-axis. However, I need to make sure that the air bubble doesnt move too far, e.g out of the background-bitmap.
So I tried to which x coordinate the bubble can travel to,
before I have to set xPos = xPos -1 using trial and error
This works fine on my device.
To clarify: On my phone, the air bubble could move to the coordinate x = 50 from the middle of the screen. This would be the point, where the bitmap is at the very left of the background spirit level.
On a larger phone, the position x = 50 is too far left, and therefore looking like the air bubble travelled out of the water level.
Now I've tried following:
I calculated the area in % in which the air bubble can move. Let's say that
is 70% of the entire width of the bitmap. So I tried to calculate the two x boundary values:
leftBoundary = XmiddlePoint - (backgroundBitmap.getWidth() * 0.35);
rightBoundary = XmiddlePoint + (backgroundBitmap.getWidth() * 0.35);
...which doesnt work when testing with different screen sizes :(
Is it possible to compensate for different screen sizes and densities using absolute coordinates or do I have to rethink my idea?
If you need any information that I forgot about, please let me know. If this question has already been answered, I would appreciate a link :) Thanks in advance!
Edit:
I load my bitmaps like this:
private Bitmap backgroundBitmap;
private static final int BITMAP_WIDTH = 1898;
private static final int BITMAP_HEIGHT = 438;
public class SimulationView extends View implements SensorEventListener{
public SimulationView(Context context){
Bitmap map = BitmapFactory.decodeResource(getResources, R.mipmap.backgroundImage);
backgroundBitmap = Bitmap.createScaledBitmap(map, BITMAP_WIDTH, BITMAP_HEIGHT, true;
}
and draw it like this:
protected void onDraw(Canvas canvas){
canvas.drawBitmap(backgroundBitmap, XmiddlePoint - BITMAP_WIDTH / 2, YmiddlePont - BITMAP_HEIGHT / 2, null);
}
backgroundBitmap.getWidth() and getHeight() prints out the correct sizes.
Calculating like mentioned above would return following boundaries:
DisplayMetrics displayMetrics = new DisplayMetrics();
((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int width = displayMetrics.widthPixels;
//which prints out width = 2392
xMiddlePoint = width / 2;
// = 1196
leftBoundary = xMiddlePoint - (BITMAP.getWidth()* 0.35);
// = 531,7
However, when I use trial and error, the right x coordinate seems to be at around 700.
I've come across a great explanation on how to fix my issue here.
As user AgentKnopf explained, you have to scale coordinates or bitmaps like this:
X = (targetScreenWidth / defaultScreenWidth) * defaultXCoordinate
Y = (targetScreenHeight / defaultScreenHeight) * defaultYCoordinate
which, in my case, translates to:
int defaultScreenWidth = 1920;
int defaultXCoordinate = 333;
DisplayMetrics displayMetrics = new DisplayMetrics();
((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
displayWidth = displayMetrics.widthPixels;
leftBoundary = (displayWidth / defaultScreenWidth) * defaultXCoordinates

Animate imageView from one side of screen to the other - android

I am trying to animate an ImageView using TranslateAnimation from it's current position (0,Yscreensize/2) to the other side of the screen (Xscreensize,imageview.getY()) but I can't manage it to work. here is my code:
dart = (ImageView) findViewById(R.id.dart);
Display disp = getWindowManager().getDefaultDisplay();
Point poi = new Point();
disp.getSize(poi);
sizex = poi.x;
sizey = poi.y;
ViewTreeObserver vto = dart.getViewTreeObserver();
vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
public boolean onPreDraw() {
dart.getViewTreeObserver().removeOnPreDrawListener(this);
finalHeight = dart.getMeasuredHeight();
dart.setY(sizey / 2 - finalHeight);
return true;
}
}); // till now - got screen size and set dart imageview to Y middle.
Now when I am trying to use the animation:
TranslateAnimation animation = new TranslateAnimation(dart.getX(), sizex, dart.getY(), dart.getY());
animation.setDuration(10000);
dart.startAnimation(animation); // from current x to screen size, from currenty to currenty.
this will not work and the dart just dissappear. what can I do?
Make the variable for the animation a class variable (to ensure it won't be garbage collected to soon). In order to make the ImageView stop its movement before it leaves the screen, you need an additional class variable
float finalWidth;
Then change your OnPreDrawListener like this:
vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener()
{
public boolean onPreDraw()
{
dart.getViewTreeObserver().removeOnPreDrawListener(this);
float finalHeight = dart.getMeasuredHeight();
// if you want the view to stop before it leaves the screen
float finalWidth = dart.getMeasuredWidth();
animation = new TranslateAnimation(0, sizex - finalWidth, 0, 0);
animation.setDuration(10000);
// use this if you want the changes to persist
// animation.setFillAfter(true);
dart.setY(sizey / 2 - finalHeight);
return true;
}
});
I used a button to call 'startAnimation()'. The only problem when testing with an emulator is that the ImageView keeps running until it reaches the emulator window egde. So it disappears below the hardware controls region on the right side. In order to make it stop earlier, I tested with
Rect myRect = new Rect();
dart.getWindowVisibleDisplayFrame(myRect);
sizex = myRect.width() * 0.9f;
sizey = myRect.height();
which almost did the trick. It's just an approximation, all right, but getting the exact dimensions of the display - taking into account paddings or insets - seems to be difficult. At least below API 20.
Hope this helps anyway :)

Android move ImageView to a specific pixel color

I made a treasure map in photoshop and I made a transparent image with coloured hotspots to put over the treasure map so I can make it clickable.
When I click on the colored dots (that are invisible), Android detects the color clicked and does the appropriate methods, just as asked.
Now I have an imageview, that would be my player, and each day I want it to move to another colored hotspot on the map (each hotspot represents a day of the week).
I have this code, but the position is way off:
private void moveToColor(ImageView iv, int toColor) {
Bitmap bm = BitmapFactory.decodeResource(getResources(),R.drawable.hotspots);
int width = bm.getWidth();
int height = bm.getHeight();
int[] pixels = new int[width * height];
bm.getPixels(pixels, 0, width, 0, 0, width, height);
for (int ix = 0; ix < width; ++ix) {
for (int iy = 0; iy < height; ++iy) {
if (toColor == bm.getPixel(ix, iy)) {
iv.animate().translationX((float)ix);
iv.animate().translationY((float)iy);
return;
}
}
}
}
sometimes it will move the imageview close to the toColor, and other times it is completely off or not even on the map.
Any pointers on how I could do this. I tried it with a buffer copypixelstobuffer, but I didn't understand very well how that works. Because above is quite slow..
Thanks!
Sure it is off, read the description of the translationX property carefully: http://developer.android.com/reference/android/view/View.html#attr_android:translationX
"This value is added post-layout to the left property of the view"
It's relative to the left of the view, i.e. it's displacement, not absolute position.
Instead, use
iv.animate().x((float)ix);
and it's twin brother .y()
I actually found that if I change
Bitmap bm = BitmapFactory.decodeResource(getResources(),R.drawable.hotspots);
int width = bm.getWidth();
int height = bm.getHeight();
to this:
Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.hotspots);
bm = Bitmap.createScaledBitmap(bm,size.x, size.y, true);
int width = bm.getWidth();
int height = bm.getHeight();
where size.x and size.y come from
display = getWindowManager().getDefaultDisplay();
size = new Point();
display.getSize(size);
Then the position is not that far off. It is still not completely where I want it, the player is positioned on the bottom of the dot, but at least it is a step closer.

Adding Endless parallax background in cocos2d android

I am working on CoCos2d with android.I want to add an endless scrolling background to my Screen by using CCParallaxNode.
I am able to add background and move it but after the completion of that move action the screen goes black.
Can someone help me out?
My code is
CCParallaxNode parallaxNode;
CCSprite spacedust1;
CCSprite spacedust2;
CCSprite planetsunrise;
CCSprite galaxy;
CCSprite spacialanomaly;
CCSprite spacialanomaly2;
parallaxNode = CCParallaxNode.node();
spacedust1 = CCSprite.sprite("bg_front_spacedust.png");
spacedust2 = CCSprite.sprite("bg_front_spacedust.png");
planetsunrise = CCSprite.sprite("bg_planetsunrise.png");
galaxy = CCSprite.sprite("bg_galaxy.png");
spacialanomaly = CCSprite.sprite("bg_spacialanomaly.png");
spacialanomaly2 = CCSprite.sprite("bg_spacialanomaly2.png");
// 3) Determine relative movement speeds for space dust and background
// CGPoint cgPoint = CGPoint.ccp(0.1, 0.1);
CGPoint dustSpeed = CGPoint.ccp(10, 10);
CGPoint bgSpeed = CGPoint.ccp(5, 5);
// CGPoint bgSpeed = ccp(0.05, 0.05);
parallaxNode.addChild(spacedust1, 0, dustSpeed.x, dustSpeed.y, 0,
winSize.height / 2);
parallaxNode.addChild(spacedust2, 0, dustSpeed.x, dustSpeed.y,
spacedust1.getContentSize().width, winSize.height / 2);
parallaxNode.addChild(galaxy, -1, bgSpeed.x, bgSpeed.y, 0, 10);
parallaxNode.addChild(planetsunrise, -1, bgSpeed.x, bgSpeed.y, 600, 5);
parallaxNode
.addChild(spacialanomaly, -1, bgSpeed.x, bgSpeed.y, 900, 20);
parallaxNode.addChild(spacialanomaly2, -1, bgSpeed.x, bgSpeed.y, 1500,
30);
CCIntervalAction go = CCMoveBy.action(4, CGPoint.ccp(winSize.width, 0));
CCIntervalAction goBack = go.reverse();
CCIntervalAction seq = CCSequence.actions(go, goBack);
CCRepeatForever action = CCRepeatForever.action(goBack);
parallaxNode.runAction(action);
I see that since not a single answer worked for you. I will provide a simple code which will help you for your parralax scrolling background.
Add this code in your game layers constructor
background1 = CCSprite.sprite("bg2.png");
background2 = CCSprite.sprite("bg2.png");
background1.setPosition(CGPoint.ccp(winSize.width*0.5f,winSize.height*0.5f));
addChild(background1);
background2.setPosition(CGPoint.ccp(winSize.width+winSize.width*0.5f,winSize.height*0.5f));
addChild(background2);
and a scroll method which is scheduled every millisecond.
add this in constructor
this.schedule("scroll");
and now the scroll method.
public void scroll(float dt) {
CGPoint pos1 = background1.getPosition();
CGPoint pos2 = background2.getPosition();
pos1.x -= 5.0f;
pos2.x -= 5.0f;
if(pos1.x <=-(winSize.width*0.5f) )
{
pos1.x = pos2.x + winSize.width;
}
if(pos2.x <=-(winSize.width*0.5f) )
{
pos2.x = pos1.x + winSize.width;
}
background1.setPosition(pos1);
background2.setPosition(pos2);
}
mark my answer if it worked.
Call this method from the class Constructor. I found this trick from Example : "shotingblock-master" available on github...
private void endlessBackground() {
// Create the two background sprites which will alternate
_oddBackground = CCSprite.sprite("blue_background.png");
_evenBackground = CCSprite.sprite("blue_background.png");
// One starts dead centre and one starts exactly one screen height above
oddBackground.setPosition(_winSize.width / 2, _winSize.height / 2);
evenBackground.setPosition(_winSize.width / 2, _winSize.height
+ (_winSize.height / 2));
// Schedule the scrolling action
schedule("scroll");
// Add sprites to the layer
addChild(_oddBackground).addChild(_evenBackground);
}
public void scroll(float dt) {
// move them 100*dt pixels down
_oddBackground.setPosition(_oddBackground.getPosition().x,
_oddBackground.getPosition().y - 150 * dt);
_evenBackground.setPosition(_evenBackground.getPosition().x,
_evenBackground.getPosition().y - 150 * dt);
// reset position when they are off from view.
if (_oddBackground.getPosition().y < -_winSize.height / 2) {
_oddBackground.setPosition(_winSize.width / 2, _winSize.height / 2);
_evenBackground.setPosition(_winSize.width / 2, _winSize.height
+ (_winSize.height / 2));
}
}
}
IT works excellent in my case. May be it'll help full for you.
try using this:
CCTexture2D *texture = CCTextureCache::sharedTextureCache()->addImage("pic.png");
ccTexParams params = {GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT};
texture->setTexParameters(&params);
CCSprite *sprite = CCSprite::spriteWithTexture(texture, CCRectMake(0, 0, 90, 90));
and make sure that The image's height and width must be power of 2.
It looks like the CCRepeatForever action is only running it goBack, which means that it's not reversing. Try the following:
CCIntervalAction go = CCMoveBy.action(4, CGPoint.ccp(winSize.width, 0));
CCIntervalAction goBack = go.reverse();
CCIntervalAction seq = CCSequence.actions(go, goBack);
CCRepeatForever action = CCRepeatForever.action(seq); // change to seq instead of goBack
parallaxNode.runAction(action);
This is trick for make it happen. You can Use the large png and working on it or check the sample test code which is available in coocs2d-android library
CCSprite background = CCSprite.sprite("background.png");
// create a void node, a parent node
CCParallaxNode voidNode = CCParallaxNode.node();
// background image is moved at a ratio of 0.4x, 0.5y
voidNode.addChild(background, -1, 0.4f, 0.5f, 0, 0);
// write your own code for the parallax node
CCIntervalAction goUp = CCMoveBy.action(4, CGPoint.make(0,-200));
CCIntervalAction goDown = goUp.reverse();
CCIntervalAction go = CCMoveBy.action(8, CGPoint.make(-1000, 0));
CCIntervalAction goBack = go.reverse();
CCIntervalAction seq = CCSequence.actions(goUp, go, goDown, goBack);
voidNode.runAction(CCRepeatForever.action(seq));
addChild(voidNode);
Please check out below link for Parallax vertical endless background:
http://kalpeshsantoki.blogspot.in/2014/07/create-vertical-endless-parallax.html
CGSize winSize = CCDirector.sharedDirector().displaySize();
//I made graphics for screen 720*1200....so I made this dynamic scale to support multiple screens
float sX = winSize.width / 720.0f;
float sY = winSize.height / 1200.0f;
background = CCVerticalParallaxNode.node(sX, sY, true);
background.addEntity(1f, "background.png", 0);
background.addEntity(3, "road_simple.png", winSize.width / 2);
background.addEntity(1.7f, "road_side.png", 0);
addChild(background);

Scale a view and its layered subviews relatively

(This is somewhat a follow-up on Android: How do you scale multiple views together?)
My task is to port an iPhone / iPad app on Android that consists of a simple image view on which animations are layered on top in absolute positions. While this sounds rather easy on iOS where you only have to target a few possible screen sizes, it gets rather messy with Android.
My current setup is this: A RelativeLayout in which I place my main (background) image on left = 0, top = 0 and multiple ViewFlipper instances used as "animation containers" that are positioned relatively to the upper left corner of the parent layout instance.
This approach has two basic problems:
The positioned "animations" are mis-positioned as soon as the actual size of the layout does not match the size of the main background image.
The positioned "animations" are also mis-sized, because since they usually have "enough space" around themselves, Android doesn't scale them to fit into the RelativeLayout (nor would it scale them relatively to the original background.
Since the animations itself must be interactive, its not a solution to place and position all of the animations on a transparent layer that has the same size as the main (background) image, as they'd overlap each other and only the upper-most would be interactive at all.
I thought of different solutions:
To get the the scale factor of the main image, I could retrieve its measuredWidth and measuredHeight and set this into relation of the original width and height of the view. Then I'd use this scale factor for custom positioning and eventually custom scaling. But, apparently the measuredWidth/-Height properties are only set during the onMeasure() call and this is called after the component tree was built, so I don't know if this solution is feasible at all.
Implement my own layout manager and scale / position the views accordingly. I had a look at the implementation of RelativeLayout, but have to admit that the onMeasure() method scares me a bit.
What would you do in my case? Is there anything I haven't yet taken into account?
Thanks in advance.
Well, answering my own question - here is the way I resolved the issue:
I placed the background image on the top of my ImageView with ImageView.setScaleType(ScaleType.FIT_START)
I calculated the scale factor of my background image like so:
WindowManager mgr = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics metrics = new DisplayMetrics();
mgr.getDefaultDisplay().getMetrics(metrics);
Drawable image = context.getResources().getDrawable(R.drawables.someImage);
float scale = metrics.widthPixels / (float) image.getIntrinsicWidth();
Finally, I used this scale in a custom ImageView class that loads the overlays to position and scale the view properly:
public class OverlayImage extends ImageView
{
private int imgWidth, imgHeight;
private final float scale;
public OverlayImage(Context context, int xPos, int yPos, float scale)
{
super(context);
this.scale = scale;
LayoutParams animParams = new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
animParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
animParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
animParams.leftMargin = (int) (scale * xPos);
animParams.topMargin = (int) (scale * yPos);
setLayoutParams(animParams);
Drawable dr = context.getResources().getDrawable(R.id.someImage);
setBackgroundDrawable(dr);
imgWidth = dr.getIntrinsicWidth();
imgHeight = dr.getIntrinsicHeight();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
setMeasuredDimension((int) (scale * imgWidth),
(int) (scale * imgHeight));
}
}
I lately needed to do something similar, i also had to port a IPad app to android, the screen had many images that had to be in specific locations.
I solved this slightly differently, absolute layout, and run through all the views and set the coordinated and size of each.
//This gets the scale of the screen change:
DisplayMetrics displaymetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
Drawable image = getResources().getDrawable(R.drawable.background_image);
float scaleW = displaymetrics.widthPixels / (float)image.getIntrinsicWidth();
float scaleH = displaymetrics.heightPixels / (float)image.getIntrinsicHeight();
//And this scales each view accordingly:
for(int i = 0; i < mainLayout.getChildCount(); i++)
{
View v = mainLayout.getChildAt(i);
v.setLayoutParams(new AbsoluteLayout.LayoutParams(
Math.round(scaleW * v.getMeasuredWidth()),
Math.round(scaleH * v.getMeasuredHeight()),
Math.round(scaleW * ((AbsoluteLayout.LayoutParams)v.getLayoutParams()).x),
Math.round(scaleH * ((AbsoluteLayout.LayoutParams)v.getLayoutParams()).y)));
}

Categories

Resources