Android使用RecyclerView實(shí)現(xiàn)水平滾動(dòng)控件
前言
相信大家都知道Android滾動(dòng)控件的實(shí)現(xiàn)方式有很多, 使用RecyclerView也比較簡(jiǎn)單. 做了一個(gè)簡(jiǎn)單的年齡滾動(dòng)控件, 讓我們來看看RecyclerView的使用方式, 主要有以下幾點(diǎn):
(1) 對(duì)齊控件中心位置.
(2) 計(jì)算滾動(dòng)距離.
(3) 高亮中心視圖.
(4) 實(shí)時(shí)顯示中心數(shù)據(jù).
(5) 停止時(shí)自動(dòng)對(duì)齊.
(6) 滾動(dòng)時(shí), 設(shè)置按鈕狀態(tài)開關(guān).
效果

1. 框架
主要關(guān)注RecyclerView部分邏輯.
/**
* 初始化年齡滑動(dòng)條
*/
private void initAgeList() {
LinearLayoutManager mLayoutManager =
new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false);
mRvAgeList.setLayoutManager(mLayoutManager);
mAgeAdapter = new PersonAgeAdapter(START_NUM, END_NUM);
mRvAgeList.setAdapter(mAgeAdapter);
mRvAgeList.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
mBDownStep.setEnabled(false);
// 效果在暫停時(shí)顯示, 否則會(huì)導(dǎo)致重繪異常
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
mAgeAdapter.highlightItem(getMiddlePosition());
mRvAgeList.scrollToPosition(getScrollPosition());
mLastValue = getMiddlePosition();
UserInfoManager.setAge(getMiddlePosition() + START_NUM);
mBDownStep.setEnabled(true); // 滑動(dòng)時(shí)不可用, 停止時(shí)才可以
}
}
@Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
// 值是實(shí)時(shí)增加
mTvAgeValue.setText(String.valueOf(getMiddlePosition() + START_NUM));
}
});
mAgeAdapter.highlightItem(getMiddlePosition());
}
設(shè)置一個(gè)水平布局
LinearLayoutManager mLayoutManager =
new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false);
mRvAgeList.setLayoutManager(mLayoutManager);
添加Adapter, 設(shè)置起始和結(jié)束位置.
mAgeAdapter = new PersonAgeAdapter(START_NUM, END_NUM); mRvAgeList.setAdapter(mAgeAdapter);
2. Adapter
Adapter定制一些功能, 如標(biāo)示指向中心位置, 數(shù)據(jù)聯(lián)動(dòng)顯示, 高亮Item等.
/**
* 年齡的適配器
* <p>
* Created by wangchenlong on 15/11/12.
*/
public class PersonAgeAdapter extends RecyclerView.Adapter<PersonAgeAdapter.AgeItemViewHolder> {
public static final int ITEM_NUM = 7; // 每行擁有的Item數(shù), 必須是奇數(shù)
private int mFrom; // 起始
private int mTo; // 終止
private int mHighlight = -1; // 高亮
public PersonAgeAdapter(int from, int to) {
mFrom = from;
mTo = to;
}
@Override public AgeItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View item = LayoutInflater.from(parent.getContext()).
inflate(R.layout.view_age_item, parent, false);
// 設(shè)置Item的寬度
ViewGroup.LayoutParams lp = item.getLayoutParams();
lp.width = getItemStdWidth();
return new AgeItemViewHolder(item);
}
@Override public void onBindViewHolder(AgeItemViewHolder holder, int position) {
holder.getTextView().setText(String.valueOf(mFrom + position));
// 高亮顯示
if (isSelected(position)) {
holder.getTextView().setTextSize(30);
holder.getTextView().setTextColor(ChunyuApp.getAppContext().getResources().getColor(R.color.black));
} else {
holder.getTextView().setTextSize(20);
holder.getTextView().setTextColor(ChunyuApp.getAppContext().getResources().getColor(R.color.gray_line));
}
}
// 高亮中心, 更新前后位置
public void highlightItem(int position) {
mHighlight = position;
int offset = ITEM_NUM / 2;
for (int i = position - offset; i <= position + offset; ++i)
notifyItemChanged(i);
}
// 判斷是否是高亮
public boolean isSelected(int position) {
return mHighlight == position;
}
@Override public int getItemCount() {
return mTo - mFrom + 1;
}
// 獲取標(biāo)準(zhǔn)寬度
public static int getItemStdWidth() {
DisplayMetrics displayMetrics = ChunyuApp.getAppContext().getResources().getDisplayMetrics();
return displayMetrics.widthPixels / ITEM_NUM;
}
// ViewHolder
public class AgeItemViewHolder extends RecyclerView.ViewHolder {
private TextView mTextView;
public AgeItemViewHolder(View itemView) {
super(itemView);
mTextView = (TextView) itemView.findViewById(R.id.item_age_value);
mTextView.setTag(this);
}
public TextView getTextView() {
return mTextView;
}
}
}
每個(gè)單元格寬度是屏幕寬度的奇數(shù)分之一, 填充屏幕, 則起始指向中心.
@Override public AgeItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View item = LayoutInflater.from(parent.getContext()).
inflate(R.layout.view_age_item, parent, false);
// 設(shè)置Item的寬度
ViewGroup.LayoutParams lp = item.getLayoutParams();
lp.width = getItemStdWidth();
return new AgeItemViewHolder(item);
}
單數(shù)Item數(shù)量, 則中心的指示器必指向中心Item的中心.
根據(jù)選中狀態(tài), 更新Item樣式.
@Override public void onBindViewHolder(AgeItemViewHolder holder, int position) {
holder.getTextView().setText(String.valueOf(mFrom + position));
// 高亮顯示
if (isSelected(position)) {
holder.getTextView().setTextSize(30);
holder.getTextView().setTextColor(ChunyuApp.getAppContext().getResources().getColor(R.color.black));
} else {
holder.getTextView().setTextSize(20);
holder.getTextView().setTextColor(ChunyuApp.getAppContext().getResources().getColor(R.color.gray_line));
}
}
設(shè)置高亮, 把中心位置高亮, 兩邊恢復(fù), 通知Adapter重繪ViewHolder.
// 高亮中心, 更新前后位置
public void highlightItem(int position) {
mHighlight = position;
int offset = ITEM_NUM / 2;
for (int i = position - offset; i <= position + offset; ++i)
notifyItemChanged(i);
}
// 判斷是否是高亮
public boolean isSelected(int position) {
return mHighlight == position;
}
notifyItemChanged()會(huì)重繪所選擇的頁面.
獲取寬度, 把一行顯示Item數(shù)量設(shè)置為單數(shù), 則中心指向一個(gè)Item.
// 獲取標(biāo)準(zhǔn)寬度
public static int getItemStdWidth() {
DisplayMetrics displayMetrics = ChunyuApp.getAppContext().getResources().getDisplayMetrics();
return displayMetrics.widthPixels / ITEM_NUM;
}
注意RecyclerView不能移動(dòng)半個(gè)單元, 每行數(shù)量是單數(shù)時(shí), 則必會(huì)指向中心.
3. 滾動(dòng)邏輯
在滾動(dòng)時(shí), 實(shí)時(shí)更新頁面顯示; 在停止時(shí), 更新高亮和存儲(chǔ)數(shù)據(jù); 滾動(dòng)結(jié)束時(shí), 激活按鈕.
mRvAgeList.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
mBDownStep.setEnabled(false);
// 效果在暫停時(shí)顯示, 否則會(huì)導(dǎo)致重繪異常
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
mAgeAdapter.highlightItem(getMiddlePosition());
mRvAgeList.scrollToPosition(getScrollPosition());
mLastValue = getMiddlePosition();
UserInfoManager.setAge(getMiddlePosition() + START_NUM);
mBDownStep.setEnabled(true); // 滑動(dòng)時(shí)不可用, 停止時(shí)才可以
}
}
@Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
// 值是實(shí)時(shí)增加
mTvAgeValue.setText(String.valueOf(getMiddlePosition() + START_NUM));
}
});
mAgeAdapter.highlightItem(getMiddlePosition());
}
/**
* 獲取中間位置
*
* @return 當(dāng)前值
*/
private int getMiddlePosition() {
return getScrollPosition() + (PersonAgeAdapter.ITEM_NUM / 2);
}
/**
* 獲取滑動(dòng)值, 滑動(dòng)偏移 / 每個(gè)格子寬度
*
* @return 當(dāng)前值
*/
private int getScrollPosition() {
return (int) ((double) mRvAgeList.computeHorizontalScrollOffset()
/ (double) PersonAgeAdapter.getItemStdWidth());
}
判斷滾動(dòng)的距離單位, 偏移總距離/單個(gè)Item寬度.
private int getScrollPosition() {
return (int) ((double) mRvAgeList.computeHorizontalScrollOffset()
/ (double) PersonAgeAdapter.getItemStdWidth());
}
computeHorizontalScrollOffset()獲取RecyclerView的偏移總位移.
中間位置, 還要加上半行Item數(shù)量
private int getMiddlePosition() {
return getScrollPosition() + (PersonAgeAdapter.ITEM_NUM / 2);
}
判斷滾動(dòng)是否停止.
if (newState == RecyclerView.SCROLL_STATE_IDLE)
在滾動(dòng)停止時(shí), 自動(dòng)對(duì)齊.
mRvAgeList.scrollToPosition(getScrollPosition());
調(diào)用需要高亮顯示的Item.
mAgeAdapter.highlightItem(getMiddlePosition());
注意在滾動(dòng)停止時(shí), 更新高亮比較好, 否則重繪速度較慢, 會(huì)影響滾動(dòng)效果.
滑動(dòng)效果

總結(jié)
好了,以上就是這篇文章的全部?jī)?nèi)容了,我們可以根據(jù)這些定制各式各樣的滾動(dòng)條了!希望這篇文章對(duì)大家能有所幫助。
- RecyclerView實(shí)現(xiàn)縱向和橫向滾動(dòng)
- RecyclerView實(shí)現(xiàn)抖音縱向滾動(dòng)ViewPager效果
- Android RecyclerView 滾動(dòng)到中間位置的方法示例
- Android RecyclerView 實(shí)現(xiàn)快速滾動(dòng)的示例代碼
- Android使用Recyclerview實(shí)現(xiàn)圖片水平自動(dòng)循環(huán)滾動(dòng)效果
- XRecyclerView實(shí)現(xiàn)下拉刷新、滾動(dòng)到底部加載更多等功能
- Android_RecyclerView實(shí)現(xiàn)上下滾動(dòng)廣告條實(shí)例(帶圖片)
- Android中RecyclerView實(shí)現(xiàn)分頁滾動(dòng)的方法詳解
- Android代碼實(shí)現(xiàn)AdapterViews和RecyclerView無限滾動(dòng)
- RecyclerView實(shí)現(xiàn)橫向滾動(dòng)效果
相關(guān)文章
Flutter實(shí)現(xiàn)不同縮放動(dòng)畫效果詳解
這篇文章主要為大家詳細(xì)介紹了Flutter利用不同組件(ScaleTransition、SizeTransition、AnimatedSize和AnimatedBuilder)實(shí)現(xiàn)不同縮放動(dòng)畫效果,感興趣的可以動(dòng)手嘗試一下2022-06-06
在Android平臺(tái)上實(shí)現(xiàn)消息推送功能
隨著移動(dòng)互聯(lián)網(wǎng)應(yīng)用的飛速發(fā)展,消息推送已成為移動(dòng)應(yīng)用中不可或缺的功能,在Android平臺(tái)上,實(shí)現(xiàn)消息推送涉及到服務(wù)端的消息發(fā)送、客戶端的消息接收、通知渠道(Notification?Channels)的管理以及應(yīng)用在前臺(tái)和后臺(tái)不同狀態(tài)下的處理等多個(gè)方面,下面小編給大家詳細(xì)介紹一下2025-04-04
Android動(dòng)畫 實(shí)現(xiàn)開關(guān)按鈕動(dòng)畫(屬性動(dòng)畫之平移動(dòng)畫)實(shí)例代碼
這篇文章主要介紹了Android動(dòng)畫 實(shí)現(xiàn)開關(guān)按鈕動(dòng)畫(屬性動(dòng)畫之平移動(dòng)畫)實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2016-11-11
Android四大組件之Service(服務(wù))實(shí)例詳解
這篇文章主要介紹了Android四大組件之Service(服務(wù))的用法,結(jié)合實(shí)例形式詳細(xì)分析了Service的基本概念,類型,用法與相關(guān)注意事項(xiàng),需要的朋友可以參考下2016-01-01
Android中判斷網(wǎng)絡(luò)連接狀態(tài)的方法
App判斷用戶是否聯(lián)網(wǎng)是很普遍的需求,這篇文章主要介紹了Android中判斷網(wǎng)絡(luò)連接狀態(tài)的方法,感興趣的朋友可以參考一下2016-02-02

