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)。

您可以使用调度和拦截 TouchEvents (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 这个视图到原始大小。整个操作会非常快,所以用户甚至不会注意到这个小的调整大小(和反转)