I've got an imageview that resizes to a random size within a given range. The method below is called in onCreate and works fine when it's called the first time. However when it is called in onTouchEvent, the method runs but doesn't resize the imageView.
public void setInnerCircleSize()
{
widthMultiplier = r.nextInt(75 - 25) + 25;
widthToSet = ((widthScreen / 100) * widthMultiplier);
circle.getLayoutParams().height = widthToSet;
circle.getLayoutParams().width = widthToSet;
}
public boolean onTouchEvent(MotionEvent event) {
int eventaction = event.getAction();
switch (eventaction) {
case MotionEvent.ACTION_DOWN:
if(hitCheck == true)
{
hitCheck = false;
setInnerCircleSize();
}
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
hitCheck = true;
break;
}
}
setInnerCircleSize is being called fine when there is a motionevent and the random variables are created so there's nothing wrong in that respect. It's just that it won't resize a second time even though it worked perfectly fine when called from onCreate.
You have to invalidate the view to tell android to redraw it. Since you are changing the view bounds, I think you are suppose to use:
View.requestLayout();
Related
I'm developing an application where I'm applying on touch listener to my layout view. I move the layout over screen but that layout doesn't respond well after 10 to 15 secs. Here is my code:
base = (LinearLayout) findViewById(R.id.load);
base.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, final MotionEvent event) {
int motion = event.getAction();
int numberOfPointers = event.getPointerCount();
if (numberOfPointers < 3) {
switch (motion & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
drag = true;
break;
case MotionEvent.ACTION_POINTER_DOWN: {
drag = false;
if (drawWaveForm != null) {
zoom = true;
}
// initial x1 and x2
BitmapDetector.prevX1 = Math.min(event.getX(0), event.getX(1));
BitmapDetector.prevX2 = Math.max(event.getX(0), event.getX(1));
BitmapDetector.prevY1 = event.getY(0);
BitmapDetector.prevY2 = event.getY(1);
}
break;
case MotionEvent.ACTION_MOVE:
if (drag && event.getActionIndex() == 0) {
// Log.d("detecting","inside the action move");
if (touching == false) {
touching = detector.isWaveformAnchor(event.getX(0), event.getY(0));
} else if (touching == true) {
float diff = event.getY(0) - loadWaveFormManager.getCh1_pos();
if ((loadWaveFormManager.getCh1_pos() + diff) < (StaticValues.screenHeight - 50)) {
int tempCh1_pos = (int) (loadWaveFormManager.getCh1_pos() + diff);
loadWaveFormManager.setCh1_pos(tempCh1_pos);
}
drawWaveForm.update(loadWaveFormManager);
}
}
break;
case MotionEvent.ACTION_UP:
// Log.d("detecting","inside the action up");
drag = false;
touching = false;
zoom = false;
break;
case MotionEvent.ACTION_POINTER_UP:
// here different function
return;
}
});
Does anyone have any idea about this? Please help me on this one.
It is not clear from your code what is your problem and when does it happen. I don't see any TimerTask or AsyncTask reference.
I guess, you're trying to do some expensive operation in UI thread, and this freezes your application. Possible long-running candidate is drawWaveForm.update(loadWaveFormManager)
If this is so, you should preform your computations asynchronously, and only then update UI.
By the way, your code will not compile at all:
public boolean onTouch(View v, final MotionEvent event)
should return true or false, instead you perform return; at the end of this method. That leads to an assumption, that you're providing irrelevant code in your question, but still want to receive a relevant answer.
Provide relevant code piece for further analyze.
Currently you are starting thread on touch event use handler instead
of thread
I have on my activity several ImageView's that the user can move around the screen. How can I identify which ImageView is actually selected by the user? In Xcode I can assign a unique Tag to each ImageView and than onTouch get the Tag number. Is there a similar way on Android?
this is Xcode:
UITouch *touch = [[event allTouches] anyObject];
if ([touch view].tag == 901)
{
iTouchObject = 901;
[self.view bringSubviewToFront:imgRojo1];
}
In Android Activity I have so far done: in onCreate I can assign an ID to each ImageView
ImageView selImage = (ImageView)findViewById(R.id.i904);
selImage.setId(904);
and in onTouch.MOVED I can use the ImageView with a directly assigned ID. In this case the image moves smooth below the finger on the screen.
iID = 904;
ImageView selImage = (ImageView)findViewById(iID);
BUT, how can I get the ID from the ImageView that the user has touched?
I have tried to use setOnTouchListener:
ImageView selImage = (ImageView)findViewById(R.id.i904);
selImage.setId(904);
selImage.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
iID = v.getId();
ImageView selImage = (ImageView)findViewById(iID);
int eventaction = event.getAction();
int X = (int)event.getX();
int Y = (int)event.getY();
switch (eventaction ) {
case MotionEvent.ACTION_DOWN: // touch down so check if the finger is on a ball
break;
case MotionEvent.ACTION_MOVE: // touch drag with the ball
// move the balls the same as the finger
selImage.setX(X-25);
selImage.setY(Y-25);
break;
case MotionEvent.ACTION_UP:
// touch drop - just do things here after dropping
break;
}
// redraw the canvas
return true;
}});
By this I can select each ImageView and it moves, BUT, it doesn't move smooth and also the ImageView is duplicated on the screen.
Is it possible to get in the first case in the onTouch.MOVED the View and use it to get the ID with: iID = v.getId();
You can check what id your current view has and compare it with the actual resource Id.
For example:
switch (v.getId()) {
case R.id.image1:
// Do this
break;
case R.id.image2:
// ..
break;
case R.id.image3:
// ..
break;
}
You may also check out this page to read a bit more about drag-and-drop with the ChoiceDragListener instead.
UPDATE:
I have found a solution, maybe not the best optimized, but great working. Hope that it helps others of you in similar situations:
For each ImageView I am assigning an ID (setID) and setOnTouchListener. Only thing I am doing in setOnTouchListener is to assign to an integer the same number as I have used for the ID. Important is to put RETURN FALSE, this will stop the setOnTouchListener.
ImageView selImage = (ImageView)findViewById(R.id.i904);
selImage.setId(904);
selImage.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
iID = 904;
return false;
}
});
I have put added the following onTouchEvent(). First thing here is to select the ImageView with the ID that was set in the integer before.
public boolean onTouchEvent(MotionEvent event) {
ImageView selImage = (ImageView)findViewById(iID);
int eventaction = event.getAction();
int X = (int)event.getX();
int Y = (int)event.getY();
switch (eventaction ) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
selImage.setX(X-25);
selImage.setY(Y-25);
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
Now I can touch any of the ImageView's and move them around on the screen. Hope it helps!
When I try to apply a translate animation on an ImageView, the image just completely disappears. I am not sure why this would happen because I ran this code through the eclipse debugger and the value for the x_start and x_final seem to be correct.
Any ideas on why this would be happening or how I can end up getting my TranslateAnimation to work?
chargeButton.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
int x_cord = (int) event.getRawX();
int y_cord = (int) event.getRawY();
switch (event.getActionMasked()){
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
chargeButton.setLayoutParams(setPosition(x_cord, false));
break;
case MotionEvent.ACTION_UP:
int x_start = x_cord;
x_cord = 0;
slowMove(x_start, x_cord, false);
chargeButton.setLayoutParams(setPosition(x_cord, false));
break;
default:
break;
}
return true;
}
});
}
public void slowMove(int x_start, int x_final, boolean pay)
{
Animation transAnimation = new TranslateAnimation(x_start, x_final, 0, 0);
transAnimation.setFillAfter(true);
transAnimation.setDuration(1000);
if (pay)
payButton.startAnimation(transAnimation);
else
{
chargeButton.clearAnimation();
chargeButton.startAnimation(transAnimation);
}
}
A little more background, this function is being called from an onTouchListener under the MotionEvent.ACTION_UP case.
I feel as though I need to use Animation.Relative_TO_SELF or something like that for the positioning. However, I am not sure how to do that when I only have the absoute positioning of the ImageViews.
Any and all ideas will be greatly appreciated.
Do not use the layout parameters to move your views - it is really not a good idea. I have been down that road and nothing good came out of it.
Consider extending View and setup your own custom view where you can have full control of what goes on.
hi i need to rotate an ImageView in my app on touch
and i am using the following code
public android.view.View.OnTouchListener onTableTouched = new android.view.View.OnTouchListener(){
public boolean onTouch(View v, MotionEvent evt) {
WrapMotionEvent event = WrapMotionEvent.wrap(evt);
ImageView view = (ImageView) v;
// Dump touch event to log
dumpEvent(event);
//
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_POINTER_DOWN:
mode = NONE;
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_MOVE:
if(mode == NONE){
updateRotation();
}
break;
}
return true;
}
};
private void updateRotation()
{
matrix.postRotate(10);
Bitmap redrawnBitmap = Bitmap.createBitmap(itembmp, 0, 0,itembmp.getWidth(), itembmp.getHeight(), matrix, true);
itembmp=redrawnBitmap;
image.setImageBitmap(itembmp);
}
but by using this i can rotate the image in clock-direction only.
But i need to rotate the image in both directions .
How to do that.
I would make two functions, one called updateRotationClockwise, one called updateRotationCounterClockWise.
In clockwise, it should stay:
matrix.postRotate(10);
In counterclockwise, it should say:
matrix.postRotate(350);
I am in process of developing this logic.
1. Difference in angle. one needs to also check other boundary conditions for more accuracy.
2. Instead of handling touch actions. I used in build ScaleGestureDetector class for information.
In onScaleBegin assuming I get the event using a setter (called in on touch) in the OnScaleGestureListener.
before=gety/getx;
In on ScaleEnd
after=gety/getx;
thus difference is tan-1(before)-tan-1(after)
if +tive anticlockwise -tive clockwise.
It is not so accurate I am adding conditions but one can try this out.
from the class ClockView you can find the method for rotation for your bitmap image
https://github.com/jselbie/xkcdclock
My class extends View and I need to get continuous touch events on it.
If I use:
public boolean onTouchEvent(MotionEvent me) {
if(me.getAction()==MotionEvent.ACTION_DOWN) {
myAction();
}
return true;
}
... the touch event is captured once.
What if I need to get continuous touches without moving the finger?
Please, tell me I don't need to use threads or timers. My app is already too much heavy.
Thanks.
Use if(me.getAction() == MotionEvent.ACTION_MOVE). It's impossible to keep a finger 100% completely still on the screen so Action_Move will get called every time the finger moves, even if it's only a pixel or two.
You could also listen for me.getAction() == MotionEvent.ACTION_UP - until that happens, the user must still have their finger on the screen.
You need to set this properties for the element
android:focusable="true"
android:clickable="true"
if not, just produce the down action.
Her is the simple code snippet which shows that how you can handle the continues touch event. When you touch the device and hold the touch and move your finder, the Touch Move action performed.
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
if(isTsunami){
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// Write your code to perform an action on down
break;
case MotionEvent.ACTION_MOVE:
// Write your code to perform an action on contineus touch move
break;
case MotionEvent.ACTION_UP:
// Write your code to perform an action on touch up
break;
}
}
return true;
}
Try this. It works to me:
public static OnTouchListener loadContainerOnTouchListener() {
OnTouchListener listener = new OnTouchListener(){
#Override
public boolean onTouch(View v, MotionEvent event) {
LinearLayout layout = (LinearLayout)v;
for(int i =0; i< layout.getChildCount(); i++)
{
View view = layout.getChildAt(i);
Rect outRect = new Rect(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
if(outRect.contains((int)event.getX(), (int)event.getY()))
{
Log.d(this.getClass().getName(), String.format("Over view.id[%d]", view.getId()));
}
}
}
Remember: the listener you´ll set must be a container layout (Grid, Relative, Linear).
LinearLayout layout = findViewById(R.id.yourlayoutid);
layout.setOnTouchListener(HelperClass.loadContainerOnTouchListener());
This might help,
requestDisallowInterceptTouchEvent(true);
on the parent view, like this -
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
view.getParent().requestDisallowInterceptTouchEvent(true);
switch(motionEvent.getAction()){
}
return false;
}
I was making a game with a custom view used as a thumb control. . . here is what I did
float x = 0, y = 0;
#Override
public boolean onTouchEvent(MotionEvent event) {
x = event.getX();
y = event.getY();
// handle touch events with
switch( event.getActionMasked() ) {
case MotionEvent.ACTION_DOWN :
if(cont)
{
// remove any previous callbacks
removeCallbacks(contin);
// post new runnable
postDelayed(contin, 10);
}
invalidate();
return true;
case MotionEvent.ACTION_MOVE :
if(!cont && thumbing != null)
{
// do non-continuous operations here
}
invalidate();
return true;
case MotionEvent.ACTION_UP :
// set runnable condition to false
x = 0;
// remove the callbacks to the thread
removeCallbacks(contin);
invalidate();
return true;
default :
return super.onTouchEvent(event);
}
}
public boolean cont = false;
// sets input to continuous
public void set_continuous(boolean b) { cont = b; }
public Runnable contin = new Runnable()
{
#Override
public void run() {
if(x != 0)
{
// do continuous operations here
postDelayed(this, 10);
}
}
};
A quick note however, make sure in your main activity that is calling this view removes the callbacks manually via the onPause method as follows
#Override
protected void onPause() {
if(left.cont) left.removeCallbacks(left.contin);
if(right.cont) right.removeCallbacks(left.contin);
super.onPause();
}
That way if you pause and come back touch events aren't being handled twice and the view is free from it's thread's overhead.
** tested on Samsung Galaxy S3 with hardware acceleration on **
All these answer are partially correct but they do not resolve in the right way the problem.
First of all, for everyone out there that decide to track when the event is ACTION_MOVE. Well that works only guess when? When user move his finger, so could if you decide to implement a custom thumb control is okay but for a normal custom button that's not the case.
Second, using a flag inside ACTION_DOWN and check it in ACTION_UP seems the logic way to do it, but as Clusterfux find out if you implement a while(!up_flag) logic you get stuck into troubles ;)
So the proper way to do it is mentioned here:
Continuous "Action_DOWN" in Android
Just keep in mind that if the logic you're going to write during the continuous press has to modify the UI in some way, you have to do it from the main thread in all the other cases it's better use another thread.
You can use the below code snippet as a reference in which I used the background to detect if the screen is held or not...
Main_Layout.setOnTouchListener(new View.OnTouchListener() {
#SuppressLint("ResourceAsColor")
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
Main_Layout.setBackgroundColor(R.color.green);
event.setAction(MotionEvent.ACTION_DOWN);
break;
default:
Main_Layout.setBackgroundColor(R.color.blue);
break;
}
return false;
}
});