1

I have an array of imageViews inside a linearlayout inside a scrollivew. The imageviews have onTouchEvent and get DOWN touch event when scrolling. I need a method so I can scroll without the views intercepting DOWN event.

Child touch listener

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;
                }
            });
  • have you tried setting `duplicateParentState` flag to false? [LINK](http://developer.android.com/reference/android/view/View.html#setDuplicateParentStateEnabled%28boolean%29) – snachmsm Jan 22 '15 at 10:21
  • It does nothing, I need my scrollview to intercept only move action. children only down and up. – Georgian Benetatos Jan 22 '15 at 10:27

1 Answers1

1

every "set of actions" starts from ACTION_DOWN, so your ImageViews will get this action always, because they can handle it (state change). but ACTION_MOVE can be handled only by ScrollView so these TouchEvents are passed forum Button-child (dispatched) to ScrollView-parent (through LinearLayout).

you can use dispatching and intercepting TouchEvents (more here) with passing your action type to proper View

but... how do you know what user wants? in the exact moment of touch (ACTION_DOWN) user might want to scroll OR click button, so you don't know how/to who dispatch later events and how views have to behave. I see in your code that you want animate your button - better try with its states and start animation with state_pressed changed to true and reverse it when set false. BUT its probably give you same effect. another way is to hold whole ScrollView when button got hit (some examples), but its poor solution in the terms of UX

check out some apps in Google Play or even Google Play app itself. Press any clickable View/App on any list - it changes background, on Lollipop some ripple effects etc.... but when you scroll the whole view pressed View/App lose these effects (state_pressed to false). There is no way you can guess what user want to do at the moment of ACTION_DOWN, so above described effects (losing) are normal

EDIT: as I said - there is no way and proper behaviour for guessing what user wants to do (click/scroll) at moment of ACTION_DOWN, so your code and use is fine. almost...

here is an example of one of my (really old) custom views called Arrow (it contains 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();
    }
}

its not the best way (two animators), but works like a charm.. note that while animating this View drawable states wont be refreshed (eg. when you set selector as a background). usage of ValueAnimator instead of regular 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;
                }
            });

it creates smooth reverse animation, so when user press Arrow (ACTION_DOWN) animation will start, but also when next action isn't for this view (ACTION_MOVE in this case) the reverseAnimation() method will be called which return this View to original size. whole operation will be pretty fast, so user won't even notice this small resizing (and reversing)

Community
  • 1
  • 1
snachmsm
  • 17,866
  • 3
  • 32
  • 74
  • Ok, I understand the part with action down not being a good approach, what way would you suggest to have an animated menu that is in scrollview. The imageview just goes down on click and overshoots when released. – Georgian Benetatos Jan 22 '15 at 11:57