Android RecyclerView 滚动到中间位置
最近看到QQ音乐的歌词每次滑动后都可以滚回到中间位置。觉得甚是神奇,打开开发者模式显示布局,发现歌词部分不是采用 android 控件的写的,应该是前端写的。于是,我想,能不能用 recyclerView 实现这个自动回滚到中间位置呢。
功夫不负有心人,查找了一些资料之后,终于搞定了。
下面由我细细讲来。
目标
点击某个条目,在经过4s无任何操作之后,该条目滚动到中间位置显示。点击后,用户在滑动,等用户不操作后再开始延时。用户多次点击,记最后一次点击位置。
分析
首先先考虑,滚动到指定位置是如何操作的?
// 滚动到指定位置
recyclerView.scrollToPosition(position);
// 平滑滚动到指定位置
recyclerView.smoothScrollToPosition(position);
有没有滚动到制定像素位置呢?
// scrollBy(x, y)这个方法是自己去控制移动的距离,单位是像素,所以在使用scrollBy(x, y)需要自己去计算移动的高度或宽度。
recyclerView.scrollBy(x, y)
可是,问题是滚动到中间位置啊?这个怎么办呢?这样子行不行呢?
mRecyclerView.scrollToPosition(0);
mRecyclerView.scrollBy(0,400);
先滚动到制定位置,在滚动一段距离不就好了?运行发现,这两行代码只执行第一行,第二行无效。
debug 调试看了下,还是没有弄懂,实现太复杂。
那就是说这样是不行的,那有没有其他办法呢?
RecyclerView 有一个滚动监听方法:
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);
}
});
onScrollStateChanged 方法对应三种状态:静止(SCROLL_STATE_IDLE),拖动滚动(SCROLL_STATE_DRAGGING),滑动(SCROLL_STATE_SETTLING)。
当手动缓慢滑动的时候,会触发:onScrollStateChanged (拖动滚动) --> (n个)onScrolled -->onScrollStateChanged(静止);
当手快速滑动的时候,会触发:onScrollStateChanged (拖动滚动) --> (n个)onScrolled --> onScrollStateChanged (滑动) -->
(n个)onScrolled --> onScrollStateChanged (静止);
有想法了,点击的时候,先运行 scrollToPosition,在 onScrolled 方法里面 运行 scrollBy 方法。写代码,运行,通过。
下面就是中间位置的计算了。
首先计算出 recylerview 的展现高度。
Rect rect = new Rect();
mRecyclerView.getGlobalVisibleRect(rect);
reHeight = rect.bottom - rect.top - vHeight;
当运行 scrollToPosition 后,点击条目就会出现在视野当中,这时候,计算出相应的位移即可。需要注意一点的是,当点击条目在视野内的时候,是不会运行 scrollToPosition 方法的。
int top = mRecyclerView.getChildAt(position - firstPosition).getTop();
int half = reHeight / 2;
mRecyclerView.scrollBy(0, top - half);
最后就是延时的设定,采用Handler 进行延时。
代码
核心代码如下:
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);
//创建默认的线性LayoutManager
mLayoutManager = new LinearLayoutManager(this);
mLayoutManager.setAutoMeasureEnabled(true);
mRecyclerView.setLayoutManager(mLayoutManager);
//如果可以确定每个item的高度是固定的,设置这个选项可以提高性能
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, "第" + pos + "行", 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;
}
}
第二种方法
可以直接采用 scrollToPositionWithOffset 方法。
if (mLayoutManager != null && mLayoutManager instanceof LinearLayoutManager) {
((LinearLayoutManager) mLayoutManager)
.scrollToPositionWithOffset(position, half);
}
scrollToPositionWithOffset 会直接把你想滚动的条目滚动到顶部,第二个参数是用来控制滚动的偏移量,距离顶部多少距离,这样的话,我们就不用写上面那么多代码啦。
Android RecyclerView 滚动到中间位置的更多相关文章
- Android RecyclerView滚动类控件修改、去掉滑动边界的阴影效果
前言 滚动类控件,大家都用的很多,如 RecyclerView.NestedSrollView.... 下面以recyclerView为例讲解,其他滚动控件也同理. RecyclerView 滚动列表 ...
- Android 记录和恢复ListView滚动的位置的三种方法
本文主要介绍记录和恢复listView滚动位置的3种方法(1)记录listView滚动到的位置的坐标(推荐)(2)记录listView显示在屏幕上的第一个item的位置(3)通知适配器数据改变. 有时 ...
- Android RecyclerView的基本使用
Android RecyclerView 在去年的Google I/O大会上就推出来了,以前经常使用的ListView 继承的是AbsListView,而RecyclerView则直接继承 ViewG ...
- (转载) Android RecyclerView 使用完全解析 体验艺术般的控件
Android RecyclerView 使用完全解析 体验艺术般的控件 标签: Recyclerviewpager瀑布流 2015-04-16 09:07 721474人阅读 评论(458) 收藏 ...
- Android RecyclerView 实现支付宝首页效果
Android RecyclerView 实现支付宝首页效果 [TOC] 虽然我本人不喜欢支付宝的,但是这个网格本身其实还是不错的,项目更新中更改了一个布局为网格模式,类似支付宝.(估计是产品抄袭的= ...
- Android RecyclerView(瀑布流)水平/垂直方向分割线
Android RecyclerView(瀑布流)水平/垂直方向分割线 Android RecyclerView不像过去的ListView那样随意的设置水平方向的分割线,如果要实现Recycle ...
- Android RecyclerView完全解析
RecyclerView完全解析 (一) 前言 话说RecyclerView已经面市很久,也在很多应用中得到广泛的使用,在整个开发者圈子里面也拥有很不错的口碑,那说明RecyclerView拥有比Li ...
- [Android]RecyclerView的简单演示样例
去年google的IO上就展示了一个新的ListView.它就是RecyclerView. 下面是官方的说明,我英语能力有限,只是我大概这么理解:RecyclerView会比ListView更具有拓展 ...
- Android View各种尺寸位置相关的方法探究
Android View各种尺寸位置相关的方法探究 本来想做一个View间的碰撞检测之类的. 动手做了才发现不是想象的那么简单. 首先,写好了碰撞检测的工具类如下: package com.mengd ...
随机推荐
- angular4升级angular5问题记录之this.location.back()
在之前的项目中,导航回上一个路由采用注入的Location服务,利用浏览器的历史堆栈,导航到上一步. 官方文档也就是这么写的 而然在升级到5.2的版本的时候,在浏览器运行的时候并没有什么问题,在项目打 ...
- shiro笔记-AuthenticatingRealm和AuthorizingRealm关系
AuthenticatingRealm-------->用于认证方法的Realm AuthorizingRealm--------->用于授权和认证的realm一般使用这个 Authori ...
- Web API 之承载宿主IIS,SelfHost,OwinSelfHost
Asp.Net WebAPI这个大家应该都不陌生,在我的理解范围中就是数据提供和交换的一个方式,相比与WCF,WS而言,更加的简单轻量,但是在部署web Api的时候,一般往往需要与a ...
- java线程间通信1--简单实例
线程通信 一.线程间通信的条件 1.两个以上的线程访问同一块内存 2.线程同步,关键字 synchronized 二.线程间通信主要涉及的方法 wait(); ----> 用于阻塞进程 noti ...
- 《android开发进阶从小工到专家》读书笔记--HTTP网络请求
No1: 客户端与服务器的交互流程: 1)客户端执行网络请求,从URL中解析出服务器的主机名 2)将服务器的主机名转换成服务器的IP地址 3)将端口号从URL中解析出来 4)建立一条从客户端与Web服 ...
- hdu4825 01字典树+贪心
从高位向低位构造字典树,因为高位得到的数更大. AC代码: #include<cstdio> using namespace std; typedef long long LL; cons ...
- LOJ6002 - 「网络流 24 题」最小路径覆盖
原题链接 Description 求一个DAG的最小路径覆盖,并输出一种方案. Solution 模板题啦~ Code //「网络流 24 题」最小路径覆盖 #include <cstdio&g ...
- Luogu P1690 贪婪的Copy
题目描述 Copy从卢牛那里听说在一片叫yz的神的领域埋藏着不少宝藏,于是Copy来到了这个被划分为个区域的神地.卢牛告诉了Copy这里共有个宝藏,分别放在第Pi个(1<=Pi<=N)区域 ...
- 【DDD】领域驱动设计实践 —— 一些问题及想法
在社区系统的DDD实践过程中,将遇到一些问题和产生的想法记录下来,共讨论. 本文为[DDD]系列文章中的其中一篇,其他内容可参考:使用领域驱动设计思想实现业务系统. 1.dto.model和entit ...
- NLP+语义分析(四)︱中文语义分析研究现状(CIPS2016、角色标注、篇章分析)
摘录自:CIPS2016 中文信息处理报告<第二章 语义分析研究进展. 现状及趋势>P14 CIPS2016> 中文信息处理报告下载链接:http://cips-upload.bj. ...