Android RecyclerView Example Method for Scrolling to the Middle Position

  • 2021-08-28 21:02:58
  • OfStack

Recently, I saw that the lyrics of QQ music can roll back to the middle position after each slide. Feel very magical, open the developer mode display layout, found that the lyrics part is not written by android control, should be written in the front end. So, I wonder if I can use recyclerView to realize this automatic rollback to the middle position.

Hard work pays off, and after searching for some information, it was finally done.

Let me talk about it in detail.

Objectives

Click on an entry, and after 4s without any action, the entry scrolls to the middle position to be displayed. After clicking, the user slides, and then starts the delay after the user does not operate. Users click many times and remember the last click position.

Analysis

First of all, how does scrolling to the specified position operate?


//  Scroll to the specified position 
recyclerView.scrollToPosition(position);
//  Smooth scroll to the specified position 
recyclerView.smoothScrollToPosition(position);

Have you scrolled to the specified pixel position?


// scrollBy(x, y) This method is to control the distance of movement by yourself, and the unit is pixels , So in the use of scrollBy(x, y) You need to calculate the height or width of the movement yourself. 
recyclerView.scrollBy(x, y)

However, the problem is to scroll to the middle position. What about this? Is this okay?


mRecyclerView.scrollToPosition(0);
mRecyclerView.scrollBy(0,400);

Scroll to the established position first, and then scroll for 1 distance. The run found that these two lines of code only execute line 1, and line 2 is invalid.

debug debugging looked at, or did not understand, too complicated to achieve.

That is to say, this is not possible. Is there any other way?

RecyclerView has one scrolling listening method:


mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
      @Override
      public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        super.onScrollStateChanged(recyclerView, newState);
      }

      @Override
      public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
      }
    });

The onScrollStateChanged method corresponds to three states: static (SCROLL_STATE_IDLE), drag scroll (SCROLL_STATE_DRAGGING) and slide (SCROLL_STATE_SETTLING).

When slowly sliding manually, it will trigger: onScrollStateChanged (drag and scroll)- > (n) onScrolled-- > onScrollStateChanged (static);

When the hand slides quickly, it will trigger: onScrollStateChanged (drag and scroll)- > (n) onScrolled-- > onScrollStateChanged (sliding)-- >

(n) onScrolled-- > onScrollStateChanged (static);

Have an idea, click, first run scrollToPosition, in the onScrolled method inside the scrollBy method. Write code, run and pass.

The following is the calculation of the middle position.

Firstly, the display height of recylerview is calculated.


 Rect rect = new Rect();
 mRecyclerView.getGlobalVisibleRect(rect);
 reHeight = rect.bottom - rect.top - vHeight;
 When running  scrollToPosition  After that, clicking on the item will appear in the field of vision. At this time, calculate the corresponding displacement. Need attention 1 The point is that when the clicked item is in the field of view, it will not run  scrollToPosition  Method. 

    int top = mRecyclerView.getChildAt(position - firstPosition).getTop();
int half = reHeight / 2;
    mRecyclerView.scrollBy(0, top - half);

Finally, it is the setting of delay, which is delayed by Handler.

Code

The core code is as follows:


public class MainActivity extends AppCompatActivity {
  private static final String TAG = "MainActivity";
  private RecyclerView mRecyclerView;
  private LinearLayoutManager mLayoutManager;
  private RecyclerView.Adapter mAdapter;
  private String[] data;
  private Handler handler;
  private boolean isClick = false;
  private static int vHeight = -1;
  private static int reHeight = -1;
  private static int position = 0;
  private static final int target = 10;
  private static boolean isMove = false;
  private Runnable runnable;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    handler = new Handler();

    mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
    // Create default linearity LayoutManager
    mLayoutManager = new LinearLayoutManager(this);
    mLayoutManager.setAutoMeasureEnabled(true);
    mRecyclerView.setLayoutManager(mLayoutManager);
    // If you can determine that each item The height of is fixed, and setting this option can improve performance 
    mRecyclerView.setHasFixedSize(true);
    mRecyclerView.setNestedScrollingEnabled(false);
    data = new String[]{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21"};


    runnable = new Runnable() {
      @Override
      public void run() {
        if (isVisible()) {
          scrollToMiddle();
        } else {
          mRecyclerView.scrollToPosition(position);
          isMove = true;
          isClick = false;
        }
      }
    };

    mAdapter = new MyAdapter(data, new MyAdapter.onRecyclerViewItemClick() {
      @Override
      public void onItemClick(View v, int pos) {
        Toast.makeText(MainActivity.this, " No. 1 " + pos + " Row ", Toast.LENGTH_SHORT).show();
        position = pos;
        vHeight = v.getHeight();

        Rect rect = new Rect();
        mRecyclerView.getGlobalVisibleRect(rect);
        reHeight = rect.bottom - rect.top - vHeight;

        // handler.removeCallbacksAndMessages(null);
        handler.removeCallbacks(runnable);
        handler.postDelayed(runnable, 4000);
        isClick = true;

      }
    });
    mRecyclerView.setAdapter(mAdapter);
    mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
      @Override
      public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        super.onScrollStateChanged(recyclerView, newState);
        Log.d(TAG, "" + newState);
        if (newState == RecyclerView.SCROLL_STATE_DRAGGING && !isMove) {
          handler.removeCallbacks(runnable);
        }
        if (newState == RecyclerView.SCROLL_STATE_IDLE) {
          if (isClick) {
            handler.postDelayed(runnable, 4000);
          }
        }
      }

      @Override
      public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        if (isMove) {
          if (vHeight < 0) {
            isMove = false;
            return;
          }
          scrollToMiddle();
        }
      }
    });
public void scrollToMiddle() {
    final int firstPosition = mLayoutManager.findFirstVisibleItemPosition();
    int top = mRecyclerView.getChildAt(position - firstPosition).getTop();
    Log.d(TAG, " position" + position + " " + top);
    int half = reHeight / 2;
    mRecyclerView.scrollBy(0, top - half);
    isMove = false;

  }

  public boolean isVisible() {
    final int firstPosition = mLayoutManager.findFirstVisibleItemPosition();
    final int lastPosition = mLayoutManager.findLastVisibleItemPosition();
    return position <= lastPosition && position >= firstPosition;
  }

  @Override
  protected void onDestroy() {
    super.onDestroy();
    handler.removeCallbacksAndMessages(null);
    handler = null;
  }
}


Related articles: