📜 ⬆️ ⬇️

Implementation of simultaneous scrolling in all directions

Introduction


Good day! I’ll say right away that I started programming recently and I don’t have much experience, so don’t judge strictly, especially since there are very few materials on this topic. In the article I want to share my solution to the problem that I had when creating a step-by-step 2D strategy. For strategies, the usual thing is the presence of the playing field. But what if the user has a small phone and the entire playing field does not fit on the screen? I asked this question about a month ago, when nothing was ready for me. At first I decided to wrap the field as usual in ScrollView and HorizontalScrollView. And here begins the actual problem. Scrolling was possible only in one direction at a time, which is very inconvenient, especially for the game. If you are interested in solving this problem, welcome under cat.

We understand


First you need to think that we already have, and not try to immediately invent something of our own. Looking for some time in the Google documentation of information on the ScrollView class it becomes clear that all is not so bad. Let's look at the onInterceptTouchEvent (MotionEvent ev) method. It is called to determine the interception of touches - this is what we need. It is necessary to make it catch all the movements, except for clicking on the field element. Here's what it looks like:

Vertical scrolling class
public final class MultiScrollView extends ScrollView { //   private int origX, origY; //     ,  60 px,    private final float THRESHOLD = 60; public MultiScrollView(Context context) { super(context); } public MultiScrollView(Context context, AttributeSet attrs) { super(context, attrs); } public MultiScrollView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override public boolean canScrollHorizontally(int direction) { return true; } @Override public boolean canScrollVertically(int direction) { return true; } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { origX = (int) ev.getX(); origY = (int) ev.getY(); } else if (ev.getAction() == MotionEvent.ACTION_MOVE) { float deltaX = Math.abs(ev.getX() - origX); float deltaY = Math.abs(ev.getY() - origY); return deltaX >= THRESHOLD || deltaY >= THRESHOLD; } return false; } } 


As you can see, touches are intercepted only if the change in finger coordinates is greater than 60 px. Also in this class, you must override the canScrollHorizontally method so that it always returns true.

You also need to create your own horizontal scrolling class:
')
Horizontal scrolling class
 public final class MyHorizontalScrollView extends HorizontalScrollView { public MyHorizontalScrollView(Context context) { super(context); } public MyHorizontalScrollView(Context context, AttributeSet attrs) { super(context, attrs); } public MyHorizontalScrollView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return false; } @Override public boolean canScrollHorizontally(int direction) { return false; } @Override public boolean onTouchEvent(MotionEvent ev) { return false; } } 


Here we forbid him to react to any touch.

Check


Now you need to check how it works. Create a markup:

Activity markup - activity_main.xml
 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> <example.multiscroll.MultiScrollView android:id="@+id/field_y" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:background="@color/color_black" android:fadingEdgeLength="0dp" android:focusable="true" android:isScrollContainer="true" android:overScrollMode="never" android:requiresFadingEdge="none" android:scrollbars="none" android:splitMotionEvents="true" tools:context="example.multiscroll.MainActivity"> <example.multiscroll.MyHorizontalScrollView android:id="@+id/field_x" android:layout_width="match_parent" android:layout_height="wrap_content" android:fadingEdgeLength="0dp" android:focusable="false" android:isScrollContainer="true" android:overScrollMode="never" android:requiresFadingEdge="none" android:scrollbars="none" android:splitMotionEvents="true"> <TableLayout android:id="@+id/table" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@color/color_white" android:orientation="vertical" android:paddingBottom="70dp" android:paddingEnd="140dp" android:paddingLeft="140dp" android:paddingRight="140dp" android:paddingStart="140dp" android:paddingTop="70dp" /> </example.multiscroll.MyHorizontalScrollView> </example.multiscroll.MultiScrollView> </RelativeLayout> 


Markup field element - sq.xml
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="100dp" android:layout_height="100dp" android:layout_margin="5dp"> <ImageView android:layout_width="match_parent" android:layout_height="match_parent" /> </RelativeLayout> 


Now connect all the markup to the activity. Immediately I draw your attention to the fact that the field elements will create dynamic, i.e. we can specify any number of rows and columns. For the field element, a separate file is used (in my project it contains a picture and 2 text fields on top of it, but for example this is superfluous).

Activity code - MainActivity.java
 public class MainActivity extends Activity { private ScrollView scrollY; private GestureDetector gestureDetectorY; private RelativeLayout[][] sqContent; private int sizeI = 10; private int sizeJ = 20; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); sqContent = new RelativeLayout[sizeI][sizeJ]; scrollY = (MultiScrollView) findViewById(R.id.field_y); gestureDetectorY = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { scrollX.smoothScrollBy((int) distanceX, 0); scrollY.smoothScrollBy(0, (int) (distanceY)); return true; } }); scrollY.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { gestureDetectorY.onTouchEvent(event); return true; } }); TableLayout table = (TableLayout) findViewById(R.id.table); TableRow row[] = new TableRow[sizeJ]; TableRow.LayoutParams paramsTableRow = new TableRow.LayoutParams(TableRow.LayoutParams.MATCH_PARENT, TableRow.LayoutParams.WRAP_CONTENT); paramsTableRow.setMargins(0, 50, 0, 50); paramsTableRow.weight = 1; for (int i = 0; i < sizeI; i++) { row[i] = new TableRow(this); row[i].setLayoutParams(paramsTableRow); row[i].setWeightSum(sizeJ); for (int j = 0; j < sizeJ; j++) { sqContent[i][j] = (RelativeLayout) getLayoutInflater().inflate(R.layout.sq, row[i], false); sqContent[i][j].setBackgroundResource(R.color.color_sq_action); sqContent[i][j].setOnClickListener(getOnClickListener()); row[i].addView(sqContent[i][j], j); } table.addView(row[i], i); } } private View.OnClickListener getOnClickListener() { return new View.OnClickListener() { @Override public void onClick(View v) { v.setBackgroundResource(R.color.color_black); } }; } } 


For example, I took the size of the field 20 to 10. Particular attention should be paid here to the object of the class GestureDetector. It is needed to determine the gestures. In the touch handler for horizontal and vertical scrolling, we pass an event to it. He, in turn, causes a scroll along the X and Y axis. Also, to check the convenience of clicking on the field elements, I hung a listener on them, which changes their color. Even if you do not press, and hold a little (<= 60 px), the color will change. Thus, problems with the convenience of clicking does not arise. I hope I have explained my decision in some detail and it became clear to you.

Sources of this project you can download here .

Source: https://habr.com/ru/post/328450/


All Articles