IT TIP

RecyclerView에 빠른 스크롤러를 추가하는 방법

itqueen 2020. 11. 1. 19:06
반응형

RecyclerView에 빠른 스크롤러를 추가하는 방법


배경

ListView에서는 스크롤바를 드래그하여 원하는 곳으로 쉽게 스크롤 할 수있는 빠른 스크롤러를 가질 수 있습니다 ( fastScrollEnabled 속성 사용).

" SectionIndexer "클래스 및 선택적으로 일부 속성과 함께이 스크롤바를 사용할 때 표시되는 멋진 팝업을 가질 수 있습니다 ( 여기 링크 ).

이러한 것은 연락처 앱에 표시되므로 특정 문자로 쉽게 스크롤 할 수 있습니다.

문제

RecyclerView에는 이러한 기능이없는 것 같습니다. 빠른 스크롤도 아닙니다.

질문

RecyclerView에 빠른 스크롤러 기능을 추가하려면 어떻게해야합니까?


며칠 전이 상황에 직면했을 때이 질문을 우연히 발견했습니다. RecyclerView에 대한 FastScroll의 구현 예 는 다음과 같습니다 .

github.com/danoz73/RecyclerViewFastScroller

예제 애플리케이션을 실행하고 코드를 자세히 살펴보고 간단한 RecyclerViewFastScroller 위젯의 사용법을 살펴보세요. github에 대한 정보가 있지만 여기에 수직 고속 스크롤러에 대한 기본 사용법을 포함하겠습니다.

전체 예제 는 repo샘플 애플리케이션을 참조하십시오 .

기본 사용법

RecyclerView가있는 활동 또는 조각 XML에 VerticalRecyclerViewFastScroller 개체를 포함합니다. 다음 예는 상대 레이아웃에 있습니다.

...
  <android.support.v7.widget.RecyclerView
      android:id="@+id/recyclerView"
      android:layout_width="match_parent"
      android:layout_height="match_parent"/>


  <xyz.danoz.recyclerviewfastscroller.vertical.VerticalRecyclerViewFastScroller
      android:id="@+id/fast_scroller"
      android:layout_width="@dimen/however_wide_you_want_this"
      android:layout_height="match_parent"
      android:layout_alignParentRight="true"
    />
...

프로그래밍 방식으로 레이아웃을 설정하는 프래그먼트 또는 활동에서 빠른 스크롤러를 리사이클 러에 연결합니다.

...
  public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
      View rootView = inflater.inflate(R.layout.recycler_view_frag, container, false);
      ...

      // Grab your RecyclerView and the RecyclerViewFastScroller from the layout
      RecyclerView recyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerView);
      VerticalRecyclerViewFastScroller fastScroller = (VerticalRecyclerViewFastScroller) rootView.findViewById(R.id.fast_scroller);

      // Connect the recycler to the scroller (to let the scroller scroll the list)
      fastScroller.setRecyclerView(recyclerView);

      // Connect the scroller to the recycler (to let the recycler scroll the scroller's handle)
      recyclerView.setOnScrollListener(fastScroller.getOnScrollListener());

      ...
      return rootView;
  }
...

도움이 되었기를 바랍니다!

편집 : 이제 Android-Lollipop-Contacts 스타일 섹션 표시기에 대한 지원이 추가되었습니다! 자세한 내용샘플 애플리케이션의 구현을 확인하십시오.


모든 타사 라이브러리에 문제가 있었기 때문에 찾을 수있는 항목 (대부분 여기에서 ) 을 수집하고 모든 것을 수정하고 RecyclerView의 빠른 스크롤러에 대한 내 자신의 POC를 게시 하기로 결정했습니다 .

https://github.com/AndroidDeveloperLB/LollipopContactsRecyclerViewFastScroller

용법:

  1. BubbleTextGetter를 구현하는 RecyclerView.Adapter를 만듭니다. 데이터의 위치를 ​​지정하면 풍선 팝업에 표시 할 텍스트가 반환됩니다.

  2. RecyclerView (아마 오른쪽 영역)를 컨테이너하는 레이아웃 내부에 FastScroller를 배치합니다.

  3. FastScroller FastScroller 사용자 지정

몇 가지 단점 :

  1. 방향 변경을 지원하지 않지만 쉽게 수정할 수 있습니다.
  2. 다른 layoutManager를 지원하지 않습니다. LinearLayoutManager 만
  3. API 11 이상이 필요합니다.

암호:

BubbleTextGetter

public interface BubbleTextGetter
  {
  String getTextToShowInBubble(int pos);
  }

recycler_view_fast_scroller__fast_scroller.xml

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
       xmlns:tools="http://schemas.android.com/tools"
       android:layout_width="wrap_content"
       android:layout_height="match_parent">

  <TextView
    android:id="@+id/fastscroller_bubble"
    android:layout_gravity="right|end"
    android:gravity="center"
    android:textSize="48sp" tools:text="A"
    android:layout_width="wrap_content"
    android:textColor="#FFffffff"
    android:layout_height="wrap_content"
    android:background="@drawable/recycler_view_fast_scroller__bubble"
    android:visibility="visible"/>

  <ImageView
    android:id="@+id/fastscroller_handle"
    android:layout_width="wrap_content"
    android:layout_marginRight="8dp"
    android:layout_marginLeft="8dp"
    android:layout_height="wrap_content"
    android:src="@drawable/recycler_view_fast_scroller__handle"/>

</merge>

주요 활동

...
fastScroller=(FastScroller)findViewById(R.id.fastscroller);
fastScroller.setRecyclerView(recyclerView);

FastScroller

public class FastScroller extends LinearLayout
  {
  private static final int BUBBLE_ANIMATION_DURATION=100;
  private static final int TRACK_SNAP_RANGE=5;

  private TextView bubble;
  private View handle;
  private RecyclerView recyclerView;
  private final ScrollListener scrollListener=new ScrollListener();
  private int height;

  private ObjectAnimator currentAnimator=null;

  public FastScroller(final Context context,final AttributeSet attrs,final int defStyleAttr)
    {
    super(context,attrs,defStyleAttr);
    initialise(context);
    }

  public FastScroller(final Context context)
    {
    super(context);
    initialise(context);
    }

  public FastScroller(final Context context,final AttributeSet attrs)
    {
    super(context,attrs);
    initialise(context);
    }

  private void initialise(Context context)
    {
    setOrientation(HORIZONTAL);
    setClipChildren(false);
    LayoutInflater inflater=LayoutInflater.from(context);
    inflater.inflate(R.layout.recycler_view_fast_scroller__fast_scroller,this,true);
    bubble=(TextView)findViewById(R.id.fastscroller_bubble);
    handle=findViewById(R.id.fastscroller_handle);
    bubble.setVisibility(INVISIBLE);
    }

  @Override
  protected void onSizeChanged(int w,int h,int oldw,int oldh)
    {
    super.onSizeChanged(w,h,oldw,oldh);
    height=h;
    }

  @Override
  public boolean onTouchEvent(@NonNull MotionEvent event)
    {
    final int action=event.getAction();
    switch(action)
      {
      case MotionEvent.ACTION_DOWN:
        if(event.getX()<handle.getX())
          return false;
        if(currentAnimator!=null)
          currentAnimator.cancel();
        if(bubble.getVisibility()==INVISIBLE)
          showBubble();
        handle.setSelected(true);
      case MotionEvent.ACTION_MOVE:
        setPosition(event.getY());
        setRecyclerViewPosition(event.getY());
        return true;
      case MotionEvent.ACTION_UP:
      case MotionEvent.ACTION_CANCEL:
        handle.setSelected(false);
        hideBubble();
        return true;
      }
    return super.onTouchEvent(event);
    }

  public void setRecyclerView(RecyclerView recyclerView)
    {
    this.recyclerView=recyclerView;
    recyclerView.setOnScrollListener(scrollListener);
    }

  private void setRecyclerViewPosition(float y)
    {
    if(recyclerView!=null)
      {
      int itemCount=recyclerView.getAdapter().getItemCount();
      float proportion;
      if(handle.getY()==0)
        proportion=0f;
      else if(handle.getY()+handle.getHeight()>=height-TRACK_SNAP_RANGE)
        proportion=1f;
      else
        proportion=y/(float)height;
      int targetPos=getValueInRange(0,itemCount-1,(int)(proportion*(float)itemCount));
      recyclerView.scrollToPosition(targetPos);
      String bubbleText=((BubbleTextGetter)recyclerView.getAdapter()).getTextToShowInBubble(targetPos);
      bubble.setText(bubbleText);
      }
    }

  private int getValueInRange(int min,int max,int value)
    {
    int minimum=Math.max(min,value);
    return Math.min(minimum,max);
    }

  private void setPosition(float y)
    {
    int bubbleHeight=bubble.getHeight();
    int handleHeight=handle.getHeight();
    handle.setY(getValueInRange(0,height-handleHeight,(int)(y-handleHeight/2)));
    bubble.setY(getValueInRange(0,height-bubbleHeight-handleHeight/2,(int)(y-bubbleHeight)));
    }

  private void showBubble()
    {
    AnimatorSet animatorSet=new AnimatorSet();
    bubble.setVisibility(VISIBLE);
    if(currentAnimator!=null)
      currentAnimator.cancel();
    currentAnimator=ObjectAnimator.ofFloat(bubble,"alpha",0f,1f).setDuration(BUBBLE_ANIMATION_DURATION);
    currentAnimator.start();
    }

  private void hideBubble()
    {
    if(currentAnimator!=null)
      currentAnimator.cancel();
    currentAnimator=ObjectAnimator.ofFloat(bubble,"alpha",1f,0f).setDuration(BUBBLE_ANIMATION_DURATION);
    currentAnimator.addListener(new AnimatorListenerAdapter()
    {
    @Override
    public void onAnimationEnd(Animator animation)
      {
      super.onAnimationEnd(animation);
      bubble.setVisibility(INVISIBLE);
      currentAnimator=null;
      }

    @Override
    public void onAnimationCancel(Animator animation)
      {
      super.onAnimationCancel(animation);
      bubble.setVisibility(INVISIBLE);
      currentAnimator=null;
      }
    });
    currentAnimator.start();
    }

  private class ScrollListener extends OnScrollListener
    {
    @Override
    public void onScrolled(RecyclerView rv,int dx,int dy)
      {
      View firstVisibleView=recyclerView.getChildAt(0);
      int firstVisiblePosition=recyclerView.getChildPosition(firstVisibleView);
      int visibleRange=recyclerView.getChildCount();
      int lastVisiblePosition=firstVisiblePosition+visibleRange;
      int itemCount=recyclerView.getAdapter().getItemCount();
      int position;
      if(firstVisiblePosition==0)
        position=0;
      else if(lastVisiblePosition==itemCount-1)
        position=itemCount-1;
      else
        position=firstVisiblePosition;
      float proportion=(float)position/(float)itemCount;
      setPosition(height*proportion);
      }
    }
  }

RecyclerView빠른 스크롤 / 섹션 인덱서 에 대한 답변되지 않은 질문이 많이 있습니다. 여기에서 의견과 정보를 다시 모아서 수집 해 보겠습니다.

짧은 답은 NO, 당신은 빠른 속도로 스크롤을 활성화 할 수 없습니다 때문에 RecyclerView이 포함하지 않는 FastScroller의 객체와도 관련된 모든 논리 상태 변수를. 이것은 AbsListViewRecyclerView 가 아니기 때문 입니다 .

반면에 RecyclerView덤핑 된 버전 FastScroller과 빠른 스크롤링에 필요한 논리를 포함하는를 구현하는 것은 불가능하지 않지만 지금까지 구현 된 것을 보지 못했습니다.

그것에 대한 고려 사항을 공유하거나 내가 틀렸다고 생각하는 경우 공유하십시오.


Android 지원 라이브러리 26.0.0은 이제 fastScrollEnabled

RecyclerView에 대한 새로운 fastScrollEnabled 부울 플래그.

활성화 된 경우 fastScrollHorizontalThumbDrawable, fastScrollHorizontalTrackDrawable, fastScrollVerticalThumbDrawable 및 fastScrollVerticalTrackDrawable을 설정해야합니다.

샘플-https: //android.jlelse.eu/fast-scrolling-with-recyclerview-2b89d4574688


RecyclerView에 AZ Fastscroll을 사용할 수도 있습니다. iOS 스타일입니다.

https://github.com/code-computerlove/FastScrollRecyclerView/

이것을 어떻게 사용 하는가:

  • 교체 android.support.v7.widget.RecyclerViewcom.codecomputerlove.fastscrollrecyclerviewdemo.FastScrollRecyclerView
  • 어댑터는 FastScrollRecyclerViewInterface를 구현하고 getMapIndex(). 이 함수는 mapIndex를 반환해야합니다. calculateIndexesForName()그것을 만드는 방법 대한 영감을 찾으 십시오. 생성되면 생성자의 어댑터에 전달합니다.
  • 의 인스턴스를 FastScrollRecyclerViewItemDecoration만들고 RecyclerView에 추가하세요.FastScrollRecyclerViewItemDecoration decoration = new FastScrollRecyclerViewItemDecoration(this); mRecyclerView.addItemDecoration(decoration);
  • 추가 <dimen name="fast_scroll_overlay_text_size">100dp</dimen>사용자에게 /values/dimens.xml파일. 오버레이 된 문자의 dp 크기입니다.

lib : https://github.com/FutureMind/recycler-fast-scroll을 사용해 볼 수 있습니다 . 아직 초기 개발 단계이지만 다른 라이브러리에서 경험 한 부드러움 문제를 해결하기 위해 특별히 제작되었습니다. 약간 다른 메커니즘을 사용합니다. 수평 LayoutManager도 지원하며 가까운 장래에 다중 열 설정도 지원합니다.

편집 : 이제 몇 가지 깔끔한 사용자 정의 옵션이 있습니다.


RecyclerView 용 Android 라이브러리 26.0.0에서 FastScroller 기능이 추가되었습니다.

의존성 컴파일

    compile 'com.android.support:recyclerview-v7:26.1.0'
    compile 'com.android.support:design:26.1.0'

project.gradle에 종속성 추가

     maven {
            url "https://maven.google.com"
        }

recyclerview.xml 파일

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    xmlns:tool="http://schemas.android.com/tools"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    tool:context=".MainActivity">
    <android.support.v7.widget.RecyclerView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                xmlns:app="http://schemas.android.com/apk/res-auto"
                android:id="@+id/songlist"
                android:layout_marginStart="8dp"
                android:layout_marginEnd="8dp"
                app:fastScrollEnabled="true"
              app:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable"
                app:fastScrollVerticalTrackDrawable="@drawable/line_drawable"
                app:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable"
                app:fastScrollHorizontalTrackDrawable="@drawable/line_drawable"
               /></LinearLayout>

thumb.xml

   <?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="rectangle">

    <corners
        android:topLeftRadius="44dp"
        android:topRightRadius="44dp"
        android:bottomLeftRadius="44dp"
        android:bottomRightRadius="44dp" />

    <padding
        android:paddingLeft="22dp"
        android:paddingRight="22dp" />

    <solid android:color="#f73831" />

</shape>

line.xml

    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="rectangle">

    <solid android:color="@color/dark_grey" />

    <padding
        android:top="10dp"
        android:left="10dp"
        android:right="10dp"
        android:bottom="10dp"/>
</shape>

thumb_drawable.xml

    <?xml version="1.0" encoding="utf-8"?>
    <selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/thumb"
        android:state_focused="true"
        android:state_pressed="true" />

    <item android:drawable="@drawable/thumb"
        android:state_focused="false"
        android:state_pressed="true" />
    <item android:drawable="@drawable/thumb" 
            android:state_focused="true" />
    <item android:drawable="@drawable/thumb"
        android:state_focused="false"
        android:state_pressed="false" />
</selector>

line_drawble.xml

    <?xml version="1.0" encoding="utf-8"?>
    <selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/line"
        android:state_focused="true"
        android:state_pressed="true" />

    <item android:drawable="@drawable/line"
        android:state_focused="false"
        android:state_pressed="true" />
    <item android:drawable="@drawable/line" 
            android:state_focused="true" />
    <item android:drawable="@drawable/line"
        android:state_focused="false"
        android:state_pressed="false" />
</selector>

RecycleView.NET을 사용하여 스크롤 막대를 구현하는 조항이 있습니다 LayoutManager.

예 : computeVerticalScrollExtent(), computeVerticalScrollOffset()computeVerticalScrollRange()항상 올바른 위치에 세로 스크롤 썸을 배치하기위한 정보를 제공 할 수 있습니다.

이러한 방법은 LayoutManager실제 측정을 위임 하는데도 있습니다 . 따라서 사용 된 LayoutManager구현은 이러한 측정을 지원해야합니다.

Also, drag touch on scroll thumb can be intercepted by overriding onInterceptTouchEvent() of RecyclerView. And after calculating the desired scroll, scrollTo() can be called to update RecyclerView.


Just enable fast scrolling and add thumb, tracker for scroll bar like below.

<android.support.v7.widget.RecyclerView
android:id="@+id/rv_sensors"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:fastScrollEnabled="true"
app:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable"
app:fastScrollHorizontalTrackDrawable="@drawable/line_drawable"
app:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable"
app:fastScrollVerticalTrackDrawable="@drawable/line_drawable" />

참고URL : https://stackoverflow.com/questions/27798212/how-to-add-a-fast-scroller-to-the-recyclerview

반응형