Android RecyclerView 代碼控制scroll時不能平緩滑動解決方案
作者:圣光啊那個敵人值得一戰(zhàn)
參考文章:http://blog.csdn.net/a86261566/article/details/50906456
參考文章作者:AndroidArenas
因為現(xiàn)在所在的公司的Android產(chǎn)品是發(fā)布在自己生產(chǎn)的設備上的,所以有時候會碰到比較奇葩的需求禀崖,比如周五就有一個衩辟,因為現(xiàn)在在做的是一款考勤軟件(其實早做好了給別人了但是里面有好多bug),而且它波附,不艺晴!帶!觸掸屡!摸封寞!屏!(不要問我是怎么實現(xiàn)界面跳轉(zhuǎn)的仅财,它帶物理鍵狈究。。盏求。我能怎么辦抖锥,我也很絕望啊)其中的課表在某一個區(qū)域顯示不全亿眠,所以讓它在自動滾動,本來當時就是簡單的一秒走一個item磅废,但是吧缕探。。周五讓產(chǎn)品經(jīng)理挑刺了(話說我做這個項目的時候我可從來沒見過這公司神出鬼沒的產(chǎn)品經(jīng)理还蹲,就連產(chǎn)品經(jīng)理是誰都是各種小道消息爹耗,簡直服氣)"這個課表滾動的效果不好,一下走一格跟屎一樣谜喊!"
好吧潭兽,確實跟屎一樣,我自己仔細瞅了瞅也覺得這么不是回事斗遏,那就改唄山卦。回去工位思索了了一下(其實就是百度來著)诵次,發(fā)現(xiàn)RecycleView滾動起來好像有點不按套路出牌账蓉,順著smoothScrollToPosition方法點進去看了下,它里面長這樣:
public void smoothScrollToPosition(int position) {
if (mLayoutFrozen) {
return;
}
if (mLayout == null) {
Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. " +
"Call setLayoutManager with a non-null argument.");
return;
}
mLayout.smoothScrollToPosition(this, mState, position);
}
可以看見它的最后調(diào)用了mlayout這個類的smoothScrollToPosition方法逾一,而點一下這個mlayout發(fā)現(xiàn)它的類型是LayoutManager的铸本,可以,那除了我們設給它的LayoutManager沒其他的了遵堵,馬上去看了下LinearLayoutManager里面的對應方法
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
int position) {
LinearSmoothScroller linearSmoothScroller =
new LinearSmoothScroller(recyclerView.getContext());
linearSmoothScroller.setTargetPosition(position);
startSmoothScroll(linearSmoothScroller);
}
來大家箱玷,我們看這個方法,他首先new了一個LinearSmoothScroller 類,點進去看注釋陌宿,可以锡足,沒看懂兄弟,那我們點進構(gòu)造函數(shù)看看
public LinearSmoothScroller(Context context) {
MILLISECONDS_PER_PX = calculateSpeedPerPixel(context.getResources().getDisplayMetrics());
}
兄弟們壳坪!不覺得返回的這個變量名很可疑嗎舶得!再點擊去看看。爽蝴。
/**
* Calculates the scroll speed.
*
* @param displayMetrics DisplayMetrics to be used for real dimension calculations
* @return The time (in ms) it should take for each pixel. For instance, if returned value is
* 2 ms, it means scrolling 1000 pixels with LinearInterpolation should take 2 seconds.
*/
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
}
來我們看這注釋沐批,恩,計算滾動速度 參數(shù)為dpi霜瘪,返回參數(shù)為1px滾動所需的時間(ms)
哎~這波就很nice了各位珠插,這就意味著我們只需要重寫smoothScrollToPosition這個方法,然后在里面再重寫calculateSpeedPerPixel這個方法就可以大概理論上動態(tài)改變顯示位置時的滾動速度了,我們首先繼承LinearLayoutManager類颖对,然后重寫其中的smoothScrollToPosition方法捻撑,例子如下:
@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, final int position) {
LinearSmoothScroller linearSmoothScroller = new LinearSmoothScroller(recyclerView.getContext()) {
@Override
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
return 5;//返回滾過1px需要多少ms
}
};
linearSmoothScroller.setTargetPosition(position);
startSmoothScroll(linearSmoothScroller);
}
然后將我們重寫的好的LayoutManager設到RecyclerView上,試驗了一下,滾動速度確實的變慢了顾患,但是番捂!我的需求并不只是僅僅變慢而已,我那是課表啊各位江解!所以我得知道它什么時候滾動到了最底部好讓我返回第一列重新滾動设预。
恩。犁河。鳖枕。。我是相信Google的開發(fā)者的桨螺,所以我相信他們絕對留下了接口來讓我知道滾動什么停止了的宾符,回到剛才的smoothScrollToPosition方法,可以看見最后一行的startSmoothScroll方法灭翔,套路不變魏烫,點進去
public void startSmoothScroll(SmoothScroller smoothScroller) {
if (mSmoothScroller != null && smoothScroller != mSmoothScroller
&& mSmoothScroller.isRunning()) {
mSmoothScroller.stop();
}
mSmoothScroller = smoothScroller;
mSmoothScroller.start(mRecyclerView, this);
}
恩,這里在判斷smoothScroller的實例在和以前不一樣以及其在滾動時會讓其立馬停止?jié)L動肝箱,也就是調(diào)了一下stop方法哄褒,我們再進stop方法看看?
final protected void stop() {
if (!mRunning) {
return;
}
onStop();
mRecyclerView.mState.mTargetPosition = RecyclerView.NO_POSITION;
mTargetView = null;
mTargetPosition = RecyclerView.NO_POSITION;
mPendingInitialRun = false;
mRunning = false;
// trigger a cleanup
mLayoutManager.onSmoothScrollerStopped(this);
// clear references to avoid any potential leak by a custom smooth scroller
mLayoutManager = null;
mRecyclerView = null;
}
恩煌张。呐赡。道理我都懂,這里各種初始化各種配置完了唱矛,但是第一行就調(diào)了個onStop什么意思罚舱?點進去
/**
* Called when smooth scroller is stopped. This is a good place to cleanup your state etc.
* @see #stop()
*/
abstract protected void onStop();
你看各位井辜,我就說他們肯定留接口了吧绎谦?
這樣我們就能知道什么時候滾動停止了,完整例子如下
/**
* Created by lip on 2017/2/24.
* 控制recycler滾動速度manager
*/
public class ControlRvSpeedLinearLayoutManager extends LinearLayoutManager {
public static final String NORMAL = "normal";
public static final String SLOW = "slow";
public static final String EXTREMELY_SLOW = "extremelySlow";
/**
* 滾動完成回調(diào)
*/
private StopScrollCallBack outStopScrollCallBack;
/**
* 滾動速度
*/
private String speed;
/**
*
* @param context 上下文
* @param stopScrollCallBack 滾動完成回調(diào)
* @param speed 滾動速度
*/
public ControlRvSpeedLinearLayoutManager(Context context, StopScrollCallBack stopScrollCallBack,String speed) {
super(context);
this.outStopScrollCallBack = stopScrollCallBack;
this.speed = speed;
}
@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, final int position) {
LinearSmoothScroller linearSmoothScroller = new LinearSmoothScroller(recyclerView.getContext()) {
@Override
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
Log.i("滾動demo","======="+displayMetrics.densityDpi);
float ms = 1f;
switch (speed){
case NORMAL:
ms = 1f;
break;
case SLOW:
ms = 5f;
break;
case EXTREMELY_SLOW:
ms = 10f;
break;
}
return ms;//返回滾過1px需要多少ms
}
@Override
protected void onStop() {
super.onStop();
outStopScrollCallBack.scrollStop(position);
}
};
linearSmoothScroller.setTargetPosition(position);
startSmoothScroll(linearSmoothScroller);
}
public interface StopScrollCallBack {
void scrollStop(int position);
}
}
這里我們傳入了一個接口回調(diào)StopScrollCallBack 好讓外面來實現(xiàn)具體在停止時的操作,使用方法如下:
BaseRecyclerAdapter<String> recyclerAdapter = new BaseRecyclerAdapter<String>(MainActivity.this, strings) {
@Override
public int getItemLayoutId(int viewType) {
return R.layout.text_rv_item_layout;
}
@Override
public void bindData(RecyclerView.ViewHolder holder, int position, String item) {
RecyclerViewHolder rv = (RecyclerViewHolder) holder;
rv.setText(R.id.tv_text, item);
}
};
ControlRvSpeedLinearLayoutManager controlRvSpeedLinearLayoutManager = new ControlRvSpeedLinearLayoutManager(MainActivity.this, new ControlRvSpeedLinearLayoutManager.StopScrollCallBack() {
@Override
public void scrollStop(final int position) {
handler.sendEmptyMessageDelayed(0,1000);
}
},ControlRvSpeedLinearLayoutManager.EXTREMELY_SLOW);
rvTextList.setLayoutManager(controlRvSpeedLinearLayoutManager);
rvTextList.addItemDecoration(new SpaceItemDecoration(20));
rvTextList.setAdapter(recyclerAdapter);
demo效果如下:
但是啊各位粥脚,不得不說一個坑窃肠,在回調(diào)回來的瞬間,不要立即進行任何的讓其滾動的操作刷允,因為各位看這一段代碼
public void startSmoothScroll(SmoothScroller smoothScroller) {
if (mSmoothScroller != null && smoothScroller != mSmoothScroller
&& mSmoothScroller.isRunning()) {
mSmoothScroller.stop();
}
mSmoothScroller = smoothScroller;
mSmoothScroller.start(mRecyclerView, this);
}
在座的各位冤留,看見沒,它會在判斷其還在滾動的時候調(diào)用stop树灶,而走我們回調(diào)的一瞬間它判斷就是在滾動纤怒,然后就會不停的走stop,馬上就拋stackoverflow了天通,所以各位泊窘。。。來個handler吧烘豹。瓜贾。。携悯。
具體demo在git上 https://github.com/LIPKKKK/RecyclerViewSmoothScrollDemo
感興趣的可以去看一下祭芦,謝謝支持