Android: How do I place a ScrollView (with text in) into Gallery? - android

I am going to place text in gallery in android app. The text is in a scrollView. Everything works fine but on draging the text towards right or left, no next page/ element shows (The screen remains still).If i have some thing outside the scrollView, it changes to the next element on dragging.
Can anyone help?

You need to override onInterceptTouchEvent - you can use this to get the MotionEvents before they are delivered to the ScrollView, and then redirect them to your ViewGroup (Gallery in this case) if you wish.
The following class redirects MotionEvents to your Gallery if the user moves their finger too far left or right. Also if the user moves their finger up or down quite a bit then moving their finger left or right will have no longer have an effect, so you don't have to worry about the Gallery changing while doing a lot of scrolling.
class ScrollViewGallery extends Gallery {
/**
* The distance the user has to move their finger, in density independent
* pixels, before we count the motion as A) intended for the ScrollView if
* the motion is in the vertical direction or B) intended for ourselfs, if
* the motion is in the horizontal direction - after the user has moved this
* amount they are "locked" into this direction until the next ACTION_DOWN
* event
*/
private static final int DRAG_BOUNDS_IN_DP = 20;
/**
* A value representing the "unlocked" state - we test all MotionEvents
* when in this state to see whether a lock should be make
*/
private static final int SCROLL_LOCK_NONE = 0;
/**
* A value representing a lock in the vertical direction - once in this state
* we will never redirect MotionEvents from the ScrollView to ourself
*/
private static final int SCROLL_LOCK_VERTICAL = 1;
/**
* A value representing a lock in the horizontal direction - once in this
* state we will not deliver any more MotionEvents to the ScrollView, and
* will deliver them to ourselves instead.
*/
private static final int SCROLL_LOCK_HORIZONTAL = 2;
/**
* The drag bounds in density independent pixels converted to actual pixels
*/
private int mDragBoundsInPx = 0;
/**
* The coordinates of the intercepted ACTION_DOWN event
*/
private float mTouchStartX;
private float mTouchStartY;
/**
* The current scroll lock state
*/
private int mScrollLock = SCROLL_LOCK_NONE;
public ScrollViewGallery(Context context) {
super(context);
initCustomGallery(context);
}
public ScrollViewGallery(Context context, AttributeSet attrs) {
super(context, attrs);
initCustomGallery(context);
}
public ScrollViewGallery(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
initCustomGallery(context);
}
private void initCustomGallery(Context context) {
final float scale = context.getResources().getDisplayMetrics().density;
mDragBoundsInPx = (int) (scale*DRAG_BOUNDS_IN_DP + 0.5f);
}
/**
* This will be called before the intercepted views onTouchEvent is called
* Return false to keep intercepting and passing the event on to the target view
* Return true and the target view will recieve ACTION_CANCEL, and the rest of the
* events will be delivered to our onTouchEvent
*/
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
mTouchStartX = ev.getX();
mTouchStartY = ev.getY();
mScrollLock = SCROLL_LOCK_NONE;
/**
* Deliver the down event to the Gallery to avoid jerky scrolling
* if we decide to redirect the ScrollView events to ourself
*/
super.onTouchEvent(ev);
break;
case MotionEvent.ACTION_MOVE:
if (mScrollLock == SCROLL_LOCK_VERTICAL) {
// keep returning false to pass the events
// onto the ScrollView
return false;
}
final float touchDistanceX = (ev.getX() - mTouchStartX);
final float touchDistanceY = (ev.getY() - mTouchStartY);
if (Math.abs(touchDistanceY) > mDragBoundsInPx) {
mScrollLock = SCROLL_LOCK_VERTICAL;
return false;
}
if (Math.abs(touchDistanceX) > mDragBoundsInPx) {
mScrollLock = SCROLL_LOCK_HORIZONTAL; // gallery action
return true; // redirect MotionEvents to ourself
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
// if we're still intercepting at this stage, make sure the gallery
// also recieves the up/cancel event as we gave it the down event earlier
super.onTouchEvent(ev);
break;
}
return false;
}
}

I understand your question. This situation is already handle by me after overriding method. I am not sure this will be the best solution, may be some one have more efficient solution then this, But it works for me.
I used customised object of gallery with the over ridden method.
public boolean isScrollingLeft(MotionEvent e1, MotionEvent e2) {
return e2.getX() > e1.getX();
}
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
return super.onFling(e1, e2, 0, velocityY);
}

I found that if you place a textview in a vertical scrollbar mode it only moves up or down. If we want to swipe horizontally, you have to touch outside of the textview.

Related

How do I implement onDragListener in an object within a fragment?

My app has a class, MarkedLine, which extends View. An instance of this class is shown in a Fragment. I want users to be able to do the following 3 things:
Enlarge the line by doing "pinch" and "stretch" gestures
Touch any point of the line and get its coordinates
Move the line around
I have the first two working, but can't figure out the third one (the dragging).
Each MarkedLine consists of a horizontal line of boxes, some of which are coloured in. The user can zoom in by stretching, and tap a box to change its colour; I also want them to be able to move the line around the screen, because when it's zoomed in it will go off the edges of the screen.
The basic fragment layout (fragment_marked_line) is as follows (I've removed irrelevant bits, padding, margins etc.):
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:res="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<packagepath.models.ToolboxButton
android:id="#+id/toolbarNext"
android:layout_width="#dimen/toolbar_icon_size"
android:layout_height="#dimen/toolbar_icon_size"
android:src="#drawable/next_line"
res:layout_constraintTop_toTopOf="parent" />
<packagepath.models.MarkedLine
android:id="#+id/markedLine"
android:layout_width="0dp"
android:layout_height="wrap_content"
res:layout_constraintStart_toStartOf="parent"
res:layout_constraintEnd_toEndOf="parent"
res:layout_constraintTop_toBottomOf="#+id/toolbarNext" />
</android.support.constraint.ConstraintLayout>
(So basically it's a button with a full-width line underneath it. The button allows the user to bring up the next line).
The Fragment code (MarkedLineFragment) is as follows (n.b. A LineSet is basically just an array of MarkedLines, with a few extra variables like when it was created, the line dimensions etc):
public class MarkedLineFragment extends Fragment {
LineSet mLineSet
MarkedLine mMarkedLine;
ToolboxButton btn_next;
int mItemNumber, mMaxItems;
public MarkedLineFragment() {}
#Override
public View onCreateView(#NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View rootView = inflater.inflate(
R.layout.fragment_marked_line, container, false);
// Get view objects
btn_next = rootView.findViewById(R.id.toolbarNext);
mMarkedLine = rootView.findViewById(R.id.markedLine);
// Initialise the button
initialise_button();
// If the LineSet has already been set,
// pass it through to the MarkedLine
if(mLineSet != null) {
mMarkedLine.setLineSet(mLineSet);
mMaxItems = mLineSet.getNum_items();
}
// Initialise at line 1
mItemNumber = 1;
mMarkedLine.setCurrentItem(mItemNumber);
// Draw the MarkedLine
drawLine();
return rootView;
}
// Initialise the button so that it moves to the next line on clicking
public void initialise_button() {
btn_next.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(mItemNumber == mMaxItems) return;
else mItemNumber += 1;
set_new_item_number();
}
});
}
private void set_new_item_number() {
mMarkedLine.setCurrentItem(mItemNumber);
}
public void drawChart() {
if(mMarkedLine != null) mMarkedLine.postInvalidate();
}
}
Finally, MarkedLine class (I've left out the details of how the line is drawn, because I don't think it's relevant, and it's quite long - but I can add it in if needed):
public class MarkedLine extends View {
private LineSet mLineSet;
private int currentItem;
private int numBoxes;
private float canvas_height, canvas_width;
private float box_size;
private float minX, maxX, minY, maxY;
// Scaling (pinch & zoom) variables
private float scaleFactor = 1.0f; // Current scale factor
private ScaleGestureDetector detectorScale;// Detector for gestures
private GestureDetector detectorTouch; // Detector for tap gestures
public MarkedLine(Context thisContext, AttributeSet attrs) {
super(thisContext, attrs);
detectorScale = new ScaleGestureDetector(thisContext, new MarkedLine.ScaleListener());
detectorTouch = new GestureDetector(thisContext, new MarkedLine.TouchListener());
}
public void setCallback(OnBoxTouched callback) { mCallback = callback; }
public void setLineSet(LineSet lineSet) {
mLineSet = lineSet;
numBoxes = mLineSet.getNum_boxes();
invalidate();
}
public void setCurrentItem(int newItemNumber) {
currentItem = newItemNumber;
invalidate();
}
protected void onDraw(Canvas canvas) {
if (mLineSet == null) return;
// Set up canvas
canvas.save();
canvas.scale(scaleFactor, scaleFactor);
canvas.translate(translateX / scaleFactor, translateY / scaleFactor);
// draw_boxes reads how many boxes make up the MarkedLine,
// calculates what size they need to be to fit on the canvas,
// and then draws them
draw_boxes();
// fill_in_line adds in the appropriate colours to the
// boxes in the line
fill_in_line();
canvas.restore();
}
// GRID EVENT FUNCTIONS - respond to User touching screen
// onTouchEvent
// User has touched the screen - trigger listeners
#Override
public boolean onTouchEvent(MotionEvent event) {
detectorScale.onTouchEvent(event);
detectorTouch.onTouchEvent(event);
invalidate();
return true;
}
// LISTENERS
/*
* Respond to user touching the screen
*/
private class TouchListener extends GestureDetector.SimpleOnGestureListener {
public boolean onSingleTapUp(MotionEvent event) {
// Determine where the screen was touched
float xTouch = event.getX();
float yTouch = event.getY();
// Check that the touch was within the line; return if not
if(!touch_in_line(xTouch, yTouch)) return false;
// Figure out which Box was tapped
int xCell = getTouchedBox(xTouch);
// Now the box which was tapped is coloured in
colour_box(xCell);
return true;
}
}
/*
* Determine scale factor for zoom mode
* This can be called in View and Edit Activities
*/
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
float MIN_ZOOM = 1f; // Minimum zoom level
float MAX_ZOOM = 5f; // Maximum zoom level
scaleFactor *= detector.getScaleFactor();
scaleFactor = Math.max(MIN_ZOOM, Math.min(scaleFactor, MAX_ZOOM));
return true;
}
}
}
This all works fine. The user can stretch the line to make the boxes bigger/smaller, and then tap on any of the boxes on the line to colour them in. However, I can't get the box to move around the screen when the user drags their finger on it.
I assume I need to add an onDragListener to something, but I can't figure out what. I tried having a DragListener class, similar to the ScaleListener and TouchListener classes, with an onDrag method (I just has a couple of dummy lines so I could attach a breakpoint). Then I declared an instance of that class (dragListener). I tried attaching it in the MarkedLine constructor using this.onDragListener(dragListener) but it didn't respond to dragging.
Then I attempted something similar in the Fragment, attaching it to the mMarkedLine class in onCreateView, but again it didn't respond when I tried to drag.
I've read the Android documentation, which suggested the onDragListener class, but I'm clearly doing something wrong.
In case it helps anyone else, I fixed this by adding a check for dragging in the onTouchEvent of the MarkedLine class:
#Override
public boolean onTouchEvent(MotionEvent event) {
detectorScale.onTouchEvent(event);
detectorTouch.onTouchEvent(event);
// Check for drag gestures
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startX = event.getX() - previousTranslateX;
startY = event.getY() - previousTranslateY;
break;
case MotionEvent.ACTION_UP:
previousTranslateX = translateX;
previousTranslateY = translateY;
break;
case MotionEvent.ACTION_MOVE:
translateX = event.getX() - startX;
translateY = event.getY() - startY;
break;
}
invalidate();
return true;
}

Disable viewpager when touching/dragging certain view

Using something similar to the answer of this question, I've tried to disable the ViewPager swipe action for when the user is swiping over a particular item. The view in question is a scrollable chart from the MPAndroidChart library, so naturally I don't want the view pager interfering with the scrolling of the chart.
The issue I am having is that the ViewPager will often have "onInterceptTouch" invoked before the onTouchListener is invoked on my desired view.
In this segment of code, I'm recording when the view is pressed/unpressed:
private long lastDown;
private long lastUp;
...
public void foo(){
barChart.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
System.out.println("AAA");
if(event.getAction() == MotionEvent.ACTION_DOWN){
lastDown = System.currentTimeMillis();
}else if(event.getAction() == MotionEvent.ACTION_UP){
lastUp = System.currentTimeMillis();
}
return false;
}
});
}
In this segment of code, I determine if the view is selected:
public boolean isGraphTouched(){
return lastDown > lastUp;
}
And in this segment of code I'm overriding the onInterceptTouchEvent method:
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
System.out.println("BBB");
return isGraphSelected() ? super.onInterceptTouchEvent(ev) : false;
}
And if you take note of the printlines, the onInterceptTouchEvent method is called before...
The only way I can think of getting around this is to make a method which checks if the graph exists at the coordinates of the motion event (although I'm not sure if this is even possible) and then use that to determine if the pager will be swipable or not.
I managed to make it work using the function parent.requestDisallowInterceptTouchEvent(true); being placed inside the child view onTouchEvent(). This way the View does not allow none of his parents to interecpt his touch events in case a scroll happened and was to be handled by the ViewPager.
However in my case I had a ViewPager with a draggable custom views inside it which I wanted to move without that the ViewPager changes page.
My solution in terms of code: (Kotlin)
view.setOnTouchListener { v, event ->
parent.requestDisallowInterceptTouchEvent(true);
//Drag and drop handling is here and the rest of the event logic
}
I hope this will help you as well.
I've managed to solve my problem by incorporating this answer from another post. The code allows you to get the coordinates of a given view and compare them to your own coordinates.
Inside of my CustomViewPager class, I implemented what I mentioned above:
private boolean isPointInsideView(float x, float y, View view) {
int location[] = new int[2];
view.getLocationOnScreen(location);
int viewX = location[0];
int viewY = location[1];
return ((x > viewX && x < (viewX + view.getWidth())) && (y > viewY && y < (viewY + view.getHeight())));
}
I then had a method which returns a boolean, which checks a couple of conditions and returns true/false on if the pager should be able to be swiped:
public boolean canSwipe(float x, float y) {
boolean canSwipe = true;
if (launchActivity.isReady(MainScreenPagerAdapter.STAT_PAGE)) {
FragmentStatistics fragmentStatistics = (FragmentStatistics) launchActivity.getPageAdapter().instantiateItem(this, MainScreenPagerAdapter.STAT_PAGE);
View chart = fragmentStatistics.getView().findViewById(R.id.chart);
canSwipe = !isPointInsideView(x, y, chart) || !fragmentStatistics.isGraphPannable();
}
return canSwipe;
}
And then, of course, I overwrote the onInterceptTouchEvent like so:
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return canSwipe(ev.getX(), ev.getY()) ? super.onInterceptTouchEvent(ev) : false;
}
And now, the graph can be fully panned without the Pager interfering with it at all.
Full CustomViewPager code:
public class CustomViewPager extends ViewPager {
/** Reference to the launch activity */
private LaunchActivity launchActivity;
/**
* Constructor to call the super constructor
*
* #param context The application context
* #param attrs The attributes
*/
public CustomViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* Sets object reference for the {#code launchActivity}
*
* #param launchActivity The LaunchActivity to be set
*/
public void set(LaunchActivity launchActivity) {
this.launchActivity = launchActivity;
}
/**
* Determines if the pager can be swiped based off the x and y inputs provided, as well as if the
* barchart can be panned or not.
*
* #param x The x coordinate to check
* #param y The y coordinate to check
* #return True if the ViewPager will continue with normal swiping action.
*/
public boolean canSwipe(float x, float y) {
boolean canSwipe = true;
if (launchActivity.isReady(MainScreenPagerAdapter.STAT_PAGE)) {
FragmentStatistics fragmentStatistics = (FragmentStatistics) launchActivity.getPageAdapter().instantiateItem(this, MainScreenPagerAdapter.STAT_PAGE);
View chart = fragmentStatistics.getView().findViewById(R.id.chart);
canSwipe = !isPointInsideView(x, y, chart) || !fragmentStatistics.isGraphPannable();
}
return canSwipe;
}
/**
* Takes x and y coordinates and compares them to the coordinates of the passed view. Returns true if the passed coordinates
* are within the range of the {#code view}
*
* #param x The x coordinate to compare
* #param y The y coordinate to compare
* #param view The view to check the coordinates of
* #return True if the x and y coordinates match that of the view
*/
private boolean isPointInsideView(float x, float y, View view) {
int location[] = new int[2];
view.getLocationOnScreen(location);
int viewX = location[0];
int viewY = location[1];
// point is inside view bounds
return ((x > viewX && x < (viewX + view.getWidth())) && (y > viewY && y < (viewY + view.getHeight())));
}
/**
* Override of the onInterceptTouchEvent which allows swiping to be disabled when chart is selected
*
* #param ev The MotionEvent object
* #return Call to super if true, otherwise returns false
*/
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return canSwipe(ev.getX(), ev.getY()) ? super.onInterceptTouchEvent(ev) : false;
}
}

Check if Android WebView is consuming touch events

TL;DR
How can I detect whether Android WebView consumed a touch event? onTouchEvent always returns true and WebViewClient's onUnhandledInputEvent is never triggered.
Detailed description
I have multiple WebViews inside a TwoDScrollView. As its name suggests, the TwoDScrollView can be scrolled both vertically and horizontally. The contents of TwoDScrollView can be zoomed in / out. When the user drags his finger or uses pinch-to-zoom, I want to dispatch the touch event to:
WebView if its content is scrollable / zoomable (i.e. only the inside of the WebView will scroll / zoom)
TwoDScrollView if the above condition is false (all contents of the TwoDScrollView will scroll / zoom)
I have partially achieved this by using the canScrollHorizontally and canScrollVertically methods. But these methods only work for "native scrolling". However, in some cases, some JavaScript inside the WebView consumes the touch event, for example Google Maps. In this case, the methods return false. Is there any way to find out whether the WebView's contents consumes the touch events, i.e. is scrollable / zoomable? I cannot change the contents of the WebView, therefore my question is different from this one.
I have considered checking touch handlers by executing some JavaScript inside the Webview by the evaluateJavaScript method, but according to this answer there is no easy way to achieve this and also the page can have some other nested iframes. Any help will be appreciated.
What I've already tried
I overrode WebView's onTouchEvent and read super.onTouchEvent() which always returns true, no matter what.
canScrollHorizontally and canScrollVertically only partially solve this problem, as mentioned above
onScrollChanged isn't useful either
WebViewClient.onUnhandledInputEvent is never triggered
I considered using JavaScript via evaluateJavaScript, but it is a very complicated and ugly solution
I tried to trace the MotionEvent by Debug.startMethodTracing. I found out it is propagated as follows:
android.webkit.WebView.onTouchEvent
com.android.webview.chromium.WebViewChromium.onTouchEvent
com.android.org.chromium.android_webview.AwContents.onTouchEvent
com.android.org.chromium.android_webview.AwContents$AwViewMethodsImpl.onTouchEvent
com.android.org.chromium.content.browser.ContentViewCore.onTouchEventImpl
According to ContentViewCore's source code the touch event ends up in a native method nativeOnTouchEvent and I don't know what further happens with it. Anyway, onTouchEvent always returns true and even if it was possible to find out somewhere whether the event was consumed or not, it would require using private methods which is also quite ugly.
Note
I don't need to know how to intercept touch events sent to WebView, but whether the WebView is consuming them, i.e. is using them for doing anything, such as scrolling, dragging etc.
According to this issue report, not possible.
If the web code is under your control, you can implement some JavaScriptInterface to workaround this. If not, I am afraid there is no solution here.
You can pass all touch events to GestureDetector by overriding onTouchEvent of WebView, so you can know when Android WebView is consuming touch events anywhere, anytime by listening to GestureDetector.
Try like this:
public class MyWebView extends WebView {
private Context context;
private GestureDetector gestDetector;
public MyWebView(Context context) {
super(context);
this.context = context;
gestDetector = new GestureDetector(context, gestListener);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
return gd.onTouchEvent(event);
}
GestureDetector.SimpleOnGestureListener gestListener= new GestureDetector.SimpleOnGestureListener() {
public boolean onDown(MotionEvent event) {
return true;
}
public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX, float velocityY) {
//if (event1.getRawX() > event2.getRawX()) {
// show_toast("swipe left");
//} else {
// show_toast("swipe right");
//}
//you can trace any touch events here
return true;
}
};
void show_toast(final String text) {
Toast t = Toast.makeText(context, text, Toast.LENGTH_SHORT);
t.show();
}
}
I hope you be inspired.
This code will handle your scrolling events in a webview. This catch the click down and the click up events, and compares the positions of each one. It never minds that the content within the webview is scrollable, just compare the coordinates in the area of webview.
public class MainActivity extends Activity implements View.OnTouchListener, Handler.Callback {
private float x1,x2,y1,y2; //x1, y1 is the start of the event, x2, y2 is the end.
static final int MIN_DISTANCE = 150; //min distance for a scroll event
private static final int CLICK_ON_WEBVIEW = 1;
private static final int CLICK_ON_URL = 2;
private static final int UP_ON_WEBVIEW = 3;
private final Handler handler = new Handler(this);
public WebView webView;
private WebViewClient client;
private WebAppInterface webAppInt = new WebAppInterface(this);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
webView = (WebView)findViewById(R.id.myWebView);
webView.setOnTouchListener(this);
client = new WebViewClient();
webView.setWebViewClient(client);
webView.loadDataWithBaseURL("file:///android_asset/", "myweb.html", "text/html", "UTF-8", "");
}
//HERE START THE IMPORTANT PART
#Override
public boolean onTouch(View v, MotionEvent event) {
if (v.getId() == R.id.myWebView && event.getAction() == MotionEvent.ACTION_DOWN){
x1 = event.getX();
y1 = event.getY();
handler.sendEmptyMessageDelayed(CLICK_ON_WEBVIEW, 200);
} else if (v.getId() == R.id.myWebView && event.getAction() == MotionEvent.ACTION_UP){
x2 = event.getX();
y2 = event.getY();
handler.sendEmptyMessageDelayed(UP_ON_WEBVIEW, 200);
}
return false;
}
#Override
public boolean handleMessage(Message msg) {
if (msg.what == CLICK_ON_URL){ //if you clic a link in the webview, thats not a scroll
handler.removeMessages(CLICK_ON_WEBVIEW);
handler.removeMessages(UP_ON_WEBVIEW);
return true;
}
if (msg.what == CLICK_ON_WEBVIEW){
//Handle the click in the webview
Toast.makeText(this, "WebView clicked", Toast.LENGTH_SHORT).show();
return true;
}
if (msg.what == UP_ON_WEBVIEW){
float deltaX = x2 - x1; //horizontal move distance
float deltaY = y2 - y1; //vertical move distance
if ((Math.abs(deltaX) > MIN_DISTANCE) && (Math.abs(deltaX) > Math.abs(deltaY)))
{
// Left to Right swipe action
if (x2 > x1)
{
//Handle the left to right swipe
Toast.makeText(this, "Left to Right swipe", Toast.LENGTH_SHORT).show ();
}
// Right to left swipe action
else
{
//Handle the right to left swipe
Toast.makeText(this, "Right to Left swipe", Toast.LENGTH_SHORT).show ();
}
}
else if ((Math.abs(deltaY) > MIN_DISTANCE) && (Math.abs(deltaY) > Math.abs(deltaX)))
{
// Top to Bottom swipe action
if (y2 > y1)
{
//Handle the top to bottom swipe
Toast.makeText(this, "Top to Bottom swipe", Toast.LENGTH_SHORT).show ();
}
// Bottom to top swipe action -- I HIDE MY ACTIONBAR ON SCROLLUP
else
{
getActionBar().hide();
Toast.makeText(this, "Bottom to Top swipe [Hide Bar]", Toast.LENGTH_SHORT).show ();
}
}
return true;
}
return false;
}
}
You can also try to control the speed of the swipe, to detect it as a real swipe or scrolling.
I hope that helps you.
Try to set the android:isClickable="true" in the XML and create an onClickListener in the Java code.
Actually now Touch Actions are not supported for webview. But some workarounds are available;
I am going to show it with a longpress example : I am using Pointoption because i will get the coordinate of element and will use it for longpress.
public void longpress(PointOption po) {
//first you need to switch to native view
driver.switchToNativeView();
TouchAction action = new TouchAction((PerformsTouchActions) driver);
action.longPress(po).waitAction(WaitOptions.waitOptions(Duration.ofSeconds(2)));
action.release();
action.perform();
driver.switchToDefaultWebView();
}
For to get the coordinate of element i designed below methood
public PointOption getElementLocation(WebElement element) {
int elementLocationX;
int elementLocationY;
//get element location in webview
elementLocationX = element.getLocation().getX();
elementLocationY = element.getLocation().getY();
//get the center location of the element
int elementWidthCenter = element.getSize().getWidth() / 2;
int elementHeightCenter = element.getSize().getHeight() / 2;
int elementWidthCenterLocation = elementWidthCenter + elementLocationX;
int elementHeightCenterLocation = elementHeightCenter + elementLocationY;
//calculate the ratio between actual screen dimensions and webview dimensions
float ratioWidth = device.getDeviceScreenWidth() / ((MobileDevice) device)
.getWebViewWidth().intValue();
float ratioHeight = device.getDeviceScreenHeight() / ((MobileDevice) device)
.getWebViewHeight().intValue();
//calculate the actual element location on the screen , if needed you can increase this value,for example i used 115 for one of my mobile devices.
int offset = 0;
float elementCenterActualX = elementWidthCenterLocation * ratioWidth;
float elementCenterActualY = (elementHeightCenterLocation * ratioHeight) + offset;
float[] elementLocation = {elementCenterActualX, elementCenterActualY};
int elementCoordinateX, elementCoordinateY;
elementCoordinateX = (int) Math.round(elementCenterActualX);
elementCoordinateY = (int) Math.round(elementCenterActualY);
PointOption po = PointOption.point(elementCoordinateX, elementCoordinateY);
return po;
}
now you have a longpress(PointOption po) and getElementLocation(Webelement element) methods that gives you po. Now everything is ready and you can use them as below..
longpress(getElementLocation(driver.findElement(By.id("the selector can be any of them(xpath,css,classname,id etc.)")));

How to determine the TouchEvent when i use SlidingMenu/ViewPager and ListView?

I use slidingMenu(from jfeinstein10) in my app, I have created a new Activity which extends from SlidingFragmentActivity, in Activity I use ViewPager from support.v4 to display some fragment, using the way gtRfnkN answered in Question ViewPager inside ViewPager
public class GalleryViewPager extends ViewPager {
/** the last x position */
private float lastX;
/** if the first swipe was from left to right (->), dont listen to swipes from the right */
private boolean slidingLeft;
/** if the first swipe was from right to left (<-), dont listen to swipes from the left */
private boolean slidingRight;
public GalleryViewPager(final Context context, final AttributeSet attrs) {
super(context, attrs);
}
public GalleryViewPager(final Context context) {
super(context);
}
#Override
public boolean onTouchEvent(final MotionEvent ev) {
final int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
// Disallow parent ViewPager to intercept touch events.
this.getParent().requestDisallowInterceptTouchEvent(true);
// save the current x position
this.lastX = ev.getX();
break;
case MotionEvent.ACTION_UP:
// Allow parent ViewPager to intercept touch events.
this.getParent().requestDisallowInterceptTouchEvent(false);
// save the current x position
this.lastX = ev.getX();
// reset swipe actions
this.slidingLeft = false;
this.slidingRight = false;
break;
case MotionEvent.ACTION_MOVE:
/*
* if this is the first item, scrolling from left to
* right should navigate in the surrounding ViewPager
*/
if (this.getCurrentItem() == 0) {
// swiping from left to right (->)?
if (this.lastX <= ev.getX() && !this.slidingRight) {
// make the parent touch interception active -> parent pager can swipe
this.getParent().requestDisallowInterceptTouchEvent(false);
} else {
/*
* if the first swipe was from right to left, dont listen to swipes
* from left to right. this fixes glitches where the user first swipes
* right, then left and the scrolling state gets reset
*/
this.slidingRight = true;
// save the current x position
this.lastX = ev.getX();
this.getParent().requestDisallowInterceptTouchEvent(true);
}
} else
/*
* if this is the last item, scrolling from right to
* left should navigate in the surrounding ViewPager
*/
if (this.getCurrentItem() == this.getAdapter().getCount() - 1) {
// swiping from right to left (<-)?
if (this.lastX >= ev.getX() && !this.slidingLeft) {
// make the parent touch interception active -> parent pager can swipe
this.getParent().requestDisallowInterceptTouchEvent(false);
} else {
/*
* if the first swipe was from left to right, dont listen to swipes
* from right to left. this fixes glitches where the user first swipes
* left, then right and the scrolling state gets reset
*/
this.slidingLeft = true;
// save the current x position
this.lastX = ev.getX();
this.getParent().requestDisallowInterceptTouchEvent(true);
}
}
break;
}
super.onTouchEvent(ev);
return true;
}
}
But when i put a ListView in ViewPager's Fragment, when i moves the ListView horizontally, the ViewPager keeps unmoved and the slidingmenu slides out.Can you tell me how to slove this? thanks a lot.
Try THis Demo for Sliding with listview, map... hope this will help u
:
https://github.com/jfeinstein10/SlidingMenu

Android Gallery Flipping issue

I have a Gallery of views that contain a TextView Label and then a listview below that. It works excellent except that in order to get it to flip from element to element, the user has to touch either above the listview (near the label) and fling or in between gallery objects. Sometimes below the listview works too.But I really want to be able to fling while touching the listview too because it takes up a majority of the screen. How can this be done? What code do you need to see?
I had a similar problem and solved this by overriding the Gallery and implementing the onInterceptTouchEvent to ensure that move events are intercepted by the Gallery, and all other events are handled normally.
Returning true in the onInterceptTouchEvent causes all following touch events in this touch sequence to be sent to this view, false leaves the event for it's children.
TouchSlop is needed as when doing a click there is sometimes a small amount of movement.
Would love to claim this as my own idea, but got the basics of the code from the default Android Launcher code.
public class MyGallery extends Gallery{
private MotionEvent downEvent;
private int touchSlop;
private float lastMotionY;
private float lastMotionX;
public MyGallery(Context context) {
super(context);
initTouchSlop();
}
private void initTouchSlop() {
final ViewConfiguration configuration = ViewConfiguration.get(getContext());
touchSlop = configuration.getScaledTouchSlop();
}
#Override public boolean onInterceptTouchEvent(MotionEvent ev) {
final float x = ev.getX();
final float y = ev.getY();
switch (ev.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_MOVE: {
final int xDiff = (int) Math.abs(x - lastMotionX);
final int yDiff = (int) Math.abs(y - lastMotionY);
// have we moved enough to consider this a scroll
if (xDiff > touchSlop || yDiff > touchSlop) {
// this is the event we want, but we need to resend the Down event as this could have been consumed by a child
Log.d(TAG, "Move event detected: Start intercepting touch events");
if (downEvent != null) this.onTouchEvent(downEvent);
downEvent = null;
return true;
}
return false;
}
case MotionEvent.ACTION_DOWN: {
// need to save the on down event incase this is going to be a scroll
downEvent = MotionEvent.obtain(ev);
lastMotionX = x;
lastMotionY = y;
return false;
}
default: {
// if this is not a down or scroll event then it is not for us
downEvent = null;
return false;
}
}
}
You would want to set the onTouchListener() on the listview, or maybe the entire Linear/Relative layout.
getListView().setOnTouchListener(yourlistener) OR set it on the entire layout. If you post a little code, I could help you further. XML and how you are using in with the Java class would be most helpful.

Categories

Resources