鍍金池/ 教程/ Android/ RecyclerView FastScroll – Part 2
原文鏈接
Issue #185
Issue #181
Issue #161
Issue #192
Issue #174
Issue #190
RecyclerView FastScroll – Part 2
僅作為Android 調(diào)試模式工具的Stetho
Issue #150
Issue #167
Issue #180
Issue #151
Issue #188
Issue #159
Issue #189
Issue #160
Issue #168
Issue #146
Issue #173
Issue #198
Issue #179
延期的共享元素轉(zhuǎn)換(3b)
Yahnac:RxJava Firebase&內(nèi)容提供
Issue #162
游戲性能:規(guī)劃限定條件
分析清單:測量和尋找哪些方面
Issue #148
Issue #166
Issue #158
Issue #178
Issue #193
Issue #145
Issue #170
Issue #169
Issue #196
Issue #186
Issue #172
Issue #171
附加Android工件和Gradle的檔案
Issue #147
自定義顏色范圍
根據(jù) Material 設(shè)計(jì)導(dǎo)航制圖工具樣式
Issue #187
Issue #184
Issue #175
在Android Lollipop上使用JobScheduler API
Android性能案例追蹤研究
使用安卓Wear API創(chuàng)建watchface—第2部分
在谷歌市場上創(chuàng)造更好的用戶體驗(yàn)
映射與包的神秘關(guān)系
Issue #165
用Robolectric進(jìn)行參數(shù)化測試
Issue #155
Issue #149
MVC / MVP中的M -模型
歡迎為 Android 和 iOS 嵌入 API
Issue #164
Android UI 自動化測試
Issue #182
Issue #191
Issue #183
Issue #163
Issue #157
響應(yīng)式編程(Reactive Programming)介紹
Issue #197
原文鏈接
Issue #153
Issue #152
Issue #176
原文地址
Android Material 支持庫:Electric Boogaloo的提示與技巧
Issue #156
Issue #154
Android的模糊視圖
Issue #194
Issue #177
Issue #195
針對Jenkins的谷歌商店安卓出版插件

RecyclerView FastScroll – Part 2

請留言之前的文章中,我們學(xué)會了 FastScroller 控制框架。在這一結(jié)束篇中,我們將添加觸摸和滾動行為。

http://wiki.jikexueyuan.com/project/android-weekly/images/issue-145/31.gif" alt="Recycler" />

首先我們需要的是一種內(nèi)部方法,當(dāng)由于 FastScroller 的觸摸事件或者 用戶滾動了 RecyclerView,滾動的位置變化時(shí),為了設(shè)置 bubble 和 handle 的位置,該方法會被調(diào)用:

FastScroller.java

   public class FastScroller extends LinearLayout {
    .
    .
    .
    private void setPosition(float y) {
        float position = y / height;
        int bubbleHeight = bubble.getHeight();
        bubble.setY(getValueInRange(0, height - bubbleHeight, (int) ((height - bubbleHeight) * position)));
        int handleHeight = handle.getHeight();
        handle.setY(getValueInRange(0, height - handleHeight, (int) ((height - handleHeight) * position)));
    }

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

這里需要一些數(shù)學(xué)知識,因?yàn)?handle 和 bubble 是不同高度的,且我們需要單獨(dú)的處理每一個(gè)。當(dāng)滾動的時(shí)候,我們想讓每一個(gè)都有自己的上邊緣。

列表中的項(xiàng)目時(shí)可見的。

getValueInRange() 是一個(gè)實(shí)用程序方法,確保 bubble 和 handle 在它們的追蹤中。

我們的 FastScroller 控制與 RecyclerView 相關(guān)聯(lián),所以下一個(gè)任務(wù)是提供一種機(jī)制,使用一個(gè)簡單的設(shè)值函數(shù)來實(shí)現(xiàn)關(guān)聯(lián):
FastScroller.java

    public class FastScroller extends LinearLayout {

        private final ScrollListener scrollListener = new ScrollListener();

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

        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);
            }
        }
    }

當(dāng)調(diào)用設(shè)值函數(shù)時(shí),設(shè)置一個(gè) OnScrollListener 實(shí)例,當(dāng)用戶直接 scroll RecyclerView 時(shí),該實(shí)例會被調(diào)用,由此我們可以調(diào)整 handle 和 buddle 的位置。為了在列表頂部和底部提供正確的位置,需要一些邏輯知識。

接下來我們需要看 FastScroller 控制中處理觸摸事件。我們希望實(shí)現(xiàn)的行為是:當(dāng)用戶在控制中輕觸時(shí),handle 會出現(xiàn)。用戶可以上下拖動來改變當(dāng)前位置。當(dāng)用戶釋放時(shí),在 handle 隱藏之前會有一個(gè)短暫的延遲。這是通過覆蓋 onTouchEvent() 實(shí)現(xiàn)的:
FastScroller.java

    public class FastScroller extends LinearLayout {
    .
    .
    .
    private static final int HANDLE_HIDE_DELAY = 1000;
    private static final int TRACK_SNAP_RANGE = 5;

    private final HandleHider handleHider = new HandleHider();

    @Override
    public boolean onTouchEvent(@NonNull MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE) {
            setPosition(event.getY());
            if (currentAnimator != null) {
                currentAnimator.cancel();
            }
            getHandler().removeCallbacks(handleHider);
            if (handle.getVisibility() == INVISIBLE) {
                showHandle();
            }
            setRecyclerViewPosition(event.getY());
            return true;
        } else if (event.getAction() == MotionEvent.ACTION_UP) {
            getHandler().postDelayed(handleHider, HANDLE_HIDE_DELAY);
            return true;
        }
        return super.onTouchEvent(event);
    }

    private class HandleHider implements Runnable {
        @Override
        public void run() {
            hideHandle();
        }
    }

    private void setRecyclerViewPosition(float y) {
        if (recyclerView != null) {
            int itemCount = recyclerView.getAdapter().getItemCount();
            float proportion;
            if (bubble.getY() == 0) {
                proportion = 0f;
            } else if (bubble.getY() + bubble.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);
        }
    }
    .
    .
    .
}

當(dāng)接收一個(gè)向下或移動的動作時(shí),我們設(shè)置當(dāng)前的位置來與當(dāng)前的 Y 位置匹配,取消可能運(yùn)行的動畫,取消延遲的處理程序回調(diào)(關(guān)于這點(diǎn)在第二部分中有更多描述)。如果 handle 不可見,那我們調(diào)用之前創(chuàng)建的方法使它顯現(xiàn)。最后在重調(diào) true 之前,我們設(shè)置 RecyclerView 的當(dāng)前位置來使用觸發(fā)事件。

當(dāng)接收一個(gè)向上的動作時(shí),在一個(gè)短暫的延遲后,我們使用一個(gè) Handler 發(fā)布一個(gè)延遲動作來隱藏 handle。

當(dāng)我們設(shè)置 RecyclerView 的位置時(shí),如果我們在底部一定距離內(nèi)來與底部對齊,或若第一項(xiàng)可見時(shí)與頂部對齊,那么我們要明白其邏輯關(guān)系,否則,如果在中間的某個(gè)位置,那就需要計(jì)算出正確的比例值。

注意在這里我們只使用 scrollToPosition() ,而不用 smoothScrollToPosition(),所以在之前的系列中不會出現(xiàn)問題。

這是我們控制完成的。剩下的就是連接。首先我們將它添加到包含 RecyclerView 的布局:
res/layout/activity_main.xml

    <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">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:paddingLeft="@dimen/activity_horizontal_margin"
            android:paddingRight="@dimen/activity_horizontal_margin"
            android:paddingTop="@dimen/activity_vertical_margin"
            android:paddingBottom="@dimen/activity_vertical_margin"
            android:scrollbars="none"
            tools:context=".MainActivity" />

        <com.stylingandroid.smoothscrolling.FastScroller
            android:id="@+id/fast_scroller"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_alignParentEnd="true" />
    </RelativeLayout> 

最后我們需要在 RecyclerViewFastScroller 之間創(chuàng)建聯(lián)系:
MainActivity.java

    public class MainActivity extends Activity {
    .
    .
    .
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
        recyclerView.setAdapter(LargeAdapter.newInstance(this));
        int duration = getResources().getInteger(R.integer.scroll_duration);
        recyclerView.setLayoutManager(new ScrollingLinearLayoutManager(this, LinearLayoutManager.VERTICAL, false, duration));
        FastScroller fastScroller = (FastScroller) findViewById(R.id.fastscroller);
        fastScroller.setRecyclerView(recyclerView);
    }
    .
    .
    .
}

就這樣。現(xiàn)在我們可以看到 fas 滾動行為:

最后一點(diǎn):在聯(lián)系人應(yīng)用程序中,F(xiàn)astScroller 處理包含一個(gè)字母表示的列表中的當(dāng)前位置。這是使用了一個(gè)比我們的示例稍微復(fù)雜的適配器,但是添加這個(gè)不應(yīng)該是繁瑣的工作。也許這是我們在以后的部分中要討論的。

可用的源代碼 here。

Mark Allison.版權(quán)所有。這篇文章最開始出現(xiàn)在 Styling Android 。

這個(gè)頁面的部分內(nèi)容是基于 Google 創(chuàng)建和共享的工作修改的。

上一篇:Issue #150下一篇:Issue #161