IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 自定义带快速索引功能的RecyclerView -> 正文阅读

[移动开发]自定义带快速索引功能的RecyclerView

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


篇章目标要点

`
RecyclerView是一种常用的列表容器,但是在展示大量内容时,如何快速找到我们想要定位的内容在设计中一般都很必要考虑的。比如在小米手机的本地相册展示时就使用了一个快速引用的,通过拖动索引浮标就可以快速找到对应位置。本文参照小米相册尝试实现一种带快速索引功能的RecyclerView。

一、实现效果

  1. 列表滑动时,浮标可以指示当前列表已经翻动的位置
    请添加图片描述

  2. 浮标滑动时,列表可以滚动到对应比例位置,并且浮标上展示了当前位置序号和关键信息
    请添加图片描述

二、设计布局原理

   考虑浮标与RecyclerView是处于叠加关系,计划增加一个FrameLayout作为父类容器,方案基本思路就是实现浮标跟随手指滑动在父类容器中跟随调整位置,并且根据纵向滑动的百分比调整RecyclerView目标显示位置。

在这里插入图片描述

三、关键代码实现

1.浮标随手势移动

在视图layout完成时获取浮标在父容器当中的x,y坐标

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    super.onLayout(changed, left, top, right, bottom);
    fetchMargin();
}

/**
 * 获取浮标父布局在父容器当中的x,y坐标
 */
private void fetchMargin(){
    FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams)mLayoutShortcut.getLayoutParams();
    mMarginX = mLayoutShortcut.getX();
    mMarginY = mLayoutShortcut.getY();
    Log.d(TAG, "initial margin x = "+mMarginX+", y = "+mMarginY+",right = "+lp.rightMargin);
}

在MOVE事件中设置浮标跟随移动,并且设置列表的滚动目标位置。在UP事件中检查浮标是否存在偏移,如存在偏移,则恢复浮标右侧位置,并且设置手指抬起后3s自动收起浮标。

//记录滑动事件的过程坐标和按下坐标
private float x , y , mInitX, mInitY;
@SuppressLint("ClickableViewAccessibility")
//监听浮标布局的touch事件并执行随手移动和滚动列表
private void handleTouchEvent(){
    mLayoutShortcut.setOnTouchListener(new OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            x = event.getX();
            y = event.getY();
            mStatusState[1] = event.getAction();
            Log.d(TAG, "event x = " + x + ", y = "+y+",action ="+event.getAction());
            switch (event.getAction()){
                case MotionEvent.ACTION_DOWN:
                    //如原来有待处理的延迟关闭浮标的任务,先取消
                    mButtonInTouch = true;
                    if(mTextViewTitle.getVisibility() == View.INVISIBLE){
                        mTextViewTitle.setVisibility(View.VISIBLE);
                    }
                    if(null != mDispose && !mDispose.isDisposed()){
                        mDispose.dispose();
                    }
                    mInitX = x;
                    mInitY = y;
                    break;
                case MotionEvent.ACTION_MOVE:
                    //执行浮标跟随手指拖动及列表滚动
                    mButtonInTouch = true;
                    locateFloatButtonByTouch(x - mInitX, y - mInitY);
                    fastScrollList(v.getY() - mInitY);
                    break;
                case MotionEvent.ACTION_UP:
                    //如浮标有出现横向偏移,抬起时恢复默认的右侧位置
                    mButtonInTouch = false;
                    recoveryFloatButton();
                    hideFloatButton();
                    fetchMargin();
                    break;
                case MotionEvent.ACTION_CANCEL:
                    mButtonInTouch = false;
                default:
                    break;
            }
            return true;
        }
    });

调整浮标位置

/**
 * 拖动浮标时调整浮标的位置
 * @param x touch位置的x坐标,即shiftButton中心x坐标
 * @param y touch位置的y坐标,即shiftButton中心y坐标
 */
private void locateFloatButtonByTouch(float x, float y){
    FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams)mLayoutShortcut.getLayoutParams();
    Log.d(TAG, "locate new x = "+(mMarginX + x)+", new y = "+mMarginY+", origin x = "+lp.leftMargin+", origin y = "+y);
    int afterX = (int)(mMarginX + x);
    int afterY = (int)(mMarginY + y);
    if(afterX > mWidth){
        afterX = mWidth;
    }else if(afterX <= 0){
        afterX = 0;
    }
    if(afterY > (mHeight - mFloatButtonHeight)){
        afterY = (int)(mHeight - mFloatButtonHeight);
    }else if(afterY <= 0){
        afterY = 0;
    }
    lp.topMargin = afterY;
    mLayoutShortcut.setLayoutParams(lp);
    mLayoutShortcut.setX(afterX);
}

抬起时恢复浮标位置

/**
 * 如果悬浮按钮被拖动至了非默认位置,需要首先执行位置恢复动效
 */
private void recoveryFloatButton(){
    Log.d(TAG, "recovery float button");
    int diff = (int)((mWidth - mLayoutShortcut.getWidth() -DEFAULT_MARGIN_RIGHT) - mLayoutShortcut.getX());
    int initial = (int)mLayoutShortcut.getX();
    if(diff != 0){
        int MAX_VALUE = 10;
        ValueAnimator vm = ValueAnimator.ofInt(MAX_VALUE).setDuration(800);
        vm.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float x = initial + diff * (int)animation.getAnimatedValue() / MAX_VALUE;
                mLayoutShortcut.setX(x);
            }
        });
        vm.start();
    }
}

没有滑动时,超时自动隐藏浮标

/**
 * 没有滑动时,超过5s后自动隐藏
 */
private void hideFloatButton(){
    if(null != mDispose && !mDispose.isDisposed()){
        mDispose.dispose();
    }
    if(mTextViewTitle.getVisibility() == View.VISIBLE){
        mDispose = Schedulers.computation().scheduleDirect(new Runnable() {
            @Override
            public void run() {
                Animation animation = AnimationUtils.loadAnimation(getContext(), R.anim.anim_exit_to_right);
                mTextViewTitle.setAnimation(animation);
                mTextViewTitle.startAnimation(animation);
                Log.d(TAG,"anim begin");
                animation.setAnimationListener(new Animation.AnimationListener() {
                    @Override
                    public void onAnimationStart(Animation animation) {
                        Log.d(TAG,"anim start");
                    }

                    @Override
                    public void onAnimationEnd(Animation animation) {
                        Log.d(TAG,"anim end");
                        mTextViewTitle.setVisibility(INVISIBLE);
                        mImageViewIcon.setVisibility(INVISIBLE);
                    }

                    @Override
                    public void onAnimationRepeat(Animation animation) {

                    }
                });
            }
        }, 3, TimeUnit.SECONDS);
    }else{
        mDispose = Schedulers.computation().scheduleDirect(new Runnable() {
            @Override
            public void run() {
                mImageViewIcon.setVisibility(INVISIBLE);
            }
        }, 3, TimeUnit.SECONDS);
    }
}

2.浮标随列表移动

列表移动,浮标移动可以标记当前可见位置居于整体列表的大概位置。跟随列表移动时,仅调整y向位置即可。

/**
 * 在RecyclerView滑动时调整浮标位置
 * @param y 浮标中心的y坐标
 */
private void locateFloatButtonByScroll(float y){
    FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams)mLayoutShortcut.getLayoutParams();
    mMarginY = y - mFloatButtonHeight / 2;
    lp.topMargin = (int)mMarginY;
    mLayoutShortcut.setLayoutParams(lp);
}

3.列表随浮标移动而滚动

floatButton Touch时快速滚动RecyclerView,根据滑动位置占据视图高度的百分比计算目标列表位置,设置列表目标位置居中显示。

private void fastScrollList(float deltaY){
    float percentY = 1.00f * (deltaY - mFloatButtonHeight / 2) / (mHeight - mFloatButtonHeight);
    mRecyclerView.fastScrollList(percentY);
}

在自定义RecyclerView中执行滑动逻辑,并且实时回调当前浮标位置对应的位置和关键信息。

/**
 * 根据滑动位置占据视图的百分比,计算目标移动到列表的位置
 * @param percentY
 */
public void fastScrollList(float percentY){
    int count = getAdapter().getItemCount();
    int pos = (int)(percentY * count);
    Log.d(TAG,"scroll to position = "+pos+", percentY = "+percentY +",count = "+count);
    if(pos >= 0){
        smoothScrollToPosition(pos);
        //设置浮标显示内容,显示序号+内容,可以根据需求自行定义
        TrackBean trackBean = ((SongAdapter)(getAdapter())).getDataList().get(pos);
        mScrollState.onCurrentPos(pos, trackBean);
    }
}

学习心得

   文中Demo给出了一个实现思路和基本功能完成,距离量产质量还需要更多的测试和优化,也欢迎大家反馈意见和多多交流。
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-05-10 12:02:43  更:2022-05-10 12:03:52 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 2:00:48-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码