There is a LinearLayout, and on it elements TextView. A handler is assigned to each TextView element.

... new View.OnTouchListener() { int initialX = 0; int initialY = 0; static final int MAX_CLICK_DURATION = 200; long startClickTime; @Override public boolean onTouch(View v, MotionEvent event) { long clickDuration = Calendar.getInstance().getTimeInMillis() - startClickTime; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: initialX = (int) event.getX(); initialY = (int) event.getY(); startClickTime = Calendar.getInstance().getTimeInMillis(); break; case MotionEvent.ACTION_MOVE: if (clickDuration > MAX_CLICK_DURATION) { int currentX = (int) event.getX(); int currentY = (int) event.getY(); dcl.setText(String.format(Locale.getDefault(), "(%d,%d)", currentX, currentY)); } break; case MotionEvent.ACTION_UP: if (clickDuration < MAX_CLICK_DURATION) v.performClick(); break; } return true; } }); 

Ideally, I plan to put an element when releasing my finger, where Drag-and-drop was started in front of the element, to which it was stretched, but for now I just output the coordinates, checking the work of the Listener.

The problem is that when a finger leaves the source element, including moving to another TextView element, the handler stops working (in this “pre-debugging example”, the coordinates stop updating), and ScrollView starts running, which contains this LinearLayout with elements .

How to make the dragging possible when leaving the original element, and scrolling is carried out only when you pinch your finger on a ScrollView's free space from TextView elements? Even with a short press, you need to save OnClick TextView, but I more or less figured out this, and the line is responsible for this:

 if (clickDuration < MAX_CLICK_DURATION) v.performClick(); 

Ps. I am aware that it would be easier to do this using the DragListView, but in this case it is impossible. LinearLayout, on which TextView's are sitting, is a part of a component that is inherited deeply in the hierarchy, and you cannot change LinearLayout to ListView, since other components of this hierarchy will collapse.

    1 answer 1

    I decided this in the following way:

     @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); scrollView = null; ViewParent parent = this; while (parent != null && !(parent instanceof ScrollView)) parent = parent.getParent(); if (parent != null) scrollView = (ScrollView) parent; } private int displayHeight; private ScrollView scrollView; private boolean scrollDisabled = false; private void disableScrolling() { if (scrollView != null) scrollView.requestDisallowInterceptTouchEvent(true); scrollDisabled = true; } private void enableScrolling() { if (scrollView != null) scrollView.requestDisallowInterceptTouchEvent(false); scrollDisabled = false; } private void scrollDynamically(int posY) { if (scrollView == null) return; int[] layoutOnScreen = new int[2]; mLayout.getLocationOnScreen(layoutOnScreen); //здесь и в дальнейшем mLayout - LinearLayout, на котором размещены перетаскиваемые элементы if (layoutOnScreen[1] < 240 && posY < 300) scrollView.smoothScrollBy(0, posY < 200 ? -20 : -10); if (posY > displayHeight - 150) scrollView.smoothScrollBy(0, posY > displayHeight - 50 ? 20 : 10); } private void init() { //здесь определяется высота дисплея WindowManager windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); if (windowManager == null) return; Display display = windowManager.getDefaultDisplay(); Point size = new Point(); display.getSize(size); displayHeight = size.y; mLayout.setPadding(2, 4, 2, 20); } private final OnTouchListener DRAG_AND_DROP = new OnTouchListener() { long startClickTime; TextView target = null; TextView source = null; @Override public boolean onTouch(View v, MotionEvent event) { long clickDuration = Calendar.getInstance().getTimeInMillis() - startClickTime; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: source = (TextView) v; startClickTime = Calendar.getInstance().getTimeInMillis(); break; case MotionEvent.ACTION_MOVE: if (clickDuration > MAX_CLICK_DURATION) { if (!scrollDisabled) disableScrolling(); target = null; for (int i = mLayout.getChildCount(); i >= 0; --i) { View anyChild = mLayout.getChildAt(i); if (anyChild instanceof TextView) { TextView child = (TextView) anyChild; Rect bounds = new Rect(); child.getHitRect(bounds); if (bounds.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())) target = child; else if (child != source) child.setTextColor(0xFFFFFFFF); } } if (target == null) { source.setTextColor(0xFF660000); source.setPaintFlags(source.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); } else { source.setPaintFlags(source.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG); if (target != source) { target.setTextColor(0xFF77FF00); source.setTextColor(0xFFFF7700); } else source.setTextColor(0xFF77FFFF); } scrollDynamically((int) event.getRawY()); } break; case MotionEvent.ACTION_UP: if (clickDuration < MAX_CLICK_DURATION) v.performClick(); else { if (target == null) /*действия при удалении*/; else { target.setTextColor(0xFFFFFFFF); if (target == source) /*действия при вставке*/; else /*действия при перемещении*/; } source.setPaintFlags(source.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG); source.setTextColor(0xFFFFFFFF); } enableScrolling(); break; } return true; } }; 

    As a result, Drag-and-drop provides three functions: deleting an element, moving an element (with the possibility of simultaneous dragging and scrolling if the target of movement is outside the screen) and inserting new elements. This keeps the element's onClick and ScrollView scrolling.