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
Related
I am developing a game and I need to be able to detect that one finger is performing a MOVE while posibly another finger can TOUCH another part of the screen.
With the following code I am able to detect both the ACTION_MOVE (on certain region of the screen) and the ACTION_DOWN
public boolean onTouch(View v, MotionEvent event) {
final int dest_x = (int) event.getX();
final int dest_y = (int) event.getY();
onTrackPad = dbSettings.TRACK_PAD.contains(dest_x, dest_y);
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
if (onTrackPad)
{
//move character
}
break;
case MotionEvent.ACTION_DOWN:
// Fire bullets
break;
}
//The event was consumed
return true;
}
The problem is that I am not able to move and fire at the same time (I need to stop moving in order to fire and viceversa)
I am aware that Android can handle multi-touch events but have not figure it how to use that to be able to process these events and the same time so that the player can move and fire at the same time
I have also try using the getActionMasked without any luck
After reading this question Android MotionEvent.getActionIndex() and MultiTouch
This is how I solved the problem
public boolean onTouch(View v, MotionEvent event) {
int dest_x ;
int dest_y ;
p = event.getActionIndex() ;
dest_x = (int) event.getX(p);
dest_y = (int) event.getY(p);
onTrackPad = dbSettings.TRACK_PAD.contains(dest_x, dest_y);
action = event.getActionMasked() ;
switch (action) {
case MotionEvent.ACTION_MOVE:
if (onTrackPad)
{
//move character
}
break;
case MotionEvent.ACTION_DOWN:
// Fire bullets
break;
}
//The event was consumed
return true;
}
MotionEvent has all information about touches that you need. You can get number of touches by executing event.getPointersCount(), and try to check MotionEvent.ACTION_POINTER_2_DOWN instead of MotionEvent.ACTION_DOWN. To get coordinates of each touch you can use event.getX(0) and event.getX(1), same is for y. If you have a case of MotionEvent.ACTION_MOVE with 2 touches, you will receive all this information in your motion event.
Try below code.
when multiple pointers touches on the screen,system generates the action events.we can keep track of individual pointers with in motion event using pointer id. Pointer id persists across touch events and also allows to track individual pointer across entire gesture.
#Override
public boolean onTouch(View v, MotionEvent event) {
int index = event.getActionIndex();
int pointerID = event.getPointerId(index);
int action = event.getActionMasked();
if(event.getPointerCount() > 1)
{
Log.i("TouchType ", "Multi Touch");
for(int i = 0; i < event.getPointerCount(); i++)
{
performAction(action);
}
}else
{
Log.i("TouchType ", "Single Touch");
performAction(action);
}
return true;
}
public void performAction(int action){
switch(action)
{
case MotionEvent.ACTION_DOWN :
Log.i("OnTouch ", "Pressed");
// Fire bullets
break;
case MotionEvent.ACTION_MOVE :
Log.i("OnTiouch", "move");
//move character
break;
case MotionEvent.ACTION_UP :
Log.i("OnTiouch", "Up");
break;
default:
Log.i("OnTiouch", "None");
}
}
Is it possible to disable SwipeRefreshLayout drag animation on swipe down without class customization?
Try calling:
setEnabled(false)
on your SwipeRefreshLayout view.
Well, disabling SwipeLayoutAnimanion appeared to be a rather simple task, but it involves replication of android.support.v4.widget.SwipeRefreshLayout class inside one's project.
Diving in source code will reveal, that SwipeRefreshLayout consists of three classes:
android.support.v4.widget.SwipeRefreshLayout
android.support.v4.widget.SwipeProgressBar
android.support.v4.widget.BakedBezierInterpolator
All three classes should be included in the project. Then SwipeRefreshLayout can be customized as follows:
Add a new public method which will control either layout should follow the swipe down gesture or not:
private boolean mLayoutMovementEnabled = true;
public void setLayoutMovementEnabled(boolean enabled) {
mLayoutMovementEnabled = enabled;
}
All related computations are performed inside onTouchEvent(). To disable layout following the movement,
updateContentOffsetTop((int) (offsetTop)); string should be changed to
if (mLayoutMovementEnabled) updateContentOffsetTop((int) (offsetTop));
The complete modified routine is below.
#Override
public boolean onTouchEvent(MotionEvent event) {
final int action = event.getAction();
boolean handled = false;
switch (action) {
case MotionEvent.ACTION_DOWN:
mCurrPercentage = 0;
mDownEvent = MotionEvent.obtain(event);
mPrevY = mDownEvent.getY();
break;
case MotionEvent.ACTION_MOVE:
if (mDownEvent != null && !mReturningToStart) {
final float eventY = event.getY();
float yDiff = eventY - mDownEvent.getY();
if (yDiff > mTouchSlop) {
// User velocity passed min velocity; trigger a refresh
if (yDiff > mDistanceToTriggerSync) {
// User movement passed distance; trigger a refresh
startRefresh();
handled = true;
break;
} else {
// Just track the user's movement
setTriggerPercentage(
mAccelerateInterpolator.getInterpolation(
yDiff / mDistanceToTriggerSync));
float offsetTop = yDiff;
if (mPrevY > eventY) {
offsetTop = yDiff - mTouchSlop;
}
if (mLayoutMovementEnabled) updateContentOffsetTop((int) (offsetTop));
if (mPrevY > eventY && (mTarget.getTop() < mTouchSlop)) {
// If the user puts the view back at the top, we
// don't need to. This shouldn't be considered
// cancelling the gesture as the user can restart from the top.
removeCallbacks(mCancel);
} else {
updatePositionTimeout();
}
mPrevY = event.getY();
handled = true;
}
}
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (mDownEvent != null) {
mDownEvent.recycle();
mDownEvent = null;
}
break;
}
return handled;
}
One simple way to disable the swipe is to set the distance to trigger sync to some value too high to be reached.
mSwipeLayout.setDistanceToTriggerSync(999999);
I want to catch ONLY two fingers touch (one finger touch must be NOT consumed).
Unfortunately, I need to consume the "one finger touch" in order to get the "two finger touch" events.
I hope I'm a clear.
Here my code :
#Override
public boolean onTouch(View v, MotionEvent m)
{
boolean consumed = false;
int pointerCount = m.getPointerCount();
int action = m.getActionMasked();
if(pointerCount == 2)
{
switch (action)
{
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
consumed = true;
break;
case MotionEvent.ACTION_UP:
// Work
break;
case MotionEvent.ACTION_MOVE:
// Some stuff
break;
default:
break;
}
}
else if (pointerCount == 1 && action == MotionEvent.ACTION_DOWN)
{
// This is needed to get the 2 fingers touch events...
consumed = true;
}
return consumed;
}
I've ran into a similar problem and solved my problem by handling all touches at my top view, and propagating the appropriate actions to the views below. In my example, I had an overlay layer that needed to intercept scale gestures (a recognizer is attached in my constructor), while still propagating single touches to my map view below. I haven't sent the actual touch event to my map view, but I've handled it in my top view and sent the appropriate "panning" action to my view below.
#Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getPointerCount() == 2) {
if(event.getActionMasked() == MotionEvent.ACTION_DOWN){
CameraUpdate update = CameraUpdateFactory.newLatLng(initialCoords);
//attachedMapFragment.getMap().moveCamera(update);
}
scaleGestureDetector.onTouchEvent(event);
}else{
if(attachedMapFragment != null) {
if(event.getActionMasked() == MotionEvent.ACTION_DOWN){
initialCoords = attachedMapFragment.getMap().getCameraPosition().target;
lastTouchX = event.getX();
lastTouchY = event.getY();
}else if(event.getActionMasked() == MotionEvent.ACTION_MOVE){
float deltaX = event.getX() - lastTouchX;
float deltaY = event.getY() - lastTouchY;
lastTouchX = event.getX();
lastTouchY = event.getY();
Projection projection = attachedMapFragment.getMap().getProjection();
Point center = projection.toScreenLocation(attachedMapFragment.getMap().getCameraPosition().target);
Point newCenter = new Point(center.x - (int)deltaX, center.y - (int)deltaY);
LatLng newCoords = projection.fromScreenLocation(newCenter);
CameraUpdate update = CameraUpdateFactory.newLatLng(newCoords);
attachedMapFragment.getMap().moveCamera(update);
}
}
}
return true;
}
This way, I had a scale recognizer on my custom view, while the map was still panning appropriately to single touch. Actually, map view wasn't getting any touches at all, my custom view was "routing" the action as seen above. While not always ideal, this would solve the problem in many cases.
in my App I have a Custom View in an Activity.
To detect touch events I have the method onTouchEvent() in both of them.
Custom View: The onTouchEvent()-method basically just marks the object which is touched.
#Override
public boolean onTouchEvent(MotionEvent event) {
int maskedAction = event.getActionMasked();
switch (maskedAction) {
case MotionEvent.ACTION_DOWN:
touchedXY[0] = event.getX();
touchedXY[1] = event.getY();
mMatrix.mapPoints(touchedXY); // this is just for
event.setLocation(touchedXY[0], touchedXY[1]); // rotation purposes
for(int i = 0; i < mPoints[i]; i++){
mPoints[i].isSelected(setIsSelected(touchedXY)); //using method to
//check if point
//is selected
}
break;
default:
return false;
}
invalidate();
return true;
}
Activity: If an Object is touched a LinearLayout should appear at the Bottom of the Activity with the number of touched Points.
int key = 0;
#Override
public boolean onTouch(View v, MotionEvent event) {
// get masked (not specific to a pointer) action
int maskedAction = event.getActionMasked();
switch (maskedAction) {
case MotionEvent.ACTION_DOWN:
List<AddressPoint> selectedPoints = Arrays.asList(mPoints);
if (key == 0) {
if (selectedPoints.size() > 0) {
key = 1;
test.setText(String.valueOf(selectedPoints.size()));
popup.setVisibility(View.VISIBLE);
} else {
test.setText(String.valueOf(selectedPoints.size()));
}
} else {
if (selectedPoints.size() > 0) {
test.setText(String.valueOf(selectedPoints.size()));
} else {
key = 0;
popup.setVisibility(View.GONE);
}
}
break;
default:
return false;
}
return false;
}
So, it is working but behaves not as desired. The Activity seems only to react to the second touchevent and refreshes every second touchevent.
Like for example:
TouchEvent = 4 Points --> Activity shows no popup.
TouchEvent = 3 Points --> Activity shows popup with the number "4"
TouchEvent = 1 Point --> Activity shows popup with the number "3"
.
. and so on
.
I hope you can help me and I appreciate your help!
The problem seems to occour because your view is redrawn before the points are saved.
Therefore it might be better to save the number of points in the onTouch onTouchEvent and then invoke another function to set the text accordingly.
I am programming an android application and my "testing" device is a Motorola Milestone (Droid). I have done a grid that scroll like the iPhone home menu ( with "points").
I got two problems:
The first one : the drag only works on the Android Device Emulator and don't on the Droid! (Maybe the multi-touch screen is a problem?)
The drag is too responsible, and flip views sometimes one by one (this is ok) and sometimes 2 by 2 or 3 by 3! That is clearly problematic!
Here is the code of my OnTouch method:
public boolean onTouch(View v, MotionEvent event) {
if (v instanceof GridView){
int eventaction = event.getAction();
switch (eventaction) {
case MotionEvent.ACTION_MOVE:
if (event.getEdgeFlags()==MotionEvent.EDGE_LEFT){
vf2.setInAnimation(this, R.anim.slide_left);
vf2.setOutAnimation(this, R.anim.slide_right);
vf2.showNext();
if (numCurrentPage==2){
numCurrentPage= 0;
} else {
numCurrentPage++;
}
notifyPageNumber(numCurrentPage);
}
if (event.getEdgeFlags()==MotionEvent.EDGE_RIGHT){
vf2.setInAnimation(this, R.anim.slide_in);
vf2.setOutAnimation(this, R.anim.slide_out);
vf2.showPrevious();
if (numCurrentPage==0){
numCurrentPage= 2;
} else {
numCurrentPage--;
}
notifyPageNumber(numCurrentPage);
}
break;
default:
break;
}
}
return false;
}
Thanks for your help!
Update : It doesn't work anymore on the Google Nexus One!
If you consider the onTouch event being handled you should return true to indicate that it should not propagate any further. If you return false other listeners will receive it and might do something about it, which could be causing this. How about trying:
if (v instanceof GridView){
...
return true;
}
return false;
Ok, I manage to have a good drag with this:
public boolean onTouch(View v, MotionEvent event) {
if (v instanceof GridView){
int eventaction = event.getAction();
switch (eventaction) {
case MotionEvent.ACTION_DOWN:
xStart = event.getX();
break;
case MotionEvent.ACTION_UP:
xEnd = event.getX();
System.out.println("Start = "+xStart+", End = "+xEnd);
if (xEnd - xStart > 100){
vf2.setInAnimation(this, R.anim.slide_in);
vf2.setOutAnimation(this, R.anim.slide_out);
vf2.showPrevious();
if (numPageActuelle==0){
numCurrentPage= 2;
} else {
numCurrentPage--;
}
notifyPageNumber(numCurrentPage);
}
if (xEnd - xStart < -100){
vf2.setInAnimation(this, R.anim.slide_left);
vf2.setOutAnimation(this, R.anim.slide_right);
vf2.showNext();
if (numPageActuelle==2){
numCurrentPage= 0;
} else {
numCurrentPage++;
}
notifyPageNumber(numCurrentPage);
}
return true;
default:
break;
}
return true;
}
return false;
}
Now, I get an other big problem : I can't click on my grid!
The code of my grid is :
private ViewFlipper vf;
......
vf.setOnTouchListener(this);
GridView pageGrid = new GridView(this);
pageGrid.setNumColumns(3);
pageGrid.setAdapter(new ModuleAdapter(this,listPages.get(j)));
pageGrid.setOnTouchListener(this);
vf.addView(pageGrid);
I don't now how to get again the clicks on my grid...
If someone have an idea...