RecyclerViewに高速スクロールを追加する方法
質問
背景
ListViewでは、スクロールバーをドラッグして好きなところに簡単にスクロールできる高速なスクローラーを持つことができました(using fastScrollEnabled 属性)
"と一緒に。 セクション インデクサ クラスと、オプションでいくつかの属性を追加すれば、このスクロールバーを使用したときに表示される素敵なポップアップを作成することができます (link ここで ).
連絡先アプリにこのようなものが表示され、特定の文字に簡単にスクロールできるようになりました。
問題点
RecyclerViewには、そのようなものはないようです。ファストスクロールすらありません。
質問
RecyclerViewに高速スクロール機能を追加するにはどうすればよいですか?
どのように解決するのですか?
新しい回答です。 時間が経つにつれて、私の元の答えは、特にViewPager内のフラグメントのための他のソリューションと比較して、いくつかの欠点があることに気づきました。
私は android-xソリューション バブルを必要としない場合、またはサードパーティのライブラリ( はこちら は良いものです)する場合に備えて。
old answer:
すべてのサードパーティライブラリに問題があったので、私は私が見つけられるものを集めることにしました(ほとんどは ここから )、すべてを修正し、RecyclerViewの高速スクロールの私自身のPOCを公開します。
https://github.com/AndroidDeveloperLB/LollipopContactsRecyclerViewFastScroller
の使い方を説明します。
-
BubbleTextGetterを実装したRecyclerView.Adapterを作成し、データ内の位置を指定すると、バブルポップアップに表示するテキストを返します。
-
RecyclerViewをコンテナとしているレイアウトの中にFastScrollerを配置します(おそらく右の領域にあります)。
-
FastScrollerをカスタマイズする FastScroller
いくつかのデメリット
- は方向転換をサポートしませんが、おそらく簡単に修正できます。
- は他の layoutManager をサポートしません。LinearLayoutManager のみです。
- API 11 以上が必要です。
コードです。
バブルテキストゲッター
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);
ファストスクローラー
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);
}
}
}
関連
-
[解決済み] Androidのソフトキーボードをプログラムで閉じる/隠すにはどうすればよいですか?
-
[解決済み] Androidでアクティビティ起動時にEditTextにフォーカスが当たらないようにする方法
-
[解決済み] インスタンス状態の保存を使用してアクティビティ状態を保存するにはどうすればよいですか?
-
[解決済み] RecyclerView onClick
-
[解決済み】「px」、「dip」、「dp」、「sp」の違いは?
-
[解決済み] Androidのソースコードにある@hideの意味とは?
-
[解決済み] アプリ内課金テスト:android.test.purchased already owned
-
[解決済み] アンドロイドのクライアントでヒープアップデートを有効にする方法
-
[解決済み] AndroidでTextViewの下にアンダーラインを引くには
-
[解決済み] TextView.setTextSizeの挙動がおかしい - テキストビューのテキストサイズを画面ごとに動的に設定する方法
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] 設定ページに移動せずに位置情報サービスをオンにする
-
[解決済み] SDカードからファイルを削除する方法を教えてください。
-
[解決済み] バイト配列の画像ファイルをビットマップに変換するには?
-
[解決済み] データベースでリサイクルビューを使用する
-
[解決済み] Studio 3.4 をアップデートしたら、引数の leftShift() メソッドが見つかりませんでした。
-
[解決済み] EditTextをReadOnlyにする
-
[解決済み] EditTextの右側のDrawableにonClickListenerを設定する [重複] [重複
-
[解決済み] TextView.setTextSizeの挙動がおかしい - テキストビューのテキストサイズを画面ごとに動的に設定する方法
-
[解決済み] PendingIntentの "requestCode "は何に使うのですか?
-
[解決済み] Android: xml リソースからの整数値