android开发分享【安卓】让你的 AbsListView可以自动滚动,还能循环!

1. 轮盘式循环 listView2. 自动选中置顶 item3. 选项对齐符合你需要就进来看看吧~


制作一个带有循环播放效果的轮盘式自动选中AbsListView

一、需求描述

我这边的需求是这样的:服务端会传过来一个数据集合,还有一个需要显示选中的数据。产品希望可以展示出来一个随机抽奖循环的效果,最终停在指定选中的位置。没问题,安排~

二、效果预览

老规矩,无图言 ×。上图!
【安卓】让你的 AbsListView可以自动滚动,还能循环!

三、上代码

自认为代码里注释已经足够多了,所以这里就不在做过多解释了。

1. 主逻辑代码 – AutoScrollAdapter.java
import android.annotation.SuppressLint; import android.content.Context; import android.support.annotation.ColorInt; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.BaseAdapter; import android.widget.LinearLayout; import android.widget.TextView;  import com.baijiayun.groupclassui.R;  import java.util.List;  /**  * @author lzd  * 自动旋转至目标位置的 adapter,调用  * {@link AutoScrollAdapter#initListView(AbsListView, Context, List)} 以使用  */ public class AutoScrollAdapter extends BaseAdapter implements         AbsListView.OnScrollListener, View.OnTouchListener {     private Context context;     private AbsListView listView;     private List<String> dataList;     private OnAutoScrollListener onAutoScrollListener;      private int maxNum = 5;     private int nowFirstPosition;      private int normalTextSize;     private int selectTextSize;     private @ColorInt     int normalTextColor;     private @ColorInt     int selectTextColor;      private int listViewVisibleHeight;     private int itemHeight;     private int listViewPaddingTop;      /**      * 最小滚动个数 - 可以滚动多圈,但是最少要这么多      */     private static final int MIN_SCROLL_NUM = 30;      /**      * 目标 FirstPosition {@link AutoScrollAdapter#updateSelect(int)} 中设定      */     private int targetFirstPosition = -1;     /**      * 前进至 {@link AutoScrollAdapter#targetFirstPosition} 的剩余距离      */     private double remainLength = 0;     /**      * 偏差补齐模式      */     private boolean isMakeUpDeviation = false;      private AutoScrollAdapter(Context context, List<String> dataList) {         this.context = context;         this.dataList = dataList;     }      /**      * 调用此方法以初始化并获取一个 AutoScrollAdapter      *      * @param listView 需要适配的 ListView      * @param dataList 数据集      */     @SuppressLint("ClickableViewAccessibility")     public static AutoScrollAdapter initListView(             final AbsListView listView, final Context context, final List<String> dataList) {         AutoScrollAdapter autoScrollAdapter = new AutoScrollAdapter(context, dataList);         listView.setOnTouchListener(autoScrollAdapter);         listView.setOnScrollListener(autoScrollAdapter);         listView.setAdapter(autoScrollAdapter);         autoScrollAdapter.listViewPaddingTop = listView.getPaddingTop();         autoScrollAdapter.initParams(listView.getLayoutParams().height                 - listView.getPaddingTop() - listView.getPaddingBottom());         autoScrollAdapter.nowFirstPosition = Integer.MAX_VALUE / 2;         listView.setSelection(autoScrollAdapter.nowFirstPosition);         autoScrollAdapter.listView = listView;         return autoScrollAdapter;     }      private void initParams(int listViewVisibleHeight) {         this.listViewVisibleHeight = listViewVisibleHeight;         this.itemHeight = listViewVisibleHeight / maxNum;         // region 默认字号         this.normalTextSize = this.itemHeight / 5;         this.selectTextSize = this.itemHeight / 4;         // endregion     }      /**      * 设置 最大显示 个数      */     public void setMaxNum(int maxNum) {         this.maxNum = maxNum;         initParams(listViewVisibleHeight);         listView.setSelection(nowFirstPosition);     }      /**      * 设置文字颜色      *      * @param normalTextColor 普通状态      * @param selectTextColor 中间选中状态      */     public void setTextColor(@ColorInt int normalTextColor, @ColorInt int selectTextColor) {         this.normalTextColor = normalTextColor;         this.selectTextColor = selectTextColor;     }      /**      * 前进至 目标      *      * @param targetPosition 目标 在 {@link AutoScrollAdapter#dataList} 中的下标      */     public void updateSelect(int targetPosition) {         if (this.remainLength != 0) {             return;         }         // region 计算 当前位置 到 目标选中位置 的 总共要前进的距离         // 计算规则:"前进至少一轮 且 要大于 MIN_SCROLL_NUM" 的结果值 + "当前位置 到 目标位置一轮内的偏移"         int targetOffset = targetPosition - maxNum / 2;         int now2targetOffset = (targetOffset - nowFirstPosition % dataList.size() + dataList.size()) % dataList.size();         int minRemainDistance = MIN_SCROLL_NUM - now2targetOffset;         int remainDistance = ((minRemainDistance / dataList.size()) + 1) * dataList.size();          this.targetFirstPosition = nowFirstPosition + remainDistance + now2targetOffset;         this.remainLength = (remainDistance + now2targetOffset) * (itemHeight + 1);         // endregion         startScrollStep();     }      public interface OnAutoScrollListener {         /**          * 自动 scroll 结束回调          */         void onStateIdle();     }      public void setOnAutoScrollListener(OnAutoScrollListener onAutoScrollListener) {         this.onAutoScrollListener = onAutoScrollListener;     }      /**      * 每帧刷新      * 注: 这里之所以使用 {@link android.widget.ListView#scrollListBy(int)}      *     而没有使用 {@link android.widget.ListView#smoothScrollToPositionFromTop(int, int, int)}      *     是因为 {@link android.widget.ListView#smoothScrollToPositionFromTop(int, int, int)}      *     方法 有bug,目前( 2020-07-14 )未修复      * 注: smooth 系列均有bug,具体表现为:偶现,虽然回调了      *     {@link android.widget.AbsListView.OnScrollListener#onScroll(AbsListView, int, int, int)}      *     但是并无法正确跳转到指定位置      */     private void startScrollStep() {         // region 每帧刷新前进         // length 计算规则:1. remainLength > 0 前进;remainLength > 0 后退         //                2. isMakeUpDeviation 为 true 即为补齐偏差,每帧 1 像素         //                3. isMakeUpDeviation 为 false,正常前进,每次前进 remainLength 的 20 分之 1,         //                   不超过 itemHeight 的一半         final int length = (isMakeUpDeviation ? 1 :                 (int) Math.ceil(Math.min(Math.abs(remainLength) / 20, itemHeight / 2d)))                 * (remainLength > 0 ? 1 : -1);         listView.scrollListBy(length);         remainLength -= length;         // endregion         listView.postDelayed(() -> {             if (Math.abs(remainLength) > 1) {                 // 剩余超过 1 像素,继续                 startScrollStep();             } else {                 // 不足一像素                 if (checkCalcDeviation()) {                     // 检查需补齐,继续                     startScrollStep();                     return;                 }                 // 检查已补齐,修正 selection ,回复数据                 listView.setSelection(targetFirstPosition);                 targetFirstPosition = -1;                 if (onAutoScrollListener != null) {                     // 结束回调                     onAutoScrollListener.onStateIdle();                 }             }         }, 10);     }      /**      * 计算剩余计算偏差      * 注:方法 {@link AutoScrollAdapter#updateSelect(int)} 方法中,      * remainLength 的计算会有一定的偏差,这里需要补齐      *      * @return 已无偏差 返回 false      */     private boolean checkCalcDeviation() {         double calcDeviation = 0;         if (listView.getFirstVisiblePosition() > targetFirstPosition) {             // 超出,过 item height 回退             if (listView.getTop() > listView.getChildAt(0).getTop()) {                 calcDeviation = listView.getChildAt(0).getTop() - listView.getTop();             } else {                 calcDeviation = itemHeight;             }         } else if (listView.getFirstVisiblePosition() == targetFirstPosition) {             // 超出,回退超出部分             if (listView.getTop() > listView.getChildAt(0).getTop()) {                 calcDeviation = listView.getChildAt(0).getTop() - listView.getTop();             }         } else {             // 未到达,继续前进             calcDeviation = itemHeight - (listView.getTop() - listView.getChildAt(0).getTop());         }         remainLength = calcDeviation;         isMakeUpDeviation = calcDeviation != 0;         return isMakeUpDeviation;     }      /**      * 屏蔽点击事件      */     @SuppressLint("ClickableViewAccessibility")     @Override     public boolean onTouch(View v, MotionEvent event) {         return event.getAction() == MotionEvent.ACTION_MOVE;     }      @Override     public void onScrollStateChanged(AbsListView view, int scrollState) {     }      /**      * 滚动时监听      */     @Override     public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {         if (view.getChildAt(0) == null) {             return;         }         // 中间选中位置的坐标         int selectPosition = view.getTop() + listViewPaddingTop +                 ((maxNum / 2) * itemHeight);         int newTopPosition = 0;         for (int i = 0; i < visibleItemCount; i++) {             View item = view.getChildAt(i);             if (item == null) {                 return;             }             // region 通过每个 item 的 top 坐标来计算字号和文字颜色             int itemPosition = item.getTop();              // 计算当前 item 距离中间位置的比例,超出一个 item 距离则为 0,直接使用 normal 属性             // 否则按比例使用 select 属性             double normal2selRatio = ((double) itemHeight -                     Math.min(Math.abs(itemPosition - selectPosition), itemHeight)) / itemHeight;             if (normal2selRatio < 0.5) {                 // 第一次赋值,且 normal2selRatio < 0.5,即为最接近中间的一个                 newTopPosition = firstVisibleItem;             }              double textSize = normalTextSize + (selectTextSize - normalTextSize) * normal2selRatio;             Object viewHolder = item.getTag();             if (viewHolder instanceof ViewHolder) {                 ((ViewHolder) viewHolder).tvName.setTextSize((float) textSize);                 ((ViewHolder) viewHolder).tvName.setTextColor(                         normal2selRatio < 0.5 ? normalTextColor : selectTextColor);                 if (newTopPosition != nowFirstPosition) {                     nowFirstPosition = newTopPosition;                     // 改变字号和颜色后,requestLayout 一次,否则显示有问题                     ((ViewHolder) viewHolder).tvName.requestLayout();                 }             }             // endregion         }     }      @Override     public int getCount() {         return Integer.MAX_VALUE;     }      @Override     public Object getItem(int position) {         return dataList.get(position % dataList.size());     }      @Override     public long getItemId(int position) {         return position;     }      @Override     public View getView(int position, View convertView, ViewGroup parent) {         ViewHolder viewHolder;         if (convertView == null) {             convertView = View.inflate(context, R.layout.auto_scroll_item, null);             viewHolder = new ViewHolder();              viewHolder.tvName = convertView.findViewById(R.id.auto_scroll_item_name);             convertView.setTag(viewHolder);              //region 设置 item 高度             LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) viewHolder.tvName.getLayoutParams();             params.height = itemHeight;             viewHolder.tvName.setLayoutParams(params);             //endregion         } else {             viewHolder = (ViewHolder) convertView.getTag();         }         viewHolder.tvName.setText(dataList.get(position % dataList.size()));         viewHolder.tvName.setTextSize(normalTextSize);         viewHolder.tvName.setTextColor(normalTextColor);          return convertView;     }      static class ViewHolder {         TextView tvName;     } }  
2. 相关布局文件 – auto_scroll_item.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"     android:layout_width="match_parent"     android:layout_height="match_parent"     xmlns:tools="https://schemas.android.com/tools"     tools:background="@android:color/black"     android:background="@android:color/transparent"     android:gravity="center"     android:orientation="vertical">      <TextView         android:id="@+id/auto_scroll_item_name"         android:layout_width="match_parent"         android:layout_height="wrap_content"         android:textAlignment="center"         android:gravity="center"         android:text="@string/app_name"         android:maxLines="1"         android:ellipsize="end"         android:textColor="?attr/base_window_main_text_color"         android:textSize="14sp" />  </LinearLayout> 
3. 调用方式示例
ListView lvNames = $.id(R.id.random_select_names_container).view(); String[] names = new String[]{         "0刘德华",         "1马云",         "2猪八戒",         "3太上老君",         "4爱迪生",         "5胖大海",         "6迪迦", };  // 初始化 AbsListView autoScrollAdapter = AutoScrollAdapter.initListView(lvNames, context, Arrays.asList(names));  // 注册监听 autoScrollAdapter.setOnAutoScrollListener(() -> {     $.id(R.id.random_select_operate_btn).view().setEnabled(true);     Log.d("lzdTest", "update select OK"); });  // 设置部分参数 autoScrollAdapter.setMaxNum(3); autoScrollAdapter.setTextColor(normalColor, selectColor);  // 开始选中事件 $.id(R.id.random_select_operate_btn).clicked(v -> {     int newInd = new Random().nextInt(names.length);     Log.d("lzdTest", "选中 -> " + names[newInd]);     autoScrollAdapter.updateSelect(newInd);     $.id(R.id.random_select_operate_btn).view().setEnabled(false); }); 

四、不要脸环节

如果帮到你了,给一个三连如何 (* ̄v ̄) – 点赞,收藏,心情好的话还可以评论下~
转载请标明出处 – https://blog.csdn.net/weixin_41957078

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/addevelopment/896410.html

(0)
上一篇 2021年10月22日
下一篇 2021年10月22日

精彩推荐