Android,canScrollVertically 从未调用过 ImageView
Android, canScrollVertically never called for ImageView
我正在使用 Mike Ortiz 的 TouchImageView。
其中,canScrollHorizontally
被覆盖:
@Override
public boolean canScrollHorizontally(int direction) {
matrix.getValues(m);
float x = m[Matrix.MTRANS_X];
float y = m[Matrix.MTRANS_Y];
if (getImageWidth() < viewWidth) {
return false;
} else if (x >= -1 && direction < 0) {
return false;
} else if (Math.abs(x) + viewWidth + 1 >= getImageWidth() && direction > 0) {
return false;
}
return true;
}
这按预期工作。我可以将 TouchImageView 放在 ViewPager
中,然后在缩放的图像周围滚动,但当我到达图像的边缘时,也可以通过 ViewPager 滑动 right/left。
但我想要的是嵌套在常规 ViewPager 中的垂直 ViewPager。垂直 ViewPager 仿照 this SO post:
public class VerticalViewPager extends ViewPager {
public VerticalViewPager(Context context) {
super(context);
init();
}
public VerticalViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
// The majority of the magic happens here
setPageTransformer(true, new VerticalPageTransformer());
// The easiest way to get rid of the overscroll drawing that happens on the left and right
setOverScrollMode(OVER_SCROLL_NEVER);
}
private class VerticalPageTransformer implements ViewPager.PageTransformer {
@Override
public void transformPage(View view, float position) {
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.setAlpha(0);
} else if (position <= 1) { // [-1,1]
view.setAlpha(1);
// Counteract the default slide transition
view.setTranslationX(view.getWidth() * -position);
//set Y position to swipe in from top
float yPosition = position * view.getHeight();
view.setTranslationY(yPosition);
} else { // (1,+Infinity]
// This page is way off-screen to the right.
view.setAlpha(0);
}
}
}
/**
* Swaps the X and Y coordinates of your touch event.
*/
private MotionEvent swapXY(MotionEvent ev) {
float width = getWidth();
float height = getHeight();
float newX = (ev.getY() / height) * width;
float newY = (ev.getX() / width) * height;
ev.setLocation(newX, newY);
return ev;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev){
boolean intercepted = super.onInterceptTouchEvent(swapXY(ev));
swapXY(ev); // return touch coordinates to original reference frame for any child views
return intercepted;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
return super.onTouchEvent(swapXY(ev));
}
}
对于常规的 ImageView,效果很好。我可以在水平 ViewPager 中滑动 left/right,同时还可以根据需要在嵌套的 ViewPager 中垂直滚动。对垂直 ViewPager 的一项修改(以允许从垂直 ViewPager 内的任何位置进行水平滚动)是:
@Override
public boolean canScrollHorizontally(int direction) {
return false;
}
小改动,效果很好。
当垂直ViewPager 的内容是自定义的TouchImageView 时,问题就来了。 right/left 滑动仍然有效,但垂直滑动仅在图像右边缘与设备屏幕右侧对齐时有效。这是由于 TouchImageView class.
中的 canScrollHorizontally
覆盖
int direction
参数不考虑 x 或 y。正向 y 滑动与正向 x 滑动无法区分。
我想做的是覆盖 canScrollVertically
方法,以检测何时发生垂直滑动,但从未调用该方法。即使只是像这样的快速测试:
@Override
public boolean canScrollVertically(int direction) {
matrix.getValues(m);
Toast.makeText(context, "vertical", Toast.LENGTH_SHORT).show();
return true;
}
从未被调用。在 canScrollHorizontally
方法中放置相同的 Toast 消息会在每次滑动(水平或垂直)时显示消息。
为什么 canScrollVertically
从未被调用过?
我想检测图像何时在视图中触底,以及用户何时向上滑动。当满足这两个条件时,我可以从 canScrollHorizontally
return false 并允许垂直 ViewPager 正确滚动。
在此先感谢您的帮助。有人认识迈克·奥尔蒂斯吗!?
所以我想出了以下解决方案。
在 TouchImageView
内,Mike 有一个习惯 class
private class PrivateOnTouchListener implements OnTouchListener
其中包含以下块:
case MotionEvent.ACTION_MOVE:
if (state == State.DRAG) {
float deltaX = curr.x - last.x;
float deltaY = curr.y - last.y;
... etc ...
}
break;
我所做的是设置全局 boolean verticalDrag;
。使用上面的块,我检查以确保 deltaY 比 deltaX 足够大(即垂直 drag/swipe)
if(Math.abs(deltaY) > (Math.abs(deltaX) * sensitivity)){
verticalDrag = true;
}else{
verticalDrag = false;
}
敏感度由编码人员自行决定分配。真正的垂直滑动通常会有几个像素 left/right 偏差 - 很难只在一个方向或另一个方向滑动。因此,灵敏度可能非常高(即 5 到 10,或更高,具体取决于测试),并且它仍然会触发。
回到 canScrollHorizontally
方法中,我只是添加:
float y = m[Matrix.MTRANS_Y];
else if(Math.abs(y) + viewHeight + 1 >= getImageHeight() && verticalDrag){
return false;
}
允许垂直 ViewPager 根据需要滑动。
在我的例子中,我正在检查我的 scrollView(其中包含文本)是否可以垂直滚动 当 activity 初始化时。在手机上,它会滚动,但在平板电脑上则不能。 canScrollVertically
返回给我 不正确的值 因为它还不能确定。我通过在 OnGlobalLayoutListener
.
中调用它解决了这个问题
(科特林)
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
// Must use onGlobalLayout or else canScrollVertically will not return the correct value because the layout hasn't been made yet
scrollView.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
// If the scrollView can scroll, disable the accept menu item button
if ( scrollView.canScrollVertically(1) || scrollView.canScrollVertically(-1) )
acceptMenuItem?.isEnabled = false
// Remove itself after onGlobalLayout is first called or else it would be called about a million times per second
scrollView.viewTreeObserver.removeOnGlobalLayoutListener(this)
}
})
}
我的用例是显示使用条款。在用户滚动到底部之前,我不希望启用接受按钮。我知道这已经晚了,但我希望这能解决一些关于 canScrollVertically
的困惑
我正在使用 Mike Ortiz 的 TouchImageView。
其中,canScrollHorizontally
被覆盖:
@Override
public boolean canScrollHorizontally(int direction) {
matrix.getValues(m);
float x = m[Matrix.MTRANS_X];
float y = m[Matrix.MTRANS_Y];
if (getImageWidth() < viewWidth) {
return false;
} else if (x >= -1 && direction < 0) {
return false;
} else if (Math.abs(x) + viewWidth + 1 >= getImageWidth() && direction > 0) {
return false;
}
return true;
}
这按预期工作。我可以将 TouchImageView 放在 ViewPager
中,然后在缩放的图像周围滚动,但当我到达图像的边缘时,也可以通过 ViewPager 滑动 right/left。
但我想要的是嵌套在常规 ViewPager 中的垂直 ViewPager。垂直 ViewPager 仿照 this SO post:
public class VerticalViewPager extends ViewPager {
public VerticalViewPager(Context context) {
super(context);
init();
}
public VerticalViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
// The majority of the magic happens here
setPageTransformer(true, new VerticalPageTransformer());
// The easiest way to get rid of the overscroll drawing that happens on the left and right
setOverScrollMode(OVER_SCROLL_NEVER);
}
private class VerticalPageTransformer implements ViewPager.PageTransformer {
@Override
public void transformPage(View view, float position) {
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.setAlpha(0);
} else if (position <= 1) { // [-1,1]
view.setAlpha(1);
// Counteract the default slide transition
view.setTranslationX(view.getWidth() * -position);
//set Y position to swipe in from top
float yPosition = position * view.getHeight();
view.setTranslationY(yPosition);
} else { // (1,+Infinity]
// This page is way off-screen to the right.
view.setAlpha(0);
}
}
}
/**
* Swaps the X and Y coordinates of your touch event.
*/
private MotionEvent swapXY(MotionEvent ev) {
float width = getWidth();
float height = getHeight();
float newX = (ev.getY() / height) * width;
float newY = (ev.getX() / width) * height;
ev.setLocation(newX, newY);
return ev;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev){
boolean intercepted = super.onInterceptTouchEvent(swapXY(ev));
swapXY(ev); // return touch coordinates to original reference frame for any child views
return intercepted;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
return super.onTouchEvent(swapXY(ev));
}
}
对于常规的 ImageView,效果很好。我可以在水平 ViewPager 中滑动 left/right,同时还可以根据需要在嵌套的 ViewPager 中垂直滚动。对垂直 ViewPager 的一项修改(以允许从垂直 ViewPager 内的任何位置进行水平滚动)是:
@Override
public boolean canScrollHorizontally(int direction) {
return false;
}
小改动,效果很好。
当垂直ViewPager 的内容是自定义的TouchImageView 时,问题就来了。 right/left 滑动仍然有效,但垂直滑动仅在图像右边缘与设备屏幕右侧对齐时有效。这是由于 TouchImageView class.
中的canScrollHorizontally
覆盖
int direction
参数不考虑 x 或 y。正向 y 滑动与正向 x 滑动无法区分。
我想做的是覆盖 canScrollVertically
方法,以检测何时发生垂直滑动,但从未调用该方法。即使只是像这样的快速测试:
@Override
public boolean canScrollVertically(int direction) {
matrix.getValues(m);
Toast.makeText(context, "vertical", Toast.LENGTH_SHORT).show();
return true;
}
从未被调用。在 canScrollHorizontally
方法中放置相同的 Toast 消息会在每次滑动(水平或垂直)时显示消息。
为什么 canScrollVertically
从未被调用过?
我想检测图像何时在视图中触底,以及用户何时向上滑动。当满足这两个条件时,我可以从 canScrollHorizontally
return false 并允许垂直 ViewPager 正确滚动。
在此先感谢您的帮助。有人认识迈克·奥尔蒂斯吗!?
所以我想出了以下解决方案。
在 TouchImageView
内,Mike 有一个习惯 class
private class PrivateOnTouchListener implements OnTouchListener
其中包含以下块:
case MotionEvent.ACTION_MOVE:
if (state == State.DRAG) {
float deltaX = curr.x - last.x;
float deltaY = curr.y - last.y;
... etc ...
}
break;
我所做的是设置全局 boolean verticalDrag;
。使用上面的块,我检查以确保 deltaY 比 deltaX 足够大(即垂直 drag/swipe)
if(Math.abs(deltaY) > (Math.abs(deltaX) * sensitivity)){
verticalDrag = true;
}else{
verticalDrag = false;
}
敏感度由编码人员自行决定分配。真正的垂直滑动通常会有几个像素 left/right 偏差 - 很难只在一个方向或另一个方向滑动。因此,灵敏度可能非常高(即 5 到 10,或更高,具体取决于测试),并且它仍然会触发。
回到 canScrollHorizontally
方法中,我只是添加:
float y = m[Matrix.MTRANS_Y];
else if(Math.abs(y) + viewHeight + 1 >= getImageHeight() && verticalDrag){
return false;
}
允许垂直 ViewPager 根据需要滑动。
在我的例子中,我正在检查我的 scrollView(其中包含文本)是否可以垂直滚动 当 activity 初始化时。在手机上,它会滚动,但在平板电脑上则不能。 canScrollVertically
返回给我 不正确的值 因为它还不能确定。我通过在 OnGlobalLayoutListener
.
(科特林)
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
// Must use onGlobalLayout or else canScrollVertically will not return the correct value because the layout hasn't been made yet
scrollView.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
// If the scrollView can scroll, disable the accept menu item button
if ( scrollView.canScrollVertically(1) || scrollView.canScrollVertically(-1) )
acceptMenuItem?.isEnabled = false
// Remove itself after onGlobalLayout is first called or else it would be called about a million times per second
scrollView.viewTreeObserver.removeOnGlobalLayoutListener(this)
}
})
}
我的用例是显示使用条款。在用户滚动到底部之前,我不希望启用接受按钮。我知道这已经晚了,但我希望这能解决一些关于 canScrollVertically