I have an activity that allows users to swipe between 3 different views. Each view displays a list of images. The images have onClick events that call a new activity and makes the images full screen.
This all works ok however if I try to swipe between the 3 different views and my finger swipes over an image it will trigger the onClick event and open the image fullscreen. So I am wondering how can I put priority on onTouch (for the page/view swapping) ?
(the onTouch event uses a gestureListener and i'm using onFling and flipper methods..)
(the onClick is simply a imageView.setOnClickListener(new OnClickListener() )
If you return true from your onFling(…) it should consume the event and stop propagation.
You can find here solution for delegating a touch from parent to child view, if you need more info.
mDrawerLayout.post(() -> {
Rect delegateArea = new Rect();
mCalendarView.getHitRect(delegateArea);
delegateArea.right += 100;
delegateArea.bottom += 100;
TouchDelegate touchDelegate = new TouchDelegate(delegateArea, mCalendarView);
if (View.class.isInstance(mCalendarView.getParent())) {
((View) mCalendarView.getParent()).setTouchDelegate(touchDelegate);
}
});
Related
I am trying to change the value of a number based on sliding my finger. I am currently using ACTION_MOVE to change the value when I drag across my view but if I drag to fast the number barely changes. If I drag slowly I can get the correct number.
Is there a way to make the change quicker depending on the speed of the motion. I am looking into Velocity Tracker but this only returns the speed of the move and I need to make the change while dragging my finger.
Is there an optimization needed to detect ACTION_MOVE in real-time?
More like pseudo code right now, but Here is what I would do:
Extend the image view to check if the event x value is between the starting x value of the image view and the width as such:
private void checkFingerPosition(int eventXPosition) {
if(eventXPosition > this.X && eventXPosition < this.x + this.getWidth())
imageInterface.showImage(this.getDrawable());
}
I would create the interface that your activity would have to implement
public interface ImageInterface {
public void showImage(Drawable drawable);
}
In your activity, implement the ImageInterface like so
implements ImageInterface
#Override
public void showImage(Drawable drawable) {
//TODO - show drawable here
}
Then take your touch event ACTION_MOVE, report the x value to all you image views like so:
ACTION_MOVE:
for(ExtendedImageView extendedImageView : ExtendedImageViewArray) {
entendedImageView.checkFingerPosition(event.X);
}
Given an arbitrary ViewGroup G with an arbitrary collection of child views, how can I detect when the user clicks on any of the child views? In this case, I want to draw a highlight for G.
I could add an onClick listener for each child, but I'm trying to avoid that so that the code doesn't have to be changed when the layouts change.
Alternatively, I could add onTouch handlers to G and set the highlight during ACTION_DOWN. However, this would trigger for actions that don't actually result in clicks, such as a swipe (the swipe could be handled by ViewPager, for example, and ultimately be irrelevant to G).
My layout for G has the focusable attributes:
android:focusable="true"
android:focusableInTouchMode="true"
Thanks.
Here is how I do it:
//in onTouch method of parent, I get the coordinates of click
int x = ((int) motionEvent.getX());
int y = ((int) motionEvent.getY());
//obtain the clickable arrea of the child as HitRect
Rect clickRect = new Rect();
Rect rect = new Rect();
imageView.getHitRect(rect);
//ask if the area contains the coordinates of the click
if(rect.contains(x, y)){
//do some work like if onClickListener on the child was called.
return false; //you clicked here, don't need to handle other Childs
}
//ask for other childs like before...
Now, you can target the parent as the delegate of all clicks done inside it, even if it is done in a child.
EDIT:
To ignore other touch event that are not click, you can ask for how much user moved the finger:
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_CANCEL:
if (Math.abs(motionEvent.getRawX() - initialTouchX) > 5 || Math.abs(motionEvent.getRawY() - initialTouchY) > 5) {
return true; // user mover finger too much, ignore touch
}
return false; // finger still there waiting for click
I give a square of 10 pixels to permit a confortable click, and if you exit it, I ignore it.
EXTRA:
Here is the complete code for click and long click with onTouchListener.
You could use the View.getChildCount() to loop through all child views and see if the touch intersects with the child view.
This involves getting x and y positions and calculating if it fits within the child view, use View.getChildAt(position) to get the reference to the child view .
So it would be something like this:
int childNr = theView.getChildCount();
for (int i = 0; i < childNr; i++){
YourView tmp = (YourView) theView.getChildAt(i);
if(tmp.intersects(x, y)){
do some work
}
}
here you would have to put your view variable instead of theView and the class name which handles the views instead of (YourView) and x, y are the coordinates of the pressed spot.
In your XML, you could add point all the children to the same onClick method. Inside that method you could draw the highlight to G and then do something (or nothing) for the individual child view.
In the run-time, I added a new ImageView and attached a listener, like so:
image = new ImageView(someAppContext());
relativeLayout.addView(image, someLayoutParam);
// add onTouch listener to the image view:
image.setOnTouchListener(new ImageView.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
makeToast("touched!");
return false; // tried return true, makes no difference
}
});
After that, I used TranslateAnimation with setFillAfter(true), so the ImageView would stay in the new position. However, the onTouch() is triggered if I touch the OLD position of the ImageView, but NOT the new position where the image visually is. How do I make the touch-sensitive hotspot move along with the ImageView itself? I also tried to invalidate(), doesn't seem to help.
Animation does not actually move Views. Translation animation only adds an offset to its coordinates.
I think the best workaround is to clear all the animations, and update layout parameters in order to move the actual ImageView over to the destination of the animation.
You need to store the destination of your translation, say dest_x, dest_y, and after the animation is done, do:
someLayoutParam.leftMargin = dest_x;
someLayoutParam.topMargin = dest_y;
yourImageView.clearAnimation(); // resets its position
yourRelativeLayout.updateViewLayout(yourImageView, someLayoutParam);
In this way, the location of the image view does not change, but the actual view is moved, so the touchable area is moved, too.
I have a listview in a fragment container.
Each view within the list has a gesture detector which on gesture up invalidates the list for redraw.
After an item selection I animate the fragment container to another part of the screen
ObjectAnimator x = ObjectAnimator.ofInt(ListLayout, "left", ListLayout.getLeft(), ListLayout.getLeft() - 336);
x.setDuration(500);
x.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
View ListLayout = findViewById(R.id.fragment_list_container);
Log.d(TAG, "ListLayout x after move completed = " + ListLayout.getLeft());
}
});
x.start();
All good to this point.
However if I select another item from the list (list should not move this time so animation not called) when the gesture up is detected and the list invalidated it reverts to it's original position?
If I getLeft before and after the initial animated move it returns the correct positions.
Any help or insight gratefully received,
Slip
For those that run into this problem it was simply because I was using "left" rather than translating the value.
I ended up compacting everything and using the new feature
mLayout.animate().setDuration(ANIMATE_TIME_MILLIS).translationXBy(ANIMATE_MOVE_DISTANCE);
I have a ToggleButton. I want to have more place for click on this button. If I add layout_width, layout_height etc. the image looks bad. I also tried using android:padding but it did not help me.
It is needed for the convenience of users.
Easy solution : If you have image for the button, then create transparent area around it (i.e. for touch area).
The grey area can be transparent to increase the touch area.
Or use TouchDelegate
You can also increase touch area by setting touch delegates Android Developer Training Blog Post
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Get the parent view
View parentView = findViewById(R.id.parent_layout);
parentView.post(new Runnable() {
// Post in the parent's message queue to make sure the parent
// lays out its children before you call getHitRect()
#Override
public void run() {
// The bounds for the delegate view (an ImageButton
// in this example)
Rect delegateArea = new Rect();
ImageButton myButton = (ImageButton) findViewById(R.id.button);
myButton.setEnabled(true);
myButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(MainActivity.this,
"Touch occurred within ImageButton touch region.",
Toast.LENGTH_SHORT).show();
}
});
// The hit rectangle for the ImageButton
myButton.getHitRect(delegateArea);
// Extend the touch area of the ImageButton beyond its bounds
// on the right and bottom.
delegateArea.right += 100;
delegateArea.bottom += 100;
// Instantiate a TouchDelegate.
// "delegateArea" is the bounds in local coordinates of
// the containing view to be mapped to the delegate view.
// "myButton" is the child view that should receive motion
// events.
TouchDelegate touchDelegate = new TouchDelegate(delegateArea,
myButton);
// Sets the TouchDelegate on the parent view, such that touches
// within the touch delegate bounds are routed to the child.
if (View.class.isInstance(myButton.getParent())) {
((View) myButton.getParent()).setTouchDelegate(touchDelegate);
}
}
});
}
}
Use TouchDelegate for your ToggleButton as ammar26 have commented you.
Or
Try this:
Make one parent layout like LinearLayout or RelativeLayout that cover the ToggleButton. And now put margin to that Parent layout.
Now, on click of that Parent layout do action for the toggle button.
Hope it will help you to increase touch area for your view.
Happy Coding.
Instead of putting the touch event in button put it in the layout containg the only the button..and fix the size of the layout as ur wish
increase the values of android:padding:
<SeekBar android:id="#+id/seek" android:layout_width="0dip"
android:layout_height="wrap_content" android:layout_weight="1"
android:paddingTop="5dp" android:paddingBottom="5dp"
android:progressDrawable="#drawable/green_scrubber_progress_horizontal_holo_light"
android:thumb="#drawable/thumb" />
Take the margin (place of padding) of your Button from its parent layout and then perform opration on your Layout
mLayout.setonTouchListener(View.onTouchListener{
// here your working code
});