Many of us must have come across apps like Tinder and Dripper where you can pull down on the view containing an image and the image zooms in. And then when you let it go, the image zooms out to go back to its origin state.
Let's take an example from Tinder :
Original State: and Zoomed-in state when pulled:
In iOS it is done by
- (void)viewDidLoad {
[super viewDidLoad];
self.imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"church-welcome.png"]];
self.imageView.contentMode = UIViewContentModeScaleAspectFill;
self.cachedImageViewSize = self.imageView.frame;
[self.tableView addSubview:self.imageView];
[self.tableView sendSubviewToBack:self.imageView];
self.tableView.tableHeaderView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 170)];
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat y = -scrollView.contentOffset.y;
if (y > 0) {
self.imageView.frame = CGRectMake(0, scrollView.contentOffset.y, self.cachedImageViewSize.size.width+y, self.cachedImageViewSize.size.height+y);
self.imageView.center = CGPointMake(self.view.center.x, self.imageView.center.y);
}
}
Since my expertise in Objective C and iOS is very limited, I am not being able to implement it in Android.
Here is what I think should be done :
catch the pull-down gesture
increase the height of the view by the pull amount
do some sort of scale animation on the Image to fit it in the expanded view
Does anyone have any idea if there is any library that could be used for this purpose?
Check out this project:
https://github.com/Gnod/ParallaxListView
If you combine it with the ViewPagerIndicator library, you pretty much get Tinder's profile page feature set
https://github.com/JakeWharton/Android-ViewPagerIndicator
I think the easiest way is to override the onTouchEvent method of View.
Something like this:
boolean inZoom = false;
float prevY = 0;
#Override
public boolean onTouchEvent(MotionEvent event) {
float eventY = event.getY();
float eventX = event.getX();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if(touchedTheImage(eventX, eventY)){
setZoomCenter(eventX, eventY);
prevY = eventY;
inZoom = true;
return true;
}
break;
case MotionEvent.ACTION_MOVE:
if(inZoom){
changeZoomLevel(prevY, eventY);
return true;
}
break;
case MotionEvent.ACTION_UP:
if(inZoom){
resetZoomLevel();
inZoom = false;
return true;
}
break;
}
return false;
}
EDIT:
for the animation part consider this post:
https://stackoverflow.com/a/6650473/3568892
Related
I would like to change a SeekBar's behaviour. Therefore I need to edit two methods in AbsSeekBar, one of them is protected the other is
How can I do it?
Now I can't simply copy said methods from the AbsSeekbar and override them in my custom SeekBar class - I end up missing all field parameters.
I also can't just edit the AbsSeekBar class itself, since it is not part of my project. Any help is appreciated.
These are the two methods:
public abstract class AbsSeekBar extends ProgressBar {
.
.
.
#Override
public boolean onTouchEvent(MotionEvent event) {
if (!mIsUserSeekable || !isEnabled()) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (isInScrollingContainer()) {
mTouchDownX = event.getX();
} else {
setPressed(true);
if (mThumb != null) {
invalidate(mThumb.getBounds()); // This may be within the padding region
}
onStartTrackingTouch();
trackTouchEvent(event);
attemptClaimDrag();
}
break;
case MotionEvent.ACTION_MOVE:
if (mIsDragging) {
trackTouchEvent(event);
} else {
final float x = event.getX();
if (Math.abs(x - mTouchDownX) > mScaledTouchSlop) {
setPressed(true);
if (mThumb != null) {
invalidate(mThumb.getBounds()); // This may be within the padding region
}
onStartTrackingTouch();
trackTouchEvent(event);
attemptClaimDrag();
}
}
break;
case MotionEvent.ACTION_UP:
if (mIsDragging) {
trackTouchEvent(event);
onStopTrackingTouch();
setPressed(false);
} else {
// Touch up when we never crossed the touch slop threshold should
// be interpreted as a tap-seek to that location.
onStartTrackingTouch();
trackTouchEvent(event);
onStopTrackingTouch();
}
// ProgressBar doesn't know to repaint the thumb drawable
// in its inactive state when the touch stops (because the
// value has not apparently changed)
invalidate();
break;
case MotionEvent.ACTION_CANCEL:
if (mIsDragging) {
onStopTrackingTouch();
setPressed(false);
}
invalidate(); // see above explanation
break;
}
return true;
private void trackTouchEvent(MotionEvent event) {
final int width = getWidth();
final int available = width - mPaddingLeft - mPaddingRight;
final int x = (int) event.getX();
float scale;
float progress = 0;
if (isLayoutRtl() && mMirrorForRtl) {
if (x > width - mPaddingRight) {
scale = 0.0f;
} else if (x < mPaddingLeft) {
scale = 1.0f;
} else {
scale = (float)(available - x + mPaddingLeft) / (float)available;
progress = mTouchProgressOffset;
}
} else {
if (x < mPaddingLeft) {
scale = 0.0f;
} else if (x > width - mPaddingRight) {
scale = 1.0f;
} else {
scale = (float)(x - mPaddingLeft) / (float)available;
progress = mTouchProgressOffset;
}
}
final int max = getMax();
progress += scale * max;
setHotspot(x, (int) event.getY());
setProgress((int) progress, true);
}
If you absolutely have to change how a private method works, you'll need to copy the entire class into your project, edit that copy, and use that version of the class instead of the original. Its something that should probably be avoided if possible.
I'm not sure I understand 'I end up missing all field parameters'
The only way you can edit a method's functionality in Java is by extending the class.
Example:
public class CustomSeekbar extends AbsSeekBar {
public CustomSeekbar(Context context) {
super(context);
}
#Override
protected boolean verifyDrawable(Drawable who) {
//Do your own method functionality
//Return your own true/false
return super.verifyDrawable(who);
}
}
In this case you don't have to return super.verifyDrawable if you don't want to, and you can supply your own response.
This is true for all protected or public methods in the AbsSeekBar class.
By reflection, you can modify fields of the class if you so please.
For example:
Field drawableField = absSeekBarInstance.getClass().getField("mThumb");
drawableField.setAccessible(true);
drawableField.set(absSeekBarInstance, someDrawable);
Or to GET the field values, you just say
drawableField.get(absSeekBarInstance);
To go even further, you'll need to provide me more information.
Private methods cannot be changed. It's just not possible.
Aspect-oriented programming in android
AspectJ comes close, but you can't do what you're describing. Thats just how Java works. In Objective-c you can do this, it is called 'swizzling'.
But yes, in Java, you cannot change the functionality of compiled methods.
Your best bet would be to copy AbsSeekBar and all of its required classes into your own project.
I'm trying to make an Android puzzle game out of a picture.
What i've done 'till now is:
-i've cut the picture in how many parts i get from the EditText field;
-i've put the cut pictures in a gridview and got a puzzle table;
-i've also used the Collection.suffle(list) to suffle the images in the gridview;
Now, i am trying to implement the sliding effect on two images from gridview. So what i want to achieve is a sliding effect similar to CandyCrush app or this one http://www.youtube.com/watch?v=qUoP87fNB8w . I mean, i want to hold an image and drag it top so it should slide top. If i click the picture and slide bottom, it should change with the picture below it and so on...
Here is my code:
Reset = (Button) findViewById(R.id.BReset);
SplitNo = (EditText) findViewById(R.id.edSplitBy);
SplitBy = Integer.parseInt(SplitNo.getText().toString());
ImgNo = SplitBy * SplitBy;
puzzle = (GridView) findViewById(R.id.puzzle);
rootImg = (ImageView) findViewById(R.id.source_image);
splitImage(rootImg);
formPuzzle(smallimages);
Reset.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Reset.setText("Reset");
SplitNo.getEditableText().toString();
SplitBy = Integer.parseInt(SplitNo.getText().toString());
ImgNo = SplitBy * SplitBy;
Collections.shuffle(smallimages);
formPuzzle(smallimages);
}
});
And here is how i form the puzzle:
private void formPuzzle(ArrayList<PuzzleItem> smallimages) {
SMI = new SmallImageAdapter(this, smallimages);
puzzle.setAdapter(SMI);
puzzle.setNumColumns(SplitBy);
puzzle.setHorizontalSpacing(0);
puzzle.setVerticalSpacing(0); }
PuzzleItem.class:
public class PuzzleItem {
Bitmap puzzlepartImage;
int correctPosition;
public PuzzleItem(Bitmap d, int index) {
puzzlepartImage = d;
correctPosition = index;
}
I used this class just to remember the position of the pieces so i can check if the puzzle is finished...
So i'm only interested in how to implement the sliding motion by draging an item from gridview...
Thanks :)
EDIT: Should i use an OnTouchListener ?
Ok, I've managed to find the solution by myself :
private void formPuzzle(final ArrayList<PuzzleItem> smallimages) {
SMI = new SmallImageAdapter(this, smallimages);
puzzle.setAdapter(SMI);
puzzle.setNumColumns(SplitBy);
puzzle.setHorizontalSpacing(0);
puzzle.setVerticalSpacing(0);
puzzle.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
startX = event.getX();
startY = event.getY();
smallimage_Position = puzzle.pointToPosition((int) startX,
(int) startY);
return true;
}
case MotionEvent.ACTION_MOVE: {
endX = event.getX();
endY = event.getY();
Log.d("X", "..." +endX);
Log.d("Y", "..." +endY);
if(endX > startX + smallimage_Width/2 && (endY < startY - smallimage_Height/2 || endY < startY + smallimage_Height/2) ){
PuzzleItem p1 = SMI.smallimages.get(smallimage_Position);
PuzzleItem p2 = SMI.smallimages.get(smallimage_Position +1);
SMI.smallimages.set(smallimage_Position, p2);
SMI.smallimages.set(smallimage_Position +1, p1);
SMI.notifyDataSetChanged();
startX = endX*200;
startY = endY*200;
}
return true;
}
}
return true;
}
});
The slide is given by the MotionEvent.Action_MOVE and by calculating the pixels you can find out in what direction is the finger moving ... For example .. the if from my Action_Move case is recognising a swipe to the right ...
I hope this will help some of you guys ...
Another way to go about achieving slides in a slider puzzle game is to use a game engine. I recommend Cocos2D for Android, and theres a detailed tutorial on that here ...
http://denvycom.com/blog/step-by-step-guide-on-how-to-build-your-first-slider-puzzle-game-in-cocos2d-for-android-part-1/
I have made an application that enables to use android phone as touchpad.
It is working correctly as it should.
But only thing bothering me is that there is LAG. mouse on laptop sometimes lags i.e not in complete sync with touch on android.
The only reason i could think of lag is the logic used in calculating the next coordinates.
Here is the code for Android Application that sends coordinates on touch event :(t is TextView)
t.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction()==MotionEvent.ACTION_DOWN){
startX = event.getX();
startY = event.getY();
}
if(event.getAction() == MotionEvent.ACTION_MOVE)
{
out.println("X:"+(event.getX()-startX)+"Y:"+(event.getY()-startY));
startX=event.getX();
startY=event.getY();
}
return true;
}
});
Logic i am using is that i am sending difference of the coordinates.
On serverside , basic thing happening is ('in' is BufferedReader ):
PointerInfo a = MouseInfo.getPointerInfo();
Point b = a.getLocation();
currX = (int)b.getX();
currY=(int)b.getY();
while ((inputLine = in.readLine()) != null) {
if(inputLine.equals("LeftClick"))
{
robot.mousePress(InputEvent.BUTTON1_MASK);
robot.mouseRelease(InputEvent.BUTTON1_MASK);
}
else if(inputLine.equals("RightClick")){
robot.mousePress(InputEvent.BUTTON3_MASK);
robot.mouseRelease(InputEvent.BUTTON3_MASK);
}
else{
outputLine = kkp.processInput(inputLine);
//To break the string into X cordi and Y cordi.
yloc = inputLine.indexOf("Y");
float xcord = Float.parseFloat(inputLine.substring(2,yloc));
float ycord = Float.parseFloat(inputLine.substring(yloc+2));
robot.mouseMove(currX+Math.round(xcord),currY+Math.round(ycord));
currX+=Math.round(xcord);
currY+=Math.round(ycord);
}
}
Is the delay normal like Can't be improved . or can be improved by changing the calculations in above code. and PS: i am not using UDP.
Please help me i want to get position of imageview on a layout.
x = imageViewObject.getLeft();
y = imageViewObject.getTop();
Hope, it help you!
You can use this with getLocationOnScreen(int [])
At runtime you can get the location of every View object (this includes Layouts too) with
Left Position of View Object getleft()
Top Position of View Object getTop()
Right Position of View Object getRight()
Bottom Position of View Object getBottom()
Even you can get the location with
getLocationOnScreen(int [] )
Position meaning x and y and height and width?
This layout is a view, that view has these properties.
the below code works for me to get the original spot i used the global boolean variable moved to find the value once in the onTouch method v.getX() and ...getY() gets the value of my imageView object I have run this on my apk file on a device and it works perfectly. hope this helps
joystick.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if(moved){
moved = false;
xOrigin = v.getX();
yOrigin = v.getY();
mode.setText(Float.toString(xOrigin));
challenge.setText(Float.toString(yOrigin));
}
mode.setText(Float.toString(v.getX()));
challenge.setText(Float.toString(v.getY()));
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
xCoOrdinate = v.getX() - event.getRawX();
yCoOrdinate = v.getY() - event.getRawY();
//mode.setText(Float.toString(v.getX()));
//challenge.setText(Float.toString(v.getY()));
break;
case MotionEvent.ACTION_MOVE:
v.animate().x(event.getRawX() + xCoOrdinate).y(event.getRawY() + yCoOrdinate).setDuration(0).start();
if(v.getX() < xOrigin -50){
v.animate().x(xOrigin -50).setDuration(0).start();
}
else if(v.getX() > xOrigin +50) {
v.animate().x(xOrigin +50).setDuration(0).start();
}
if(v.getY() < yOrigin -50){
v.animate().y(yOrigin -50).setDuration(0).start();
}
else if(v.getY() > yOrigin +50) {
v.animate().y(yOrigin +50).setDuration(0).start();
}
break;
default:
v.setX(xOrigin);
v.setY(yOrigin);
mode.setText(Float.toString(xOrigin));
challenge.setText(Float.toString(yOrigin));
return false;
}
return true;
}
});
After getting the calculator application to work I decided to try to create pong. There is a box in the center and two paddles on both ends. The phone is horizontal. I have the box bouncing off the walls and the paddle moves with me moving my finger down. My problem is i want to make it two player and i want to have multiple finger input for the game. I want one finger to move paddle 1 and the other to move paddle 2. So far this is my input code
#Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_MOVE: {
// Find the index of the active pointer and fetch its position
float p1y = ev.getY();
if(ev.getX()<300)
{
player1y = p1y;
}
if(ev.getX()>300)
{
player2y = p1y;
}
//player1y = p1y;
invalidate();
break;
}
}
return true;
}
it resides in my surfaceview class. How can i modify the input method or completely get rid of it and change it to accomplish my goal? Also sorry about my variables. Eclipse crashes a lot on me and my laptops touch panel tends to move my cursor so shorter variables seemed viable. p1y is the y of the touch. and player1y and player2y is the y positions of the player1 and player2 paddle.
A MotionEvent can hold multiple pointers. Use getPointerCount() to see how many pointers are touching the screen in the current event. There are alternate versions of getX and getY that take an index from 0-getPointerCount() - 1.
In a more complex app you would want to track fingers by pointer ID, but for something this simple where you are using a cutoff point on the screen you could do something like this in your ACTION_MOVE case:
int pointerCount = ev.getPointerCount();
for (int i = 0; i < pointerCount; i++) {
float x = ev.getX(i);
float y = ev.getY(i);
if (x < 300) {
player1y = y;
} else if (x > 300) {
player2y = y;
}
}
This post from the Android Developers Blog might help if you'd like more information: http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html