Android 滚动视图中的可触摸项目
Android touchable items in scrollview
我在 scrollivew 内的线性布局内有一组 imageView。
图像视图具有 onTouchEvent 并在滚动时获取 DOWN 触摸事件。
我需要一种方法,这样我就可以在不拦截 DOWN 事件的情况下进行滚动。
儿童触摸监听器
image.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
Animation scale = new ScaleAnimation(1f, 0.5f, 1f, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
scale.setInterpolator(new DecelerateInterpolator());
scale.setFillEnabled(true);
scale.setFillBefore(true);
scale.setFillAfter(true);
scale.setDuration(300);
v.startAnimation(scale);
image.getParent().getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_UP:
image.getParent().getParent().requestDisallowInterceptTouchEvent(false);
scale = new ScaleAnimation(0.5f, 1.0f, 0.5f, 1.0f, Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
scale.setInterpolator(new OvershootInterpolator());
scale.setFillEnabled(true);
scale.setFillBefore(true);
scale.setFillAfter(true);
scale.setDuration(200);
scale.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
toggle();
}
});
image.startAnimation(scale);
mPager.setCurrentItem(image.getId(), false);
v.performClick();
break;
case MotionEvent.ACTION_CANCEL:
scale = new ScaleAnimation(0.5f, 1.0f, 0.5f, 1.0f, Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
scale.setInterpolator(new OvershootInterpolator());
scale.setFillEnabled(true);
scale.setFillBefore(true);
scale.setFillAfter(true);
scale.setDuration(200);
scale.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
toggle();
}
});
image.startAnimation(scale);
mPager.setCurrentItem(image.getId(), false);
v.performClick();
break;
}
return true;
}
});
每个 "set of actions" 都从 ACTION_DOWN
开始,因此您的 ImageView
将始终执行此操作,因为他们可以处理它(状态更改)。但是 ACTION_MOVE
只能由 ScrollView
处理,所以这些 TouchEvent
被传递给论坛 Button-child(调度)到 ScrollView-parent(通过 LinearLayout)。
您可以使用调度和拦截 TouchEvent
s (more here) 将您的操作类型传递给正确的 View
但是……你怎么知道用户想要什么?在触摸的确切时刻 (ACTION_DOWN
) 用户可能想要滚动或单击按钮,因此您不知道 how/to 谁调度了稍后的事件以及视图必须如何表现。我在你的代码中看到你想要为你的按钮设置动画 - 最好尝试使用它的状态并开始动画,将 state_pressed
更改为 true
并在设置 false
时反转它。但它可能会给你同样的效果。另一种方法是在按钮被击中时保持整个 ScrollView
(some examples),但它在 UX
方面的解决方案很差
在 Google Play 甚至 Google Play 应用程序中查看一些应用程序。在任何列表上按任何可点击的 View/App - 它会改变背景,在 Lollipop 上会产生一些涟漪效应等......但是当你滚动整个视图时按下 View/App 会失去这些效果(state_pressed
到 false ).在ACTION_DOWN
的时刻,你无法猜到用户想要做什么,所以上面描述的效果(丢失)是正常的
编辑:
正如我所说 - 在 ACTION_DOWN
时刻没有办法和正确的行为来猜测用户想要做什么 (click/scroll),所以你的代码和使用没问题。差不多...
这是我的一个(非常旧的)自定义视图的示例,称为 Arrow(它包含箭头 ;))
@SuppressLint("NewApi") //Honeycomb ofc, use NineOldAndroid for older
public class Arrow extends ImageView implements ValueAnimator.AnimatorUpdateListener{
ValueAnimator va_scaleX=null, va_scaleY=null;
public Arrow(Context context) {
super(context);
createAnimation();
}
public Arrow(Context context, AttributeSet attrs) {
super(context, attrs);
createAnimation();
}
public Arrow(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
createAnimation();
}
@Override
public void onAnimationUpdate(ValueAnimator animation) {
invalidate();
}
private void createAnimation() {
if (va_scaleX == null || va_scaleY==null) {
va_scaleX = ObjectAnimator.ofFloat(this, "scaleX", 0.5f);
va_scaleY = ObjectAnimator.ofFloat(this, "scaleY", 0.5f);
/* useful, optional, similar methods from Animation
va_scaleX.setDuration(150);
va_scaleY.setDuration(150);
va_scaleX.setInterpolator(new LinearInterpolator());
va_scaleY.setInterpolator(new LinearInterpolator());*/
va_scaleY.addUpdateListener(this); //one is enough
}
}
public void startAnimation() {
va_scaleX.start();
va_scaleY.start();
}
public void reverseAnimation() {
va_scaleX.reverse();
va_scaleY.reverse();
}
}
这不是最好的方法(两个动画师),但工作起来很有魅力。请注意,在为该视图绘制动画时,可绘制状态不会刷新(例如,当您将 selector
设置为背景时)。使用 ValueAnimator
而不是常规 Animation
arrow.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
a.startAnimation();
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
a.reverseAnimation();
break;
default:
break;
}
return false;
}
});
它创建平滑的反向动画,因此当用户按下箭头 (ACTION_DOWN
) 动画将开始,而且当下一个动作不针对此视图时(ACTION_MOVE
在这种情况下)reverseAnimation()
方法将被调用 return 这个视图到原始大小。整个操作会非常快,所以用户甚至不会注意到这个小的调整大小(和反转)
我在 scrollivew 内的线性布局内有一组 imageView。 图像视图具有 onTouchEvent 并在滚动时获取 DOWN 触摸事件。 我需要一种方法,这样我就可以在不拦截 DOWN 事件的情况下进行滚动。
儿童触摸监听器
image.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
Animation scale = new ScaleAnimation(1f, 0.5f, 1f, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
scale.setInterpolator(new DecelerateInterpolator());
scale.setFillEnabled(true);
scale.setFillBefore(true);
scale.setFillAfter(true);
scale.setDuration(300);
v.startAnimation(scale);
image.getParent().getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_UP:
image.getParent().getParent().requestDisallowInterceptTouchEvent(false);
scale = new ScaleAnimation(0.5f, 1.0f, 0.5f, 1.0f, Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
scale.setInterpolator(new OvershootInterpolator());
scale.setFillEnabled(true);
scale.setFillBefore(true);
scale.setFillAfter(true);
scale.setDuration(200);
scale.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
toggle();
}
});
image.startAnimation(scale);
mPager.setCurrentItem(image.getId(), false);
v.performClick();
break;
case MotionEvent.ACTION_CANCEL:
scale = new ScaleAnimation(0.5f, 1.0f, 0.5f, 1.0f, Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
scale.setInterpolator(new OvershootInterpolator());
scale.setFillEnabled(true);
scale.setFillBefore(true);
scale.setFillAfter(true);
scale.setDuration(200);
scale.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
toggle();
}
});
image.startAnimation(scale);
mPager.setCurrentItem(image.getId(), false);
v.performClick();
break;
}
return true;
}
});
每个 "set of actions" 都从 ACTION_DOWN
开始,因此您的 ImageView
将始终执行此操作,因为他们可以处理它(状态更改)。但是 ACTION_MOVE
只能由 ScrollView
处理,所以这些 TouchEvent
被传递给论坛 Button-child(调度)到 ScrollView-parent(通过 LinearLayout)。
您可以使用调度和拦截 TouchEvent
s (more here) 将您的操作类型传递给正确的 View
但是……你怎么知道用户想要什么?在触摸的确切时刻 (ACTION_DOWN
) 用户可能想要滚动或单击按钮,因此您不知道 how/to 谁调度了稍后的事件以及视图必须如何表现。我在你的代码中看到你想要为你的按钮设置动画 - 最好尝试使用它的状态并开始动画,将 state_pressed
更改为 true
并在设置 false
时反转它。但它可能会给你同样的效果。另一种方法是在按钮被击中时保持整个 ScrollView
(some examples),但它在 UX
在 Google Play 甚至 Google Play 应用程序中查看一些应用程序。在任何列表上按任何可点击的 View/App - 它会改变背景,在 Lollipop 上会产生一些涟漪效应等......但是当你滚动整个视图时按下 View/App 会失去这些效果(state_pressed
到 false ).在ACTION_DOWN
的时刻,你无法猜到用户想要做什么,所以上面描述的效果(丢失)是正常的
编辑:
正如我所说 - 在 ACTION_DOWN
时刻没有办法和正确的行为来猜测用户想要做什么 (click/scroll),所以你的代码和使用没问题。差不多...
这是我的一个(非常旧的)自定义视图的示例,称为 Arrow(它包含箭头 ;))
@SuppressLint("NewApi") //Honeycomb ofc, use NineOldAndroid for older
public class Arrow extends ImageView implements ValueAnimator.AnimatorUpdateListener{
ValueAnimator va_scaleX=null, va_scaleY=null;
public Arrow(Context context) {
super(context);
createAnimation();
}
public Arrow(Context context, AttributeSet attrs) {
super(context, attrs);
createAnimation();
}
public Arrow(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
createAnimation();
}
@Override
public void onAnimationUpdate(ValueAnimator animation) {
invalidate();
}
private void createAnimation() {
if (va_scaleX == null || va_scaleY==null) {
va_scaleX = ObjectAnimator.ofFloat(this, "scaleX", 0.5f);
va_scaleY = ObjectAnimator.ofFloat(this, "scaleY", 0.5f);
/* useful, optional, similar methods from Animation
va_scaleX.setDuration(150);
va_scaleY.setDuration(150);
va_scaleX.setInterpolator(new LinearInterpolator());
va_scaleY.setInterpolator(new LinearInterpolator());*/
va_scaleY.addUpdateListener(this); //one is enough
}
}
public void startAnimation() {
va_scaleX.start();
va_scaleY.start();
}
public void reverseAnimation() {
va_scaleX.reverse();
va_scaleY.reverse();
}
}
这不是最好的方法(两个动画师),但工作起来很有魅力。请注意,在为该视图绘制动画时,可绘制状态不会刷新(例如,当您将 selector
设置为背景时)。使用 ValueAnimator
而不是常规 Animation
arrow.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
a.startAnimation();
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
a.reverseAnimation();
break;
default:
break;
}
return false;
}
});
它创建平滑的反向动画,因此当用户按下箭头 (ACTION_DOWN
) 动画将开始,而且当下一个动作不针对此视图时(ACTION_MOVE
在这种情况下)reverseAnimation()
方法将被调用 return 这个视图到原始大小。整个操作会非常快,所以用户甚至不会注意到这个小的调整大小(和反转)