I am developing a simple app that produced bubbles on screen on touch. Bubble move around on the screen and get popped when it reaches the border of screen or if a user touches it. I have successfully coded a bubble to pop when reaches borders of the screen but can't figure out a way to detect if the user touched it.
I want to detect if the user touched any bubble on the screen.
Note:- The bubbles are created using custom view. Also I have included some important functions only but can include whole code if you want. Here's the code
public class BubbleActivity extends Activity {
// These variables are for testing purposes, do not modify
private final static int RANDOM = 0;
private final static int SINGLE = 1;
private final static int STILL = 2;
private static int speedMode = RANDOM;
private static final int MENU_STILL = Menu.FIRST;
private static final int MENU_SINGLE_SPEED = Menu.FIRST + 1;
private static final int MENU_RANDOM_SPEED = Menu.FIRST + 2;
private static final String TAG = "Lab-Graphics";
// Main view
private RelativeLayout mFrame;
// Bubble image
private Bitmap mBitmap;
// Display dimensions
private int mDisplayWidth, mDisplayHeight;
// Sound variables
// AudioManager
private AudioManager mAudioManager;
// SoundPool
private SoundPool mSoundPool;
// ID for the bubble popping sound
private int mSoundID;
// Audio volume
private float mStreamVolume;
// Gesture Detector
private GestureDetector mGestureDetector;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Set up user interface
mFrame = (RelativeLayout) findViewById(R.id.frame);
// Load basic bubble Bitmap
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.b64);
}
#Override
protected void onResume() {
super.onResume();
// Manage bubble popping sound
}
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
// Get the size of the display so this view knows where borders are
mDisplayWidth = mFrame.getWidth();
mDisplayHeight = mFrame.getHeight();
}
}
// Set up GestureDetector
private void setupGestureDetector() {
mGestureDetector = new GestureDetector(this,
new GestureDetector.SimpleOnGestureListener() {
// Detecting if user touched bubble here
#Override
public boolean onSingleTapConfirmed(MotionEvent event) {
// Trying to get bubble position but can't just get x=0, y=0 tried
// many things
Log.d(TAG,""+((ViewGroup)mFrame).getChildCount());
for(int i=0; i<((ViewGroup)mFrame).getChildCount(); ++i) {
View nextChild = ((ViewGroup)mFrame).getChildAt(i);
Rect rect = new Rect();
nextChild.getLocalVisibleRect(rect);
int[] location = new int[2];
nextChild.getLocationOnScreen(location);
Log.d(TAG, "X = " + location[0] + " Y = " + location[1]);
}
if(event.getAction() == MotionEvent.ACTION_DOWN){
BubbleView bubbleView = new BubbleView(getApplicationContext(), event.getX(),event.getY());
bubbleView.start();
mFrame.addView(bubbleView);
}
return true;
}
});
}
#Override
public boolean onTouchEvent(MotionEvent event) {
// TODO - delegate the touch to the gestureDetector
return mGestureDetector.onTouchEvent(event);
}
#Override
protected void onPause() {
// TODO - Release all SoundPool resources
super.onPause();
}
// BubbleView is a View that displays a bubble.
// This class handles animating, drawing, popping amongst other actions.
// A new BubbleView is created for each bubble on the display
private class BubbleView extends View {
private static final int BITMAP_SIZE = 64;
private static final int REFRESH_RATE = 40;
private final Paint mPainter = new Paint();
private ScheduledFuture<?> mMoverFuture;
private int mScaledBitmapWidth;
private Bitmap mScaledBitmap;
// location, speed and direction of the bubble
private float mXPos, mYPos, mDx, mDy;
private long mRotate, mDRotate;
public BubbleView(Context context, float x, float y) {
super(context);
log("Creating Bubble at: x:" + x + " y:" + y);
// Create a new random number generator to
// randomize size, rotation, speed and direction
Random r = new Random();
// Creates the bubble bitmap for this BubbleView
createScaledBitmap(r);
// Adjust position to center the bubble under user's finger
mXPos = x - mScaledBitmapWidth / 2;
mYPos = y - mScaledBitmapWidth / 2;
// Set the BubbleView's speed and direction
setSpeedAndDirection(r);
// Set the BubbleView's rotation
setRotation(r);
mPainter.setAntiAlias(true);
}
// Start moving the BubbleView & updating the display
private void start() {
// Creates a WorkerThread
ScheduledExecutorService executor = Executors
.newScheduledThreadPool(1);
// Execute the run() in Worker Thread every REFRESH_RATE
// milliseconds
// Save reference to this job in mMoverFuture
mMoverFuture = executor.scheduleWithFixedDelay(new Runnable() {
#Override
public void run() {
// TODO - implement movement logic.
// Each time this method is run the BubbleView should
// move one step. If the BubbleView exits the display,
// stop the BubbleView's Worker Thread.
// Otherwise, request that the BubbleView be redrawn.
if(!isOutOfView()){
moveWhileOnScreen();
}
else{
stop(true);
}
}
}, 0, REFRESH_RATE, TimeUnit.MILLISECONDS);
}
private synchronized boolean intersects(float x, float y) {
// TODO - Return true if the BubbleView intersects position (x,y)
return false;
}
// Cancel the Bubble's movement
// Remove Bubble from mFrame
// Play pop sound if the BubbleView was popped
private void stop(final boolean popped) {
if (null != mMoverFuture && mMoverFuture.cancel(true)) {
// This work will be performed on the UI Thread
mFrame.post(new Runnable() {
#Override
public void run() {
// TODO - Remove the BubbleView from mFrame
if (popped) {
log("Pop!");
// TODO - If the bubble was popped by user,
// play the popping sound
mFrame.removeView(BubbleView.this);
//mMoverFuture.cancel(true);
mSoundPool.play(mSoundID, 1, 1, 1, 0, 1);
}
log("Bubble removed from view!");
}
});
}
}
// Change the Bubble's speed and direction
private synchronized void deflect(float velocityX, float velocityY) {
log("velocity X:" + velocityX + " velocity Y:" + velocityY);
//TODO - set mDx and mDy to be the new velocities divided by the REFRESH_RATE
mDx = velocityX/REFRESH_RATE;
mDy = velocityY/REFRESH_RATE;
}
// Draw the Bubble at its current location
#Override
protected synchronized void onDraw(Canvas canvas) {
// TODO - save the canvas
canvas.save();
// TODO - increase the rotation of the original image by mDRotate
mRotate = mRotate + mDRotate;
// TODO Rotate the canvas by current rotation
canvas.rotate(mRotate, mXPos + mScaledBitmapWidth/2, mYPos + mScaledBitmapWidth/2);
// TODO - draw the bitmap at it's new location
canvas.drawBitmap(mScaledBitmap, mXPos, mYPos,mPainter);
// TODO - restore the canvas
canvas.restore();
}
private synchronized boolean moveWhileOnScreen() {
// TODO - Move the BubbleView
// Returns true if the BubbleView has exited the screen
mXPos = mDx+mXPos;
mYPos = mDy+mYPos;
postInvalidate();
return false;
}
private boolean isOutOfView() {
// TODO - Return true if the BubbleView has exited the screen
if(mXPos + mScaledBitmapWidth/2 >= mDisplayWidth - mScaledBitmapWidth/2 || mXPos <0
||mYPos + mScaledBitmapWidth/2 >= mDisplayHeight - mScaledBitmapWidth/2 || mYPos <0){
return true;
}
return false;
}
}
Update :-
To clarify a bit, I want to get the location of all the bubbles on the screen and then compare them to event.getX() and event.getY() to detect if i tapped on any bubble. II have to check bubble tap in onSingleTapConfirmed(). I am correctly able to get the total number of bubbles but can't detect their location on the screen.
for(int i=0; i<((ViewGroup)mFrame).getChildCount(); ++i) {
View nextChild = ((ViewGroup)mFrame).getChildAt(i);
Rect rect = new Rect();
nextChild.getLocalVisibleRect(rect);
int[] location = new int[2];
nextChild.getLocationOnScreen(location);
Log.d(TAG, "X = " + location[0] + " Y = " + location[1]);
}
Above code gives the correct number of bubbles but return their coordinates as 0,0.
In your onSingleTapConfirmed function, try the following to iterate through your BubbleViews and pass the Event X and Y coordinates on.
for(int i=0;i<mFrame.getChildCount();i++){
BubbleView bubbleThis = (BubbleView) mFrame.getChildAt(i);
if (bubbleThis.intersects(event.getX(),event.getY())){
bubbleThis.stop(true);
return true;
}
}
The function in BubbleView should then return true if the X and Y fall inside its boundaries. I will add the function inside intersects function in BubbleView as clarification:
private synchronized boolean intersects(float x, float y) {
if ( (x>mXPos && x<(mXPos+mScaledBitmapWidth)) && (y>mYPos && y<(mYPos+mScaledBitmapWidth)) ) {
return true;
}
return false;
}
If you want to know if a user tapped a bubble, set its onClickListener. If you want to know if the user just touched it, override its onTouchEvent and look for ACTION_DOWN.
How are you implementing the onDown() method of your SimpleOnGestureListener?
Please take a look at these answers:
Gesture Detector not working
Android GestureDetector with SimpleOnGestureListener within SurfaceView
Detect which View was tapped in the onSingleTapConfirmed method
Bubble is circle in shape, so you just need to compare its radius with the distance between bubble center and the position.
mRadius = radius of the bubble
mDistance = distance between (event.getX(), event.getY()) and bubble center (mXPos + mRadius, mYPos + mRadius)
Related
I have a problem when I am running my android app.
The shake listener cannot be called when I touch down.
My logic is "Send message when I am shaking and touch down button at the same time. But it seems that cannot work for my listener part.
But the android studio cant tell me where is the error or code wrongly.
Here is my button code.
shakeitBtn.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
Toast.makeText(getBaseContext(), "Shake!", Toast.LENGTH_SHORT).show();
mShakeDetector = new ShakeDetector(new ShakeDetector.OnShakeListener() {
public void onShake() {
Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
v.vibrate(300);
status = RELEASE_TO_SEND;
}
});
break;
case MotionEvent.ACTION_UP:
if (status == RELEASE_TO_CANCEL) {
Toast.makeText(getBaseContext(), "Shake canceled", Toast.LENGTH_SHORT).show();
} else {
if (status == RELEASE_TO_SEND) {
DatabaseReference childRoot = rootRoomName.push();
Map<String, Object> map = new HashMap<String, Object>();
map.put("name", userName);
map.put("message", "I AM BUSY!!!".toString());
childRoot.updateChildren(map);
}
else{}
}
break;
case MotionEvent.ACTION_MOVE:
if (event.getY() < 0) {
status = RELEASE_TO_CANCEL;
} else {
}
break;
default:
break;
}
return true;
}
});
#Override
public void onResume() {
chat_room.super.onResume();
mSensorManager.registerListener(mShakeDetector, mAccelerometer, SensorManager.SENSOR_DELAY_UI);
}
#Override
public void onPause() {
mSensorManager.unregisterListener(mShakeDetector);
chat_room.super.onPause();
}
Here is my share detector code
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
public class ShakeDetector implements SensorEventListener {
// Minimum acceleration needed to count as a shake movement
private static final int MIN_SHAKE_ACCELERATION = 5;
// Minimum number of movements to register a shake
private static final int MIN_MOVEMENTS = 2;
// Maximum time (in milliseconds) for the whole shake to occur
private static final int MAX_SHAKE_DURATION = 500;
// Arrays to store gravity and linear acceleration values
private float[] mGravity = {0.0f, 0.0f, 0.0f};
private float[] mLinearAcceleration = {0.0f, 0.0f, 0.0f};
// Indexes for x, y, and z values
private static final int X = 0;
private static final int Y = 1;
private static final int Z = 2;
// OnShakeListener that will be notified when the shake is detected
private OnShakeListener mShakeListener;
// Start time for the shake detection
long startTime = 0;
// Counter for shake movements
int moveCount = 0;
// Constructor that sets the shake listener
public ShakeDetector(OnShakeListener shakeListener) {
mShakeListener = shakeListener;
}
#Override
public void onSensorChanged(SensorEvent event) {
// This method will be called when the accelerometer detects a change.
// Call a helper method that wraps code from the Android developer site
setCurrentAcceleration(event);
// Get the max linear acceleration in any direction
float maxLinearAcceleration = getMaxCurrentLinearAcceleration();
// Check if the acceleration is greater than our minimum threshold
if (maxLinearAcceleration > MIN_SHAKE_ACCELERATION) {
long now = System.currentTimeMillis();
// Set the startTime if it was reset to zero
if (startTime == 0) {
startTime = now;
}
long elapsedTime = now - startTime;
// Check if we're still in the shake window we defined
if (elapsedTime > MAX_SHAKE_DURATION) {
// Too much time has passed. Start over!
resetShakeDetection();
} else {
// Keep track of all the movements
moveCount++;
// Check if enough movements have been made to qualify as a shake
if (moveCount > MIN_MOVEMENTS) {
// It's a shake! Notify the listener.
mShakeListener.onShake();
// Reset for the next one!
resetShakeDetection();
}
}
}
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// Intentionally blank
}
private void setCurrentAcceleration(SensorEvent event) {
/*
* BEGIN SECTION from Android developer site. This code accounts for
* gravity using a high-pass filter
*/
// alpha is calculated as t / (t + dT)
// with t, the low-pass filter's time-constant
// and dT, the event delivery rate
final float alpha = 0.8f;
// Gravity components of x, y, and z acceleration
mGravity[X] = alpha * mGravity[X] + (1 - alpha) * event.values[X];
mGravity[Y] = alpha * mGravity[Y] + (1 - alpha) * event.values[Y];
mGravity[Z] = alpha * mGravity[Z] + (1 - alpha) * event.values[Z];
// Linear acceleration along the x, y, and z axes (gravity effects removed)
mLinearAcceleration[X] = event.values[X] - mGravity[X];
mLinearAcceleration[Y] = event.values[Y] - mGravity[Y];
mLinearAcceleration[Z] = event.values[Z] - mGravity[Z];
/*
* END SECTION from Android developer site
*/
}
private float getMaxCurrentLinearAcceleration() {
// Start by setting the value to the x value
float maxLinearAcceleration = mLinearAcceleration[X];
// Check if the y value is greater
if (mLinearAcceleration[Y] > maxLinearAcceleration) {
maxLinearAcceleration = mLinearAcceleration[Y];
}
// Check if the z value is greater
if (mLinearAcceleration[Z] > maxLinearAcceleration) {
maxLinearAcceleration = mLinearAcceleration[Z];
}
// Return the greatest value
return maxLinearAcceleration;
}
private void resetShakeDetection() {
startTime = 0;
moveCount = 0;
}
// (I'd normally put this definition in it's own .java file)
public interface OnShakeListener {
public void onShake();
}
}
As you said the ShakeDetector works if you start it before the onTouch() I suggest the following:
1) Create a parameter in your Activity/Fragment:
bool vibrateOnShake = false;
2) Init your ShakeDetector outside the onTouch():
mShakeDetector = new ShakeDetector(new ShakeDetector.OnShakeListener() {
public void onShake() {
if (vibrateOnShake) {
Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
v.vibrate(300);
status = RELEASE_TO_SEND;
}
}
});
3) Update your onTouch():
#Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
vibrateOnShake = true;
...
break;
...
}
}
I believe in your bright mind and strong android skills. I am little bit stuck.
I have the following situation. I've created app for learning how to work with Gestures and canvas.
Idea is simple when I single time tap on the screen and where I've tapped should appear bubble (R.drawable.bubble). If there is already some bubble application should delete it (clear space).
But, I have some difficulties with this. Place where I've tapped and where bubble actually appears have some significantly different in position.
Please give some advice where I should look. What I missed ?
Thanks in advance. Below I provide my code.
public class BubbleActivity extends Activity {
// Main view
RelativeLayout mFrame;
// Bubble image
private Bitmap mBitmap;
// gesture detector
GestureDetector mGestureDetector;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bubble);
// setup user interface
mFrame = (RelativeLayout) findViewById(R.id.frame);
// load basic bubble Bitmap
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.b128);
}
#Override
protected void onResume() {
super.onResume();
// init gesture detector
setupGestureDetector();
}
private void setupGestureDetector() {
mGestureDetector = new GestureDetector(this,
new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onSingleTapConfirmed(MotionEvent e) {
if(mFrame.getChildCount() == 0) {
BubbleView bubble = new BubbleView(getApplicationContext(),
e.getX(),
e.getY());
mFrame.addView(bubble);
} else {
for(int i=0; i < mFrame.getChildCount(); i++) {
BubbleView bubble = (BubbleView) mFrame.getChildAt(i);
if(bubble.intersect(e.getX(), e.getY())) {
mFrame.removeViewAt(i);
} else {
BubbleView newBubble = new BubbleView(getApplicationContext(),
e.getX(),
e.getY());
mFrame.addView(newBubble);
}
}
}
return true;
}
});
}
#Override
public boolean onTouchEvent(MotionEvent event) {
this.mGestureDetector.onTouchEvent(event);
return false;
}
private class BubbleView extends View {
private static final int BITMAP_SIZE = 64;
private float mXPos;
private float mYPos;
private Bitmap mScaledBitmap;
private int mScaledBitmapWidth;
public BubbleView(Context context, float x, float y) {
super(context);
mXPos = x;
mYPos = y;
Random r = new Random();
createScaledBitmap(r);
}
private void createScaledBitmap(Random r) {
mScaledBitmapWidth = (r.nextInt(3) + 1) * BITMAP_SIZE;
mScaledBitmap = Bitmap.createScaledBitmap(mBitmap,
mScaledBitmapWidth,
mScaledBitmapWidth,
false);
}
#Override
protected void onDraw(Canvas canvas) {
Paint mPaint = new Paint();
mPaint.setAntiAlias(true);
canvas.drawBitmap(mScaledBitmap,
this.mXPos,
this.mYPos,
mPaint);
}
public boolean intersect(float x, float y) {
if(Math.abs(this.mXPos - x) < mScaledBitmapWidth
|| Math.abs(this.mYPos - y) < mScaledBitmapWidth) {
return true;
} else {
return false;
}
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.bubble, menu);
return true;
}
}
One thing I have seen is that you should create the new BubbleView object outside your for loop.
I would use a boolean and if you don't find anyone inside the loop, then you could create one.
public boolean onSingleTapConfirmed(MotionEvent e) {
boolean found = false;
if(mFrame.getChildCount() == 0) {
BubbleView bubble = new BubbleView(getApplicationContext(),
e.getX(),
e.getY());
mFrame.addView(bubble);
} else {
for(int i=0; i < mFrame.getChildCount(); i++) {
BubbleView bubble = (BubbleView) mFrame.getChildAt(i);
if(bubble.intersect(e.getX(), e.getY())) {
mFrame.removeViewAt(i);
found = true;
break;
}
}
}
if (!found) {
BubbleView newBubble = new BubbleView(getApplicationContext(),
e.getX(),
e.getY());
mFrame.addView(newBubble);
}
It is part of the code of a course in coursera!
I would recommend you using other options to find the solution (for example by reviewing the videos and the example code gived)
Use following lines in BubbleView Constructor to set the position of your scaled bitmap.
// Adjust position to center the bubble under user's finger
mXPos = x - mScaledBitmapWidth / 2;
mYPos = y - mScaledBitmapWidth / 2;
This will center the bubble view bitmap under user's finger. Coursera's skeleton project already includes these line.
Also, you need one more correction in your code -
#Override
public boolean onTouchEvent(MotionEvent event) {
return this.mGestureDetector.onTouchEvent(event);
//return false;
}
I agree with Ignacio Rubio's suggestion to create the new bubble outside of the for loop however, in this particular scenario where the default value for found is false, the first click will create two bubbles: (1) to satisfy the condition of if(mFrame.getChildCount() == 0) and (2) if(!found)
To resolve this issue you can use an int in place of a boolean
public boolean onSingleTapConfirmed(MotionEvent event) {
int found = 0;
if(mFrame.getChildCount() == 0) {
Log.v(TAG, "Make First Bubble");
BubbleView bubble = new BubbleView(getApplicationContext(),
event.getX(),
event.getY());
mFrame.addView(bubble);
} else {
for(int i=0; i < mFrame.getChildCount(); i++) {
BubbleView bubble = (BubbleView) mFrame.getChildAt(i);
if(bubble.intersects(event.getX(), event.getY())) {
mFrame.removeViewAt(i);
found = 1;
break;
}else{
found = 2;
}
}
}
if (found == 2) {
BubbleView newBubble = new BubbleView(getApplicationContext(),
event.getX(),
event.getY());
mFrame.addView(newBubble);
}
return true;
}
Also, double check you View.intersect(). The touch will have to intersect the x AND y position of the bubble rather than the x OR y position.
Hi I am using the Gallery widget to show images downloaded from the internet.
to show several images and I would like to have a gradual zoom while people slide up and down on the screen. I know how to implement the touch event the only thing I don't know how to make the whole gallery view grow gradually. I don't want to zoom in on one image I want the whole gallery to zoom in/out gradually.
EDIT3: I manage to zoom the visible part of the gallery but the problem is I need to find a way for the gallery to find out about it and update it's other children too.
What happens is if 3 images are visible then you start zooming and the gallery does get smaller, so do the images but what I would like in this case is more images to be visible but I don't know how to reach this desired effect. Here's the entire code:
public class Gallery1 extends Activity implements OnTouchListener {
private static final String TAG = "GalleryTest";
private float zoom=0.0f;
// Remember some things for zooming
PointF start = new PointF();
PointF mid = new PointF();
Gallery g;
LinearLayout layout2;
private ImageAdapter ad;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.gallery_1);
layout2=(LinearLayout) findViewById(R.id.layout2);
// Reference the Gallery view
g = (Gallery) findViewById(R.id.gallery);
// Set the adapter to our custom adapter (below)
ad=new ImageAdapter(this);
g.setAdapter(ad);
layout2.setOnTouchListener(this);
}
public void zoomList(boolean increase) {
Log.i(TAG, "startig animation");
AnimatorSet set = new AnimatorSet();
set.playTogether(
ObjectAnimator.ofFloat(g, "scaleX", zoom),
ObjectAnimator.ofFloat(g, "scaleY", zoom)
);
set.addListener(new AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
// TODO Auto-generated method stub
}
#Override
public void onAnimationEnd(Animator animation) {
}
#Override
public void onAnimationCancel(Animator animation) {
// TODO Auto-generated method stub
}
});
set.setDuration(100).start();
}
public class ImageAdapter extends BaseAdapter {
private static final int ITEM_WIDTH = 136;
private static final int ITEM_HEIGHT = 88;
private final int mGalleryItemBackground;
private final Context mContext;
private final Integer[] mImageIds = {
R.drawable.gallery_photo_1,
R.drawable.gallery_photo_2,
R.drawable.gallery_photo_3,
R.drawable.gallery_photo_4,
R.drawable.gallery_photo_5,
R.drawable.gallery_photo_6,
R.drawable.gallery_photo_7,
R.drawable.gallery_photo_8
};
private final float mDensity;
public ImageAdapter(Context c) {
mContext = c;
// See res/values/attrs.xml for the <declare-styleable> that defines
// Gallery1.
TypedArray a = obtainStyledAttributes(R.styleable.Gallery1);
mGalleryItemBackground = a.getResourceId(
R.styleable.Gallery1_android_galleryItemBackground, 1);
a.recycle();
mDensity = c.getResources().getDisplayMetrics().density;
}
public int getCount() {
return mImageIds.length;
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView == null) {
convertView = new ImageView(mContext);
imageView = (ImageView) convertView;
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
imageView.setLayoutParams(new Gallery.LayoutParams(
(int) (ITEM_WIDTH * mDensity + 0.5f),
(int) (ITEM_HEIGHT * mDensity + 0.5f)));
} else {
imageView = (ImageView) convertView;
}
imageView.setImageResource(mImageIds[position]);
return imageView;
}
}
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_MOVE
&& event.getPointerCount() > 1) {
midPoint(mid, event);
if(mid.y > start.y){
Log.i(TAG, "Going down (Math.abs(mid.y - start.y)= "+(Math.abs(mid.y - start.y))+" and zoom="+zoom); // going down so increase
if ((Math.abs(mid.y - start.y) > 10) && (zoom<2.5f)){
zoom=zoom+0.1f;
midPoint(start, event);
zoomList(true);
}
return true;
}else if(mid.y < start.y){
Log.i(TAG, "Going up (Math.abs(mid.y - start.y)= "+(Math.abs(mid.y - start.y))+" and zoom="+zoom); //smaller
if ((Math.abs(mid.y - start.y) > 10) &&(zoom>0.1)){
midPoint(start, event);
zoom=zoom-0.1f;
zoomList(false);
}
return true;
}
}
else if (event.getAction() == MotionEvent.ACTION_POINTER_DOWN) {
Log.e(TAG, "Pointer went down: " + event.getPointerCount());
return true;
}
else if (event.getAction() == MotionEvent.ACTION_UP) {
Log.i(TAG, "Pointer going up");
return true;
}
else if (event.getAction() == MotionEvent.ACTION_DOWN) {
Log.i(TAG, "Pointer going down");
start.set(event.getX(), event.getY());
return true;
}
return false;
// indicate event was handled or not
}
private void midPoint(PointF point, MotionEvent event) {
float x = event.getX(0) + event.getX(1);
float y = event.getY(0) + event.getY(1);
point.set(x / 2, y / 2);
}
I realise I will probably have to extend the Gallery or even another View group or create my own class but I don't know where to start: which method use the one responsible for scaling...
EDIT4: I don't know if he question is clear enough. Here is an example of states:
State one: initial state, we have 3 images in view
State 2: we detect vertical touches going up with 2 fingers = we have to zoom out
state 3: we start zooming = animation on the gallery or on the children???
state 4: gallery detects that it's 3 children are smaller
state 5: gallery adds 1 /more children according to the new available space
LAST UPDATE:
Thanks to all that have posted but I have finally reached a conclusion and that is to not use Gallery at all:
1. It's deprecated
2. It's not customizable enough for my case
If you want to animate several images at once you may want to consider using OpenGl, I am using libgdx library:
https://github.com/libgdx/libgdx
The following ScalingGallery implementation might be of help.
This gallery subclass overrides the getChildStaticTransformation(View child, Transformation t) method in which the scaling is performed. You can further customize the scaling parameters to fit your own needs.
Please note the ScalingGalleryItemLayout.java class. This is necessary because after you have performed the scaling operationg on the child views, their hit boxes are no longer valid so they must be updated from with the getChildStaticTransformation(View child, Transformation t) method.
This is done by wrapping each gallery item in a ScalingGalleryItemLayout which extends a LinearLayout. Again, you can customize this to fit your own needs if a LinearLayout does not meet your needs for layout out your gallery items.
File : /src/com/example/ScalingGallery.java
/**
* A Customized Gallery component which alters the size and position of its items based on their position in the Gallery.
*/
public class ScalingGallery extends Gallery {
public static final int ITEM_SPACING = -20;
private static final float SIZE_SCALE_MULTIPLIER = 0.25f;
private static final float ALPHA_SCALE_MULTIPLIER = 0.5f;
private static final float X_OFFSET = 20.0f;
/**
* Implemented by child view to adjust the boundaries after it has been matrix transformed.
*/
public interface SetHitRectInterface {
public void setHitRect(RectF newRect);
}
/**
* #param context
* Context that this Gallery will be used in.
* #param attrs
* Attributes for this Gallery (via either xml or in-code)
*/
public ScalingGallery(Context context, AttributeSet attrs) {
super(context, attrs);
setStaticTransformationsEnabled(true);
setChildrenDrawingOrderEnabled(true);
}
/**
* {#inheritDoc}
*
* #see #setStaticTransformationsEnabled(boolean)
*
* This is where the scaling happens.
*/
protected boolean getChildStaticTransformation(View child, Transformation t) {
child.invalidate();
t.clear();
t.setTransformationType(Transformation.TYPE_BOTH);
// Position of the child in the Gallery (... +2 +1 0 -1 -2 ... 0 being the middle)
final int childPosition = getSelectedItemPosition() - getPositionForView(child);
final int childPositionAbs = (int) Math.abs(childPosition);
final float left = child.getLeft();
final float top = child.getTop();
final float right = child.getRight();
final float bottom = child.getBottom();
Matrix matrix = t.getMatrix();
RectF modifiedHitBox = new RectF();
// Change alpha, scale and translate non-middle child views.
if (childPosition != 0) {
final int height = child.getMeasuredHeight();
final int width = child.getMeasuredWidth();
// Scale the size.
float scaledSize = 1.0f - (childPositionAbs * SIZE_SCALE_MULTIPLIER);
if (scaledSize < 0) {
scaledSize = 0;
}
matrix.setScale(scaledSize, scaledSize);
float moveX = 0;
float moveY = 0;
// Moving from right to left -- linear move since the scaling is done with respect to top-left corner of the view.
if (childPosition < 0) {
moveX = ((childPositionAbs - 1) * SIZE_SCALE_MULTIPLIER * width) + X_OFFSET;
moveX *= -1;
} else { // Moving from left to right -- sum of the previous positions' x displacements.
// X(n) = X(0) + X(1) + X(2) + ... + X(n-1)
for (int i = childPositionAbs; i > 0; i--) {
moveX += (i * SIZE_SCALE_MULTIPLIER * width);
}
moveX += X_OFFSET;
}
// Moving down y-axis is linear.
moveY = ((childPositionAbs * SIZE_SCALE_MULTIPLIER * height) / 2);
matrix.postTranslate(moveX, moveY);
// Scale alpha value.
final float alpha = (1.0f / childPositionAbs) * ALPHA_SCALE_MULTIPLIER;
t.setAlpha(alpha);
// Calculate new hit box. Since we moved the child, the hitbox is no longer lined up with the new child position.
final float newLeft = left + moveX;
final float newTop = top + moveY;
final float newRight = newLeft + (width * scaledSize);
final float newBottom = newTop + (height * scaledSize);
modifiedHitBox = new RectF(newLeft, newTop, newRight, newBottom);
} else {
modifiedHitBox = new RectF(left, top, right, bottom);
}
// update child hit box so you can tap within the child's boundary
((SetHitRectInterface) child).setHitRect(modifiedHitBox);
return true;
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// Helps to smooth out jittering during scrolling.
// read more - http://www.unwesen.de/2011/04/17/android-jittery-scrolling-gallery/
final int viewsOnScreen = getLastVisiblePosition() - getFirstVisiblePosition();
if (viewsOnScreen <= 0) {
super.onLayout(changed, l, t, r, b);
}
}
private int mLastDrawnPosition;
#Override
protected int getChildDrawingOrder(int childCount, int i) {
//Reset the last position variable every time we are starting a new drawing loop
if (i == 0) {
mLastDrawnPosition = 0;
}
final int centerPosition = getSelectedItemPosition() - getFirstVisiblePosition();
if (i == childCount - 1) {
return centerPosition;
} else if (i >= centerPosition) {
mLastDrawnPosition++;
return childCount - mLastDrawnPosition;
} else {
return i;
}
}
}
File : /src/com/example/ScalingGalleryItemLayout.java
public class ScalingGalleryItemLayout extends LinearLayout implements SetHitRectInterface {
public ScalingGalleryItemLayout(Context context) {
super(context);
}
public ScalingGalleryItemLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ScalingGalleryItemLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
private Rect mTransformedRect;
#Override
public void setHitRect(RectF newRect) {
if (newRect == null) {
return;
}
if (mTransformedRect == null) {
mTransformedRect = new Rect();
}
newRect.round(mTransformedRect);
}
#Override
public void getHitRect(Rect outRect) {
if (mTransformedRect == null) {
super.getHitRect(outRect);
} else {
outRect.set(mTransformedRect);
}
}
}
File : /res/layout/ScaledGalleryItemLayout.xml
<?xml version="1.0" encoding="utf-8"?>
<com.example.ScalingGalleryItemLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/gallery_item_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:orientation="vertical"
android:padding="5dp" >
<ImageView
android:id="#+id/gallery_item_image"
android:layout_width="360px"
android:layout_height="210px"
android:layout_gravity="center"
android:antialias="true"
android:background="#drawable/gallery_item_button_selector"
android:cropToPadding="true"
android:padding="35dp"
android:scaleType="centerInside" />
<TextView
android:id="#+id/gallery_item_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textColor="#drawable/white"
android:textSize="30sp" />
</com.example.ScalingGalleryItemLayout>
To keep the state of the animation after it is done, just do this on your animation:
youranim.setFillAfter(true);
Edit :
In my project, I use this method and i think, it's help you :
http://developer.sonymobile.com/wp/2011/04/12/how-to-take-advantage-of-the-pinch-to-zoom-feature-in-your-xperia%E2%84%A2-10-apps-part-1/
U can do Image Zoom pinch option for gallery also.
by using below code lines:
you can download the example.
https://github.com/alvinsj/android-image-gallery/downloads
I hope this example will help to u..if u have any queries ask me.....
This is solution
integrate gallery component in android with gesture-image library
gesture-imageView
And here is full sample code
SampleCode
I'm not a programmer, but I'm trying to learn android development, and having a blast.
Lately I've hit a fork in the road, and I don't know that any of the directions I can identify will meet all my needs. This is where you (the experts) can help ;)
Endpoint: I want to have the user touch a ball, and drag it to any location on the screen. I would also like to animate this ball when it gets drawn, and when it gets dropped.
I can accomplish the dragging part using a custom class (that doesn't extend anything... just a class like is found in this tutorial: basic drag & drop, however, I don't know how to apply the tween animation to it since it's not a view.
I have also developed an animated version of an ImageView with my image in it, however, I can't manage to apply the same drag & drop functionality without using AbsoluteLayout, which I know is a no-no.
So... how do I move an ImageView around a ??? layout using MotionEvents, or how do I animate (using tweens defined in XML) a non-view based custom class?
Please ask questions if this is not clear. I don't know all the terminology as well as most of you might.
There is also a copy of this question on the anddev.org forums, so I'll keep this post updated with any responses I get over there.
Why can't you extend View? Then, you have complete control over how it draws because you can override the OnDraw() method. Just make the ColorBall class extend View. Change its position when you move and then invalidate just that one view and have it draw itself instead of having the DrawView class draw it.
Edit - Here is an example class
public class Card extends View
{
private Bitmap mImage;
private final Paint mPaint = new Paint();
private final Point mSize = new Point();
private final Point mStartPosition = new Point();
public Card(Context context)
{
super(context);
}
public final Bitmap getImage() { return mCardImage; }
public final void setImage(Bitmap image)
{
mImage = image;
setSize(mCardImage.getWidth(), mCardImage.getHeight());
}
#Override
protected void onDraw(Canvas canvas)
{
Point position = getPosition();
canvas.drawBitmap(mCardImage, position.x, position.y, mPaint);
}
public final void setPosition(final Point position)
{
mRegion.set(position.x, position.y, position.x + mSize.x, position.y + mSize.y);
}
public final Point getPosition()
{
Rect bounds = mRegion.getBounds();
return new Point(bounds.left, bounds.top);
}
public final void setSize(int width, int height)
{
mSize.x = width;
mSize.y = height;
Rect bounds = mRegion.getBounds();
mRegion.set(bounds.left, bounds.top, bounds.left + width, bounds.top + height);
}
public final Point getSize() { return mSize; }
#Override
public boolean onTouchEvent(MotionEvent event)
{
// Is the event inside of this view?
if(!mRegion.contains((int)event.getX(), (int)event.getY()))
{
return super.onTouchEvent(event);
}
if(event.getAction() == MotionEvent.ACTION_DOWN)
{
mStartPosition.x = (int)event.getX();
mStartPosition.y = (int)event.getY();
bringToFront();
onSelected();
return true;
}
else if(event.getAction() == MotionEvent.ACTION_MOVE)
{
int x = 0, y = 0;
if(mLock == DirectionLock.FREE || mLock == DirectionLock.HORIZONTAL_ONLY)
{
x = (int)event.getX() - mStartPosition.x;
}
if(mLock == DirectionLock.FREE || mLock == DirectionLock.VERTICAL_ONLY)
{
y = (int)event.getY() - mStartPosition.y;
}
mRegion.translate(x, y);
mStartPosition.x = (int)event.getX();
mStartPosition.y = (int)event.getY();
invalidate();
return true;
}
else
{
return super.onTouchEvent(event);
}
}
}
I'm not a programmer, but I'm trying to learn android development, and having a blast.
Lately I've hit a fork in the road, and I don't know that any of the directions I can identify will meet all my needs. This is where you (the experts) can help ;)
Endpoint: I want to have the user touch a ball, and drag it to any location on the screen. I would also like to animate this ball when it gets drawn, and when it gets dropped.
I can accomplish the dragging part using a custom class (that doesn't extend anything... just a class like is found in this tutorial: basic drag & drop, however, I don't know how to apply the tween animation to it since it's not a view.
I have also developed an animated version of an ImageView with my image in it, however, I can't manage to apply the same drag & drop functionality without using AbsoluteLayout, which I know is a no-no.
So... how do I move an ImageView around a ??? layout using MotionEvents, or how do I animate (using tweens defined in XML) a non-view based custom class?
Please ask questions if this is not clear. I don't know all the terminology as well as most of you might.
There is also a copy of this question on the anddev.org forums, so I'll keep this post updated with any responses I get over there.
Why can't you extend View? Then, you have complete control over how it draws because you can override the OnDraw() method. Just make the ColorBall class extend View. Change its position when you move and then invalidate just that one view and have it draw itself instead of having the DrawView class draw it.
Edit - Here is an example class
public class Card extends View
{
private Bitmap mImage;
private final Paint mPaint = new Paint();
private final Point mSize = new Point();
private final Point mStartPosition = new Point();
public Card(Context context)
{
super(context);
}
public final Bitmap getImage() { return mCardImage; }
public final void setImage(Bitmap image)
{
mImage = image;
setSize(mCardImage.getWidth(), mCardImage.getHeight());
}
#Override
protected void onDraw(Canvas canvas)
{
Point position = getPosition();
canvas.drawBitmap(mCardImage, position.x, position.y, mPaint);
}
public final void setPosition(final Point position)
{
mRegion.set(position.x, position.y, position.x + mSize.x, position.y + mSize.y);
}
public final Point getPosition()
{
Rect bounds = mRegion.getBounds();
return new Point(bounds.left, bounds.top);
}
public final void setSize(int width, int height)
{
mSize.x = width;
mSize.y = height;
Rect bounds = mRegion.getBounds();
mRegion.set(bounds.left, bounds.top, bounds.left + width, bounds.top + height);
}
public final Point getSize() { return mSize; }
#Override
public boolean onTouchEvent(MotionEvent event)
{
// Is the event inside of this view?
if(!mRegion.contains((int)event.getX(), (int)event.getY()))
{
return super.onTouchEvent(event);
}
if(event.getAction() == MotionEvent.ACTION_DOWN)
{
mStartPosition.x = (int)event.getX();
mStartPosition.y = (int)event.getY();
bringToFront();
onSelected();
return true;
}
else if(event.getAction() == MotionEvent.ACTION_MOVE)
{
int x = 0, y = 0;
if(mLock == DirectionLock.FREE || mLock == DirectionLock.HORIZONTAL_ONLY)
{
x = (int)event.getX() - mStartPosition.x;
}
if(mLock == DirectionLock.FREE || mLock == DirectionLock.VERTICAL_ONLY)
{
y = (int)event.getY() - mStartPosition.y;
}
mRegion.translate(x, y);
mStartPosition.x = (int)event.getX();
mStartPosition.y = (int)event.getY();
invalidate();
return true;
}
else
{
return super.onTouchEvent(event);
}
}
}