I am working with the ViewPager trying to accomplish some animations. One of them is trying to slide from left to right (default transition of view pager is from right to left). I have done that.
My problem is that I want to "hack" the touch event so I don't need to modify the view pager. For example, for the left to right transition, I will want to make some kind of mirroring with the X in the touch event passed to the view pager.
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
event.setX(Math.abs(event.getX() - getWidth());
return super.onInterceptTouchEvent(event);
}
I finally did it.
public boolean dispatchTouchEvent(MotionEvent event) {
MotionEvent hackedEvent = MotionEvent.obtain(event.getDownTime(),
event.getEventTime(), event.getAction(), (event.getX() - getWidth()) * -1,
event.getY(), event.getMetaState());
boolean result = super.dispatchTouchEvent(hackedEvent);
hackedEvent.recycle();
return result;
};
For people with the same issue and using Kotlin:
I created an extension function to copy the whole MotionEvent in data class style, which allows you to modify certain attributes of it.
E.g.
val modifiedEvent = event.copy(downTime = System.currentTimeMillis())
event.recycle()
Extension function:
/**
* Copies a whole MotionEvent. Use the named parameters to modify certain
values.
* Don't forget to recycle the original event (if it is not used anymore :)
)!
*/
fun MotionEvent.copy(
downTime: Long = getDownTime(),
eventTime: Long = getEventTime(),
action: Int = getAction(),
pointerCount: Int = getPointerCount(),
pointerProperties: Array<MotionEvent.PointerProperties>? =
(0 until getPointerCount())
.map { index ->
MotionEvent.PointerProperties().also { pointerProperties ->
getPointerProperties(index, pointerProperties)
}
}
.toTypedArray(),
pointerCoords: Array<MotionEvent.PointerCoords>? =
(0 until getPointerCount())
.map { index ->
MotionEvent.PointerCoords().also { pointerCoords ->
getPointerCoords(index, pointerCoords)
}
}
.toTypedArray(),
metaState: Int = getMetaState(),
buttonState: Int = getButtonState(),
xPrecision: Float = getXPrecision(),
yPrecision: Float = getYPrecision(),
deviceId: Int = getDeviceId(),
edgeFlags: Int = getEdgeFlags(),
source: Int = getSource(),
flags: Int = getFlags()
): MotionEvent =
MotionEvent.obtain(
downTime,
eventTime,
action,
pointerCount,
pointerProperties,
pointerCoords,
metaState,
buttonState,
xPrecision,
yPrecision,
deviceId,
edgeFlags,
source,
flags
)
Source: https://gist.github.com/sebschaef/b803da53217c88e8c691aeed08602193
Related
How to decrease sensitivity of onTouch ? I have a custom remote with a virtual touch pad on it. The problem is that when I move finger over the touch pad the mouse cursor moves very fast. So is there a way to decrease that movement speed ?
I have googled the problem and in some articles there is written that it is possible only if the phone is rooted.
public void onTouch(View v, MotionEvent event) {
KeyImageView mKeyImageView= (KeyImageView)v;
int width = mKeyImageView.getWidth();
int height = mKeyImageView.getHeight();
Log.d(TAG,"onTouch:"+width+" "+height+" "+RemoteIME.mService.getWidth()+" "+RemoteIME.mService.getHeigt());
int touch_x = switchTouchXY((int)event.getX(), width, RemoteIME.mService.getWidth());
int touch_y = switchTouchXY((int)event.getY(), height, RemoteIME.mService.getHeigt());
Touch mTouch = new Touch(RemoteIME.mService, event.getAction(), touch_x, touch_y);
sendCmdMessage(SendThread.CMD_SEND_TOUCH, 0, 0, mTouch);
}
public int switchTouchXY(int touch, int source, int target) {
return (touch * target / source);
}
I have implemented this spinner in my app. I'm displaying a dialog and I have my spinner in that dialog. I want my spinner to open as the dialog comes up. I have tried performClick() event right after setAdapter() method but it doesn't seem to work.
My code looks like this:
final MaterialBetterSpinner selectBrandForSODRating = dialog.findViewById(R.id.selectBrandForSODspinner);
final ArrayList<Products> brandList = new ArrayList<Products>();
Cursor crsCheckSODData = database.rawQuery(myQuery, null);
if(crsCheckSODData.getCount() > 0){
while (crsCheckSODData.moveToNext()) {
//data...
brandList.add(data);
}
}
crsCheckSODData.close();
final ArrayAdapter<Products> SODBrandAdapter = new ArrayAdapter<Products>(myView.this, android.R.layout.simple_dropdown_item_1line, brandList);
selectBrandForSODRating.setAdapter(SODBrandAdapter);
selectBrandForSODRating.performClick(); //this right here isnt working...
If you are using https://github.com/Lesilva/BetterSpinner, the code is overriding onTouchEvent here, but not overriding onClick. That's why performClick will not work.
You could simulate touch event instead to the view. Here's how:
int[] coords = new int[2];
selectBrandForSODRating.getLocationOnScreen(coords);
float x = (float) coords[0];
float y = (float) coords[1];
// List of meta states found here: developer.android.com/reference/android/view/KeyEvent.html#getMetaState()
int metaState = 0;
// Obtain MotionEvent object
long downTime1 = SystemClock.uptimeMillis();
long eventTime1 = SystemClock.uptimeMillis() + 100;
MotionEvent motionEventDown = MotionEvent.obtain(
downTime1,
eventTime1,
MotionEvent.ACTION_DOWN,
x,
y,
metaState
);
// Dispatch touch event to view
selectBrandForSODRating.dispatchTouchEvent(motionEventDown);
long downTime2 = SystemClock.uptimeMillis();
long eventTime2 = SystemClock.uptimeMillis() + 100;
MotionEvent motionEventUp = MotionEvent.obtain(
downTime2,
eventTime2,
MotionEvent.ACTION_UP,
x,
y,
metaState
);
// Dispatch touch event to view
selectBrandForSODRating.dispatchTouchEvent(motionEventUp);
You could also directly call the methods from here
selectBrandForSODRating.requestFocus();
selectBrandForSODRating.showDropDown();
But I don't recommend it, because there is another boolean state, isPopUp, that has private access and you can't access.
How to show a response while gesture input is in progress?
I'm using swipe down gesture to reload my WebView.
Using this for gesture detection:How to detect swipe direction between left/right and up/down
Problem
SimpleGestureListener captures the result of user input only. It cannot be used to show a response, e.g. animation, while the user is performing a gesture.
Imperfect inelegant solution:
flaw: Shows animation independent of SimpleOnGestureListener; a response to user gesture input is displayed and gesture input may still fail or vice versa.
private volatile float userTouchY = -1;
// translating WebView every onTouch event call is inefficient.
// only translate when translateYBy value is greater than a threshold.
private float translateYBy = 0;
/**
* Shift webView without animation.
* #param num
*/
private void __shiftWebViewDownBy(int num){
float current = webView.getY();
if(current >= webviewTranslationLimit) return;
current += num;
if(current> webviewTranslationLimit) current = webviewTranslationLimit;
webView.setY(current);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
//Skip everything unless at the content top.
if(webView.getScrollY() > 0) return false;
int action = event.getAction();
if(action == MotionEvent.ACTION_MOVE){
float y = event.getY();
float dif = y - userTouchY;
if(dif < webviewTranslationLimit/10){
//dif less than screenHeight*1/40, ignore input
}else if(dif < -(webviewTranslationLimit/5)){
//swipe up
userTouchY = -1;//not swipe down cancelling
__shiftWebViewTopTo(0);
}else if(userTouchY < y) {
//swipe down
translateYBy += dif;
if(userTouchY == -1){
//userTouchY at the initial value, ignore for this once.
userTouchY = y;
}else{
userTouchY = y;
if(translateYBy > 5 ){
__shiftWebViewDownBy((int)translateYBy);
translateYBy = 0;
}
}
}
}else{
Log.d(TAG,"action not move:" +MotionEvent.actionToString(action));
// Webview shift down should only occur while moving
// Once finger is off,cancel
__shiftWebViewTopTo(0);
userTouchY = -1;
}
boolean result = gestureDetector.onTouchEvent(event);
return result;
}
Writing a GestureListener of a sort from scratch obviously solves the problem and I can simply use Toast to message the user for a failed gesture input, but there ought to be an easier, more readable solution.
I am developing an android application where I am creating dynamic Images arrow on relative layout. The images are created on a x,y coordinated of the click area of relative layout. Below is the code the I am using for it.
presciptionScreenArrowImg.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (canSelectMedianStatus == 2) {
if (event == simulationEvent)
return false;
int action = event.getAction();
int x = (int) event.getX();
int y = (int) event.getY();
Log.e("onTouchListener", "User touch at X:" + x + " Y:" + y);
pointerArrow = new ImageView(getApplication());
pointerArrow.setImageResource(R.drawable.pointer);
pointerArrow.setId(imageArrayTag);
imageArrayTag++;
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(40, 40);
params.topMargin = y;
params.leftMargin = x;
pointerArrow.setLayoutParams(params);
presciptionScreenArrowImg.addView(pointerArrow);
long length = 0;
if (action == MotionEvent.ACTION_DOWN) {
// click(v, x, y);
}
}
return false;
}
});
Now, I need is there on button click the last Image drawn should remove first. Basically I need an undo functionality to remove Images as LIFO structure.
let consider presciptionScreenArrowImg is your main layout which contains your all views so for removing
int index=presciptionScreenArrowImg.getChildCount();
if(index>0)
presciptionScreenArrowImg.removeViewAt(index-1);
from above get count of child and remove last view if you have any problem let me know
Store the views in a Queue, and when you add a new view check if the queue is full. If it is, pop a view from the queue, and call presciptionScreenArrowImg.remove(poppedView);
I've got a sprite that rotates and when touch input is released, it rotates back to 0 degrees quickly. How can I get the rotation (degrees or otherwise) of the sprite just before the touch input is released?
I've looked and can't find any way to achieve, tough question to google.
EDIT Sorry for the latent response. Here is my code so far, will I be able to use the rPower variable to direct a projectile? Haven't gotten that far yet.
#Override
public boolean touchDown(int x, int y, int pointer, int button) {
if (Gdx.input.isTouched(0)) {
cam.unproject(touchPoint.set(Gdx.input.getX(), Gdx.input.getY(), 0));
}
if (Gdx.input.isTouched(1)) {
cam.unproject(touchPoint2.set(Gdx.input.getX(), Gdx.input.getY(), 0));
}
return true;
}
#Override
public boolean touchDragged(int x, int y, int pointer) {
if (Gdx.input.isTouched(0)) {
cam.unproject(dragPoint.set(Gdx.input.getX(), Gdx.input.getY(), 0));
dx = touchPoint.x - dragPoint.x;
dy = touchPoint.y - dragPoint.y;
throwerLowerArmSprite.setRotation(dx * 30);
}
if (Gdx.input.isTouched(1)){
cam.unproject(dragPoint2.set(Gdx.input.getX(), Gdx.input.getY(), 0));
d1x = dragPoint2.x - touchPoint2.x;
d1y = dragPoint2.y - touchPoint2.y;
throwerUpperArmSprite.setRotation(d1x * 30);
}
return true;
}
#Override
public boolean touchUp(int x, int y, int pointer, int button) {
if (!Gdx.input.isTouched(0)) {
cam.unproject(releasePoint.set(Gdx.input.getX(), Gdx.input.getY(), 0));
rPower = releasePoint.x - touchPoint.x;
throwerLowerArmSprite.setRotation(0);
}
if (!Gdx.input.isTouched(1)) {
cam.unproject(releasePoint2.set(Gdx.input.getX(), Gdx.input.getY(), 0));
rPower = releasePoint2.x - touchPoint2.x;
throwerUpperArmSprite.setRotation(0);
}
return true;
}
Your sprite rotates back to 0 degrees because of this in the touchUp method:
throwerLowerArmSprite.setRotation(0);
All the objects with setRotation methods also have a getRoatation() method. So you can save the current rotation with:
float oldRotation = mySprite.getRotation();
Unrelated to the question, but you can simplify all of your input event callbacks. You're mixing in the event polling methods to lookup data that the event callback already provides. For example, your touchDown method could use its parameters like this:
public boolean touchDown(int x, int y, int pointer, int button) {
if (pointer == 0) {
cam.unproject(touchPoint.set(x, y, 0));
} else if (pointer == 1) {
cam.unproject(touchPoint2.set(x, y, 0));
}
return true;
}
This is even more useful in your touchUp method where the pointer parameter will tell you what Gdx.input.isTouched cannot (namely which pointer is the one that is no longer touching the screen).