AutoScrollBackLayout

Introduction: 在 ListView 或者 RecyclerView 列表列表滚动向底部一段距离,就自动显示一个返回顶部的按钮
More: Author   ReportBugs   
Tags:
listview-recyclerview-scrolltop-hook-proxy-

在 ListView,GridView,RecyclerView 列表滚动向底部一段距离,就自动显示一个返回顶部的按钮

效果

使用

1、添加依赖:

compile ('com.gaoneng.library:autoscrollbacklayout:1.1.1'){
        exclude group: 'com.android.support', module: 'recyclerview-v7'
    }

2、通过 xml 文件添加如下:

<?xml version="1.0" encoding="utf-8"?>
<com.gaoneng.library.AutoScrollBackLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/scroll_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingLeft="8dp"
    android:paddingRight="8dp"
    app:show_scroll="true">

    <ListView
        android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:drawSelectorOnTop="false" />
</com.gaoneng.library.AutoScrollBackLayout>

3、调用 bindScrollBack():

AutoScrollBackLayout autoScrollBackLayout = (AutoScrollBackLayout) findViewById(R.id.scroll_layout);
ListView listView = (ListView) findViewById(android.R.id.list);
List<String> list = new ArrayList<>();
for (int i = 0; i < 20; i++) {
    list.add("this is a test! in " + i);
}
listView.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, list));
autoScrollBackLayout.bindScrollBack();

原理

  • 针对 ListView 和 GridView,通过反射和动态代理的方式监听 OnScrollListener,这样就不会影响已有的 OnScrollListener 的正常运行。 但是这里需要注意的是,如果已经使用AbsListView.setOnScrollListener()设置过监听, 一定要在其后面调用autoScrollBackLayout.bindScrollBack()
    private void hookScrollListenerForListview() {
          try {
              //通过反射获取 mOnScrollListener 对象
              Field scrollListenerField = AbsListView.class.getDeclaredField("mOnScrollListener");
              scrollListenerField.setAccessible(true);
              Object object = scrollListenerField.get(wrapView);
              //需要被代理的目前对象
              AbsListView.OnScrollListener target;
              if (object == null) {
                  //如果 mOnScrollListener 没有设置过,就设置一个空的用来 hook
                  target = new FakeScrollLitener();
              } else {
                  target = (AbsListView.OnScrollListener) object;
              }
              //InvocationHandler 对象,用于添加额外的控制处理
              ScrollListenerInvocationHandler listenerInvocationHandler = new ScrollListenerInvocationHandler(target);
              //Proxy.newProxyInstance 生成动态代理对象
              AbsListView.OnScrollListener proxy = listenerInvocationHandler.getProxy();
              if (DEBUG) {
                  Log.i(TAG, "target=" + target.getClass().getName() + " ,proxy=" + proxy.getClass().getName() + ", proxied interfaces=" + Arrays.toString(proxy.getClass().getInterfaces()));
              }
              //将代理对象 proxy 设置到被反射的 mOnScrollListener 的字段中
              scrollListenerField.set(wrapView, proxy);
          } catch (Exception e) {
              Log.e(TAG, e.toString());
          }
      }
    
  • 针对 RecyclerView,因为其内部的监听已经是List<OnScrollListener>形式,所以直接addOnScrollListener()方式添加即可;

    private void addScrollListenerForRecyclerView() {
          ((RecyclerView) wrapView).addOnScrollListener(new RecyclerView.OnScrollListener() {
              @Override
              public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                  if (myScrollLitener != null)
                      myScrollLitener.onScrollStateChanged(recyclerView, newState);
              }
    
              @Override
              public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                  if (myScrollLitener != null) myScrollLitener.onScroll(recyclerView, dy, 0, 0);
              }
          });
      }
    

其他属性

attrs value
app:show_scroll true/false 是否自动显示返回按钮,默认 true
app:scroll_distance dp 触发显示返回按钮的滚动距离,默认 100dp
app:show_animation anim 按钮出现动画,默认 R.anim.fab_scale_up
app:hide_animation anim 按钮出现动画,默认 R.anim.fab_scale_down
app:auto_arrow_icon drawable 按钮图标,默认 R.drawable.go_top
app:scroll_gravity Gravity 按钮的位置,默认 Gravity.BOTTOM and Gravity.CENTER_HORIZONTAL
Apps
About Me
GitHub: Trinea
Facebook: Dev Tools