I have got a custom scrollview that looks like this:
public class PagerScrollView extends ScrollView {
float touchX = 0;
float touchY = 0;
ViewPager parentPager;
public void setParentPager(ViewPager parentPager) {
this.parentPager = parentPager;
}
public PagerScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public PagerScrollView(Context context) {
super(context);
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
touchX = ev.getX();
touchY = ev.getY();
return super.onTouchEvent(ev);
case MotionEvent.ACTION_MOVE:
if (Math.abs(touchX-ev.getX()) < 40) {
return super.onTouchEvent(ev);
} else {
if (parentPager == null) {
return false;
} else {
return parentPager.onTouchEvent(ev);
}
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
touchX = 0;
touchY = 0;
break;
}
return super.onTouchEvent(ev);
}
}
In the xml, I wrap it around a ViewPager and a TabLayout (maybe this is the error?). When I try to assign it using
PagerScrollView pagerScrollView = rootView.findViewById(R.id.pager_scroll_view);
I get:
android.widget.ScrollView cannot be cast to exampleapplication.PagerScrollView
How can I cast it correctly?
The issue was that I used a normal ScrollView in the xml instead of my <exampleapplication.CustomScrollView>. Replacing that fixed my problem.
Related
I have a layout where a WebView is inside a ScrollView, and I want to be able to switch between these two to decide which one receives the touch events. Unfortunately no matter what I do, the ScrollView seems to steal the touch events from the WebView, making it impossible to pan & zoom smoothly in the WebView. Is there some solution for this?
I have tried ScrollView.SetOnTouchListener(); and set a listener that returns true, this stops the ScrollView from scrolling but does not prevent the touch events being intercepted before they reach the WebView.
I have also tried WebView.Parent.RequestDisallowInterceptTouchEvent(true); and WebView.Parent.RequestDisallowInterceptTouchEvent(true); which both seem to have no effect.
Try using NestedScrollView and implement NestedScrollingChild to a custom WebView.
Reference Link
and some code
public class NestedWebView extends WebView implements NestedScrollingChild {
private int mLastY;
private final int[] mScrollOffset = new int[2];
private final int[] mScrollConsumed = new int[2];
private int mNestedOffsetY;
private NestedScrollingChildHelper mChildHelper;
public NestedWebView(Context context) {
this(context, null);
}
public NestedWebView(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.webViewStyle);
}
public NestedWebView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mChildHelper = new NestedScrollingChildHelper(this);
setNestedScrollingEnabled(true);
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
boolean returnValue = false;
MotionEvent event = MotionEvent.obtain(ev);
final int action = MotionEventCompat.getActionMasked(event);
if (action == MotionEvent.ACTION_DOWN) {
mNestedOffsetY = 0;
}
int eventY = (int) event.getY();
event.offsetLocation(0, mNestedOffsetY);
switch (action) {
case MotionEvent.ACTION_MOVE:
int deltaY = mLastY - eventY;
// NestedPreScroll
if (dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset)) {
deltaY -= mScrollConsumed[1];
mLastY = eventY - mScrollOffset[1];
event.offsetLocation(0, -mScrollOffset[1]);
mNestedOffsetY += mScrollOffset[1];
}
returnValue = super.onTouchEvent(event);
// NestedScroll
if (dispatchNestedScroll(0, mScrollOffset[1], 0, deltaY, mScrollOffset)) {
event.offsetLocation(0, mScrollOffset[1]);
mNestedOffsetY += mScrollOffset[1];
mLastY -= mScrollOffset[1];
}
break;
case MotionEvent.ACTION_DOWN:
returnValue = super.onTouchEvent(event);
mLastY = eventY;
// start NestedScroll
startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
returnValue = super.onTouchEvent(event);
// end NestedScroll
stopNestedScroll();
break;
}
return returnValue;
}
// Nested Scroll implements
#Override
public void setNestedScrollingEnabled(boolean enabled) {
mChildHelper.setNestedScrollingEnabled(enabled);
}
#Override
public boolean isNestedScrollingEnabled() {
return mChildHelper.isNestedScrollingEnabled();
}
#Override
public boolean startNestedScroll(int axes) {
return mChildHelper.startNestedScroll(axes);
}
#Override
public void stopNestedScroll() {
mChildHelper.stopNestedScroll();
}
#Override
public boolean hasNestedScrollingParent() {
return mChildHelper.hasNestedScrollingParent();
}
#Override
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed,
int[] offsetInWindow) {
return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow);
}
#Override
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
}
#Override
public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
return mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
}
#Override
public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
return mChildHelper.dispatchNestedPreFling(velocityX, velocityY);
}
}
Declare NestedWebView instead of declaring WebView inside the NestedScrollView.For example
<com.nestedscrollwebviewexample.NestedWebView
android:id="#+id/nested_webview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#000000"
android:fillViewport="true"
android:focusable="true"
android:isScrollContainer="false"
android:visibility="visible"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
app:layout_scrollFlags="scroll|exitUntilCollapsed" />
Instead of declaring Webview you can initialize as NestedWebView inside your Activity
private NestedWebView mShopWebView;
mShopWebView = (NestedWebView) findViewById(R.id.url_load_webview);
Hope this serves the purpose.
I worked around this by adding a floating WebView over the screen at the same level of the hierarchy as the ScrollView and linked its position to the scroll value, then switched touch control with WebView.BringToFront and ScrollView.BringToFront
I had a recyclerview with an adapter and everything was cool. I added view.setOnTouchListener so that I could scroll this recyclerview along with animation and other elements on 1 screen from left to right. But after that, scrolling up and down broke in recyclerview, and onclicklistener on the elements inside it stopped working. What to do and how to fix this conflict?
At the moment I can put return false in the public boolean onTouch (View v, MotionEvent event) method; instead of return gestureDetector.onTouchEvent (event); and get a working scrolling up and down back, but left-right stops working. I can do simultaneous scrolling as in ios, when scrolling of tablecloths and collections did not break when adding the svayp to the left-right.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
view.setOnTouchListener(new OnSwipeTouchListener(MainActivity.this) {
public void onSwipeRight() {
choseold = chosenow;
chosenow = chosenow - 1;
if (chosenow <= 0) {
chosenow = 5;
choseold = 6;
}
swipe();
}
public void onSwipeLeft() {
choseold = chosenow;
chosenow = chosenow + 1;
if (chosenow >= 6) {
chosenow = 1;
choseold = 0;
}
swipe();
}
});
view.setFocusableInTouchMode(true);
MyAdapterlang = new MyAdapterLang(MainActivity.this, yaziki1, yaziki2, flagi);
RVlang.setAdapter(MyAdapterlang);
mLayoutManager = new LinearLayoutManager(this);
RV1.setLayoutManager(mLayoutManager);
MyAdapter = new MyAdapterApps(MainActivity.this, childs, childs2);
RV1.setAdapter(MyAdapter);
}
}
class OnSwipeTouchListener implements View.OnTouchListener {
private final GestureDetector gestureDetector;
public OnSwipeTouchListener (Context ctx){
gestureDetector = new GestureDetector(ctx, new GestureListener());
}
#Override
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
//return false;
}
/*public boolean onTouch(final View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
y1 = event.getY();
break;
case MotionEvent.ACTION_MOVE:
y2 = event.getY();
float deltaY = y2 - y1;
if (Math.abs(deltaY) > MIN_DISTANCE) {
return false;
} else {
return gestureDetector.onTouchEvent(event);
}
}
return gestureDetector.onTouchEvent(event);
}
private float y1, y2;
private static final int MIN_DISTANCE = 50;*/
private final class GestureListener extends GestureDetector.SimpleOnGestureListener {
private static final int SWIPE_THRESHOLD = 100;
private static final int SWIPE_VELOCITY_THRESHOLD = 100;
#Override
public boolean onDown(MotionEvent e) {
return true;
}
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
try {
float diffY = e2.getY() - e1.getY();
float diffX = e2.getX() - e1.getX();
if (Math.abs(diffX) > Math.abs(diffY)) {
if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
if (diffX > 0) {
onSwipeRight();
} else {
onSwipeLeft();
}
return true;
}
}
} catch (Exception exception) {
exception.printStackTrace();
}
return false;
}
}
public void onSwipeRight() {
}
public void onSwipeLeft() {
}
}
Here in ios, for example, is very simple and does not break the scrolling of collections:
- (void)viewDidLoad {
UISwipeGestureRecognizer * swipeleft=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:#selector(swipeleft:)];
swipeleft.direction=UISwipeGestureRecognizerDirectionLeft;
[self.view addGestureRecognizer:swipeleft];
UISwipeGestureRecognizer * swiperight=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:#selector(swiperight:)];
swiperight.direction=UISwipeGestureRecognizerDirectionRight;
[self.view addGestureRecognizer:swiperight];
}
-(void)swipeleft:(UISwipeGestureRecognizer*)gestureRecognizer
{
if (self.currentnew <= 3) {
self.whatpress = 1;
[self.buttonnew sendActionsForControlEvents:UIControlEventTouchUpInside];
}
}
-(void)swiperight:(UISwipeGestureRecognizer*)gestureRecognizer
{
if ((self.currentnew >= 1) && (self.currentnew <= 3)) {
self.whatpress = 2;
[self.buttonnew sendActionsForControlEvents:UIControlEventTouchUpInside];
}
}
Here is a video:
https://2ch.hk/pr/src/1314926/15464190246250.mp4 https://2ch.hk/pr/src/1314926/15464193253190.mp4
If you only want to hook into default touch handling implementation you must return false here
#Override
public boolean onTouch(View v, MotionEvent event) {
gestureDetector.onTouchEvent(event);//Idk if it is needed or not - out of scope
return false;
}
Otherwise touch event will be considered as consumed and wont be propagated to other handlers (that handles scrolling, clicking etc. - all that stuff that stops working for you)
I got the code below from here.
This code snaps the items of HorizontalScrollView. I tried a lot to implement this customView inside my layout but I am not able to figure out to how to attach my layout to it.
This example does add the views programmatically and calls them Features.
In XML I have done "my package name.view" but I cannot figure out how to call setFeatureItems so that my Views can be attached to it.
The snapping feature can be applied on RecyclerView easily by using SnapHelper but I haven't found anything for HorizontalScrollView.
public class HomeFeatureLayout extends HorizontalScrollView {
private static final int SWIPE_MIN_DISTANCE = 5;
private static final int SWIPE_THRESHOLD_VELOCITY = 300;
private ArrayList mItems = null;
private GestureDetector mGestureDetector;
private int mActiveFeature = 0;
public HomeFeatureLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public HomeFeatureLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public HomeFeatureLayout(Context context) {
super(context);
}
public void setFeatureItems(ArrayList items){
LinearLayout internalWrapper = new LinearLayout(getContext());
internalWrapper.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
internalWrapper.setOrientation(LinearLayout.HORIZONTAL);
addView(internalWrapper);
this.mItems = items;
for(int i = 0; i< items.size();i++){
LinearLayout featureLayout = (LinearLayout) View.inflate(this.getContext(),R.layout.homefeature,null);
//...
//Create the view for each screen in the scroll view
//...
internalWrapper.addView(featureLayout);
}
setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
//If the user swipes
if (mGestureDetector.onTouchEvent(event)) {
return true;
}
else if(event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL ){
int scrollX = getScrollX();
int featureWidth = v.getMeasuredWidth();
mActiveFeature = ((scrollX + (featureWidth/2))/featureWidth);
int scrollTo = mActiveFeature*featureWidth;
smoothScrollTo(scrollTo, 0);
return true;
}
else{
return false;
}
}
});
mGestureDetector = new GestureDetector(new MyGestureDetector());
}
class MyGestureDetector extends SimpleOnGestureListener {
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
try {
//right to left
if(e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
int featureWidth = getMeasuredWidth();
mActiveFeature = (mActiveFeature < (mItems.size() - 1))? mActiveFeature + 1:mItems.size() -1;
smoothScrollTo(mActiveFeature*featureWidth, 0);
return true;
}
//left to right
else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
int featureWidth = getMeasuredWidth();
mActiveFeature = (mActiveFeature > 0)? mActiveFeature - 1:0;
smoothScrollTo(mActiveFeature*featureWidth, 0);
return true;
}
} catch (Exception e) {
Log.e("Fling", "There was an error processing the Fling event:" + e.getMessage());
}
return false;
}
}
}
in your recycleview adapter you will have reference to HomeFeatureLayout there you can call
homeFeatureLayout.setFeatureItems(items);
EDIT
public void setFeatureItems(ArrayList items){
for(int i = 0; i< items.size();i++){
// here you need to provide layout of individual items in your horizontal scrollview
LinearLayout featureLayout = (LinearLayout) View.inflate(this.getContext(),R.layout.anyLayout,null);
//...
//display information here
//...
TextView title = (TextView)featureLayout.findviewById(R.id.title);
title.setText("view title "+item.get(i).getTitle());
ImageView image = (ImageView)featureLayout.findviewById(R.id.icon);
image.setResourceId(R.drawable.icon);
internalWrapper.addView(featureLayout);
}
}
I am trying to use the variable "mCurrentPage" from an external Library in an Activity. This Library uses the views from a FrameLayout and shows them as book pages which you can swipe. What I want to do is to execute something IF an X view of the FrameLayout is on the front. (please not bringToFront())
I have tried with getElevation() but it requires API > 21.
Actually the variable "mCurrentPage" from that code does the job. but I need to use it (or imitate it) in other activities as well, I just can´t figure out how does this variable work and where does it get their values from in order to tell what´s the current view in the front.
public class PageTurnLayout extends FrameLayout {
private Point mLastTouchPoint;
private Rect mTopViewRect;
private Rect mBottomViewRect;
private Paint mPaint;
private int mCurrentPage;
private int mPageTouchSlop;
private boolean mIsTurning;
private PageTurnDirection mDirection;
private float mFirstX;
private Handler mHandler = new Handler();
public PageTurnLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public PageTurnLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public PageTurnLayout(Context context) {
super(context);
init();
}
private void init() {
setWillNotDraw(false);
mPaint = new Paint();
mBottomViewRect = new Rect();
mTopViewRect = new Rect();
mPageTouchSlop = (int) getResources().getDimension(R.dimen.touch_start_padding);
}
protected boolean isTouchAPageTurnStart(MotionEvent ev) {
if (ev.getAction() != MotionEvent.ACTION_DOWN)
return false;
return isTouchNearEdge(ev);
}
protected boolean isTouchNearEdge(MotionEvent ev) {
if (Math.abs(ev.getX() - getMeasuredWidth()) < mPageTouchSlop)
return true;
else if (ev.getX() < mPageTouchSlop)
return true;
return false;
}
protected PageTurnDirection getPageTurnDirection(MotionEvent ev) {
if(mFirstX - ev.getX() == 0.0f)
return null;
PageTurnDirection direction = mFirstX - ev.getX() > 0 ? PageTurnDirection.LEFT : PageTurnDirection.RIGHT;
return direction;
}
protected boolean shouldTurn() {
if(mDirection == null)
return false;
if(mDirection == PageTurnDirection.LEFT && mCurrentPage == getChildCount() - 1)
return false;
else if(mDirection == PageTurnDirection.RIGHT && mCurrentPage == 0)
return false;
return true;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return true;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN && !mIsTurning) {
mIsTurning = isTouchAPageTurnStart(event);
if (!mIsTurning) {
return false;
} else {
invalidate();
mLastTouchPoint = new Point((int) event.getX(), (int) event.getY());
mFirstX = event.getX();
return true;
}
} else if (event.getAction() == MotionEvent.ACTION_MOVE && mIsTurning) {
if(mDirection == null) {
//get the page turn direction
mDirection = getPageTurnDirection(event);
//if we shouldn't turn then abort everything and reset it
if(!shouldTurn()) {
mDirection = null;
mIsTurning = false;
return false;
}
}
mLastTouchPoint = new Point((int) event.getX(), (int) event.getY());
invalidate();
} else if (event.getAction() == MotionEvent.ACTION_UP && mIsTurning) {
int halfWidth = getMeasuredWidth() / 2;
if (mLastTouchPoint.x > halfWidth) {
final Runnable animationRunnable = new Runnable() {
public void run() {
mLastTouchPoint.x += 20;
invalidate();
if (mLastTouchPoint.x < getMeasuredWidth())
mHandler.post(this);
else {
mIsTurning = false;
if (mDirection == PageTurnDirection.RIGHT)
mCurrentPage--;
mDirection = null;
}
}
};
mHandler.post(animationRunnable);
} else {
final Runnable animationRunnable = new Runnable() {
public void run() {
mLastTouchPoint.x -= 20;
invalidate();
if (mLastTouchPoint.x > -(getMeasuredWidth() / 2)) {
mHandler.post(this);
} else {
mIsTurning = false;
if (mDirection == PageTurnDirection.LEFT)
mCurrentPage++;
mDirection = null;
}
}
};
mHandler.post(animationRunnable);
}
}
return true;
}
I'm trying to change an ImageView's height with only one finger movement (upside or downside). The code below changes height with two fingers (like pinching or reverse). How can I modify this code to do what I want? Thanks.
public class MainActivity extends Activity {
private ImageView iv;
private Matrix matrix = new Matrix();
private float scale = 1f;
private ScaleGestureDetector SGD;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iv = iv=(ImageView)findViewById(R.id.imageView);
SGD = new ScaleGestureDetector(this, new ScaleListener());
}
public boolean onTouchEvent(MotionEvent ev) {
SGD.onTouchEvent(ev);
return true;
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener
{
#Override
public boolean onScale(ScaleGestureDetector detector) {
scale *= detector.getScaleFactor();
scale = Math.max(0.1f, Math.min(scale, 5.0f));
matrix.setScale(1, scale);
iv.setImageMatrix(matrix);
return true;
}
}
}
Use GestureDetector for scroll:
#Bind(R.id.container)
ViewGroup container;
#Bind(R.id.image)
ImageView image;
private GestureDetectorCompat detector;
private float yscale = 1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
ButterKnife.bind(this);
image.setScaleType(ImageView.ScaleType.MATRIX);
detector = new GestureDetectorCompat(this, new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
Matrix m = new Matrix();
yscale += distanceY / container.getHeight();
m.setScale(1, yscale, 0, 0);
image.setImageMatrix(m);
return true;
}
});
}
#Override
public boolean onTouchEvent(MotionEvent event) {
return detector.onTouchEvent(event);
} detector.onTouchEvent(event);
}
change
public boolean onTouchEvent(MotionEvent ev) {
SGD.onTouchEvent(ev);
return true;
}
to
public boolean onTouchEvent(MotionEvent ev) {
switch(ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN, case MotionEvent.ACTION_UP:
SGD.onTouchEvent(ev);
break;
return true;
}
This code snipet should help you to detect up and down
private VelocityTracker mVelocityTracker = null;
#Override
public boolean onTouchEvent(MotionEvent event) {
int index = event.getActionIndex();
int action = event.getActionMasked();
int pointerId = event.getPointerId(index);
switch(action) {
case MotionEvent.ACTION_DOWN:
if(mVelocityTracker == null) {
// Retrieve a new VelocityTracker object to watch the velocity of a motion.
mVelocityTracker = VelocityTracker.obtain();
}
else {
// Reset the velocity tracker back to its initial state.
mVelocityTracker.clear();
}
// Add a user's movement to the tracker.
mVelocityTracker.addMovement(event);
break;
case MotionEvent.ACTION_MOVE:
mVelocityTracker.addMovement(event);
// When you want to determine the velocity, call
// computeCurrentVelocity(). Then call getXVelocity()
// and getYVelocity() to retrieve the velocity for each pointer ID.
mVelocityTracker.computeCurrentVelocity(1000);
// Log velocity of pixels per second
// Best practice to use VelocityTrackerCompat where possible.
Log.d("", "X velocity: " +
VelocityTrackerCompat.getXVelocity(mVelocityTracker,
pointerId));
Log.d("", "Y velocity: " +
VelocityTrackerCompat.getYVelocity(mVelocityTracker,
pointerId));
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
// Return a VelocityTracker object back to be re-used by others.
mVelocityTracker.recycle();
break;
}
return true;
}