首页 > 其他分享 >Android开发滑动悬停效果

Android开发滑动悬停效果

时间:2024-10-15 09:22:37浏览次数:10  
标签:MotionEvent int private && ACTION 滑动 Android ev 悬停

Android开发滑动悬停效果

Android开发滑动悬停效果,有点难度,但源码中我已经加入相应注释,你只要修改布局即可。很常见的需求

一、思路:

自定义悬停控件LetterStickyNavLayout,它是继承LinearLayout

二、效果图:

在这里插入图片描述
在这里插入图片描述
看视频更直观点:

<iframe allowfullscreen="true" data-mediaembed="bilibili" frameborder="0" id="DgzdSJOz-1728871523764" src="https://player.bilibili.com/player.html?aid=230754263"></iframe>

Android开发教程实战案例源码分享-滑动悬停效果

三、关键代码:
public class LetterStickyNavLayout extends LinearLayout {
    private ScrollListener scrollListener;

    private Context mContext;
    private View mTop;
    private View mNav;
    private LinearLayout llContent;
    private int mTopViewHeight;
    private ViewGroup mInnerScrollView;
    private boolean isTopHidden = false;
    private OverScroller mScroller;
    private VelocityTracker mVelocityTracker;
    private int mTouchSlop;
    private int mMaximumVelocity, mMinimumVelocity;
    private float mLastY;
    private boolean mDragging;
    private boolean isInControl = false;
    private boolean isChange=false;
    private int screenWidth;

    public LetterStickyNavLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        setOrientation(LinearLayout.VERTICAL);
        mContext = context;
        mScroller = new OverScroller(context);
        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
        mMaximumVelocity = ViewConfiguration.get(context)
                .getScaledMaximumFlingVelocity();
        mMinimumVelocity = ViewConfiguration.get(context)
                .getScaledMinimumFlingVelocity();
        screenWidth= getScreenWidthPx(mContext);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mTop = findViewById(R.id.id_stickynavlayout_topview);
        mNav = findViewById(R.id.id_stickynavlayout_indicator);
        View llView = findViewById(R.id.id_stickynavlayout_content);
        if (!(llView instanceof LinearLayout)) {
            throw new RuntimeException(
                    "id_stickynavlayout_content show used by LinearLayout !");
        }
        llContent =(LinearLayout)llView;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        ViewGroup.LayoutParams params;
        params = llContent.getLayoutParams();
        int height;
        if(checkDeviceHasNavigationBar(mContext)){
            height = getMeasuredHeight() - mNav.getMeasuredHeight() + dip2px(mContext, 34);
        }else{
            height = getMeasuredHeight() - mNav.getMeasuredHeight();
        }
        if (height >= screenWidth && !isChange) {
            params.height = height;
            isChange = true;
        }
        getChildAt(0).measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
        setMeasuredDimension(getMeasuredWidth(), mTop.getMeasuredHeight() + mNav.getMeasuredHeight() + llContent.getMeasuredHeight());
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mTopViewHeight = mTop.getMeasuredHeight()  ;
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        float y = ev.getY();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                mLastY = y;
                break;
            case MotionEvent.ACTION_MOVE:
                float dy = y - mLastY;
                getCurrentScrollView();
                if(mInnerScrollView != null && mInnerScrollView.getVisibility()== View.VISIBLE && mInnerScrollView instanceof RecyclerView){
                    //SwipeRecyclerview mSRRceylerView = (SwipeRecyclerview) mInnerScrollView;
                    RecyclerView lv = (RecyclerView) mInnerScrollView;
                    View c = lv.getChildAt(0);
                    if (!isInControl && c != null && c.getTop() == 0 && isTopHidden
                            && dy > 0) {
                        isInControl = true;
                        ev.setAction(MotionEvent.ACTION_CANCEL);
                        MotionEvent ev2 = MotionEvent.obtain(ev);
                        dispatchTouchEvent(ev);
                        ev2.setAction(MotionEvent.ACTION_DOWN);
                        return dispatchTouchEvent(ev2);
                    }
                } else {
                    if (llContent != null) {
                        if (!isInControl && androidx.core.view.ViewCompat.canScrollVertically(llContent, -1) && isTopHidden
                                && dy > 0) {
                            isInControl = true;
                            ev.setAction(MotionEvent.ACTION_CANCEL);
                            MotionEvent ev2 = MotionEvent.obtain(ev);
                            dispatchTouchEvent(ev);
                            ev2.setAction(MotionEvent.ACTION_DOWN);
                            return dispatchTouchEvent(ev2);
                        }
                    }
                }
                break;
        }
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        final int action = ev.getAction();
        float y = ev.getY();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                mLastY = y;
                break;
            case MotionEvent.ACTION_MOVE:
                float dy = y - mLastY;
                if (Math.abs(dy) > mTouchSlop) {
                    mDragging = true;
                    if(mInnerScrollView != null && mInnerScrollView.getVisibility()== View.VISIBLE && mInnerScrollView instanceof RecyclerView){
                        //SwipeRecyclerview ptrlv = (SwipeRecyclerview) mInnerScrollView;
                        RecyclerView lv = (RecyclerView) mInnerScrollView;
                        View c = lv.getChildAt(0);
                        // 如果topView没有隐藏
                        // 或sc的PullToRefreshListView在顶部�&& topView隐藏 && 下拉,则拦截

                        if (!isTopHidden || (c != null && c.getTop() == 0 && isTopHidden && dy > 0 )) {
                            initVelocityTrackerIfNotExists();
                            mVelocityTracker.addMovement(ev);
                            mLastY = y;
                            return true;
                        }
                    } else {
                        if (llContent != null) {
                            if (!isTopHidden || (!androidx.core.view.ViewCompat.canScrollVertically(llContent, -1) && isTopHidden && dy > 0)) {
                                initVelocityTrackerIfNotExists();
                                mVelocityTracker.addMovement(ev);
                                mLastY = y;
                                return true;
                            }
                        }
                    }
                }
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                mDragging = false;
                recycleVelocityTracker();
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }




    @Override
    public boolean onTouchEvent(MotionEvent event) {
        initVelocityTrackerIfNotExists();
        mVelocityTracker.addMovement(event);
        int action = event.getAction();
        float y = event.getY();

        switch (action) {
            case MotionEvent.ACTION_DOWN:
                if (!mScroller.isFinished())
                    mScroller.abortAnimation();
                mLastY = y;
                return true;
            case MotionEvent.ACTION_MOVE:
                float dy = y - mLastY;
                if (!mDragging && Math.abs(dy) > mTouchSlop) {
                    mDragging = true;
                }
                if (mDragging) {
                    scrollBy(0, (int) -dy);
                    // 如果topView隐藏,且上滑动时,则改变当前事件为ACTION_DOWN
                    if (getScrollY() == mTopViewHeight && dy < 0) {
                        event.setAction(MotionEvent.ACTION_DOWN);
                        dispatchTouchEvent(event);
                        isInControl = false;
                    }
                }
                mLastY = y;
                break;
            case MotionEvent.ACTION_CANCEL:
                mDragging = false;
                recycleVelocityTracker();
                if (!mScroller.isFinished()) {
                    mScroller.abortAnimation();
                }
                break;
            case MotionEvent.ACTION_UP:
                mDragging = false;
                mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                int velocityY = (int) mVelocityTracker.getYVelocity();
                if (Math.abs(velocityY) > mMinimumVelocity) {
                    fling(-velocityY);
                }
                recycleVelocityTracker();
                break;
        }
        return super.onTouchEvent(event);
    }

    public void fling(int velocityY) {
        mScroller.fling(0, getScrollY(), 0, velocityY, 0, 0, 0, mTopViewHeight);
        invalidate();
    }

    @Override
    public void scrollTo(int x, int y) {

        if (y < 0) {
            y = 0;
        }
        if (y > mTopViewHeight) {
            y = mTopViewHeight;
        }
        if (y != getScrollY()) {
            super.scrollTo(x, y);
        }
        isTopHidden = getScrollY() == mTopViewHeight;
        scrollListener.scrollToTop(isTopHidden);
    }

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            scrollTo(0, mScroller.getCurrY());
            invalidate();
        }
    }

    private void initVelocityTrackerIfNotExists() {
        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
    }

    private void recycleVelocityTracker() {
        if (mVelocityTracker != null) {
            mVelocityTracker.recycle();
            mVelocityTracker = null;
        }
    }

    private void getCurrentScrollView() {
        if (llContent != null) {
            mInnerScrollView = (ViewGroup) (llContent
                    .findViewById(R.id.id_stickynavlayout_swiperefrecyclerview));
        }
    }

    /**
     * 按钮滑动事件
     *
     * @param listener
     */
    public void setOnScrollListener(ScrollListener listener) {
        scrollListener = listener;
    }

    public interface ScrollListener {
        public void scrollToTop(boolean flag);

    }

    //获取是否存在NavigationBar
    public static boolean checkDeviceHasNavigationBar(Context context) {
        boolean hasNavigationBar = false;
        Resources rs = context.getResources();
        int id = rs.getIdentifier("config_showNavigationBar", "bool", "android");
        if (id > 0) {
            hasNavigationBar = rs.getBoolean(id);
        }
        try {
            Class systemPropertiesClass = Class.forName("android.os.SystemProperties");
            Method m = systemPropertiesClass.getMethod("get", String.class);
            String navBarOverride = (String) m.invoke(systemPropertiesClass, "qemu.hw.mainkeys");
            if ("1".equals(navBarOverride)) {
                hasNavigationBar = false;
            } else if ("0".equals(navBarOverride)) {
                hasNavigationBar = true;
            }
        } catch (Exception e) {

        }
        return hasNavigationBar;
    }

    public static int getScreenWidthPx(Context mContext) {
        DisplayMetrics dm = mContext.getResources().getDisplayMetrics();
        return dm.widthPixels;
    }

    public static int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }
}
四、项目demo源码结构图

在这里插入图片描述
有问题或者需要完整源码私信我

标签:MotionEvent,int,private,&&,ACTION,滑动,Android,ev,悬停
From: https://blog.csdn.net/u010074743/article/details/142910753

相关文章

  • 高可用之限流-05-slide window 滑动窗口
    限流系列开源组件rate-limit:限流高可用之限流-01-入门介绍高可用之限流-02-如何设计限流框架高可用之限流-03-Semaphore信号量做限流高可用之限流-04-fixedwindow固定窗口高可用之限流-05-slidewindow滑动窗口高可用之限流-06-slidewindow滑动窗口sentinel源码......
  • 牛客AB33.相差不超过k的最多数 (滑动窗口) 牛客.DP最长公共子序列牛客.春游主持人调度
    目录牛客AB33.相差不超过k的最多数 (滑动窗口) 牛客.DP最长公共子序列牛客.春游主持人调度(二)牛客AB33.相差不超过k的最多数 (滑动窗口) 和之前那个空调吹风属于一道题的类型,当然滑动窗口,最大值-最小值,然后<=p即可也可以双指针来取寻找最大值和最小值impor......
  • 在 Android 开发中,如何实现蓝牙连接设备?
    在Android开发中,实现蓝牙连接设备通常通过BluetoothAdapter、BluetoothDevice、BluetoothSocket等类来实现。你可以使用这些API来搜索蓝牙设备、配对设备以及通过蓝牙进行通信。以下是实现蓝牙连接设备的详细步骤,包含设备扫描、连接以及数据传输的Java代码示例。1.......
  • Android Studio开发系统APK(引入framework.jar及系统签名)
    在开发过程中,我们需要开发一些功能的独立APK,当然某些简单的功能可以依附于Settings或SystemUI等系统有源码的APK,但是一些逻辑功能较为复杂的APK用AndroidStudio开发就比较的好一点。一、新建项目新建如MyDemo一定要选择EmptyViewsActivity,只有这个才是干净的用java/ko......
  • android开发修复第三方库生成的so库名称不是以so结尾的解决方法
    需要ubuntu安装patchelf软件:sudoapt-getinstallpatchelf1.先使用readelf-d查看so内容结构先使用readelf-dlibpsl.so.5.3.5查看libpsl.so.5.3.5库类型是NEEDED和SONAME的对应的名称是不是以.so结尾的,比如下面的图,libc.so的名称是以.so结尾的我们就不用管,libpsl.so.5不......
  • Android内容观察者(案例:监听数据库+代码+效果图)
    目录1.内容观察者概念1.什么是ContentObserver?2.主要方法3.使用场景4.工作原理5.注册和注销6.实现步骤7.注意事项2.创建内容观察者3.注册内容观察者4.取消注册内容观察者5.完整的activity代码6.案例:检测数据库1)创建一个Android​编辑2)创建数据库3......
  • Android移动应用所需的工具
    基础配置Kotlin:Kotlin是一种兼具面向对象编程(OOPS)和函数式编程范式的静态类型现代编程语言。作为JVM语言,它与Java包和库完全兼容。由Google和JetBrains共同推出,Kotlin旨在成为Java的替代品,并已被Google指定为Android开发的官方语言。AndroidStudio:AndroidStudio是Android开发......
  • Android开发编译curl库给Android使用
    Android开发编译curl库给Android使用编译zlib库官网:http://zlib.net/解决后面出现的error:--with-opensslwasgivenbutOpenSSLcouldnotbedetectedexportANDROID_NDK_HOME=/home/ubuntu20/Android/Sdk/ndk/21.4.7075529exportTOOLCHAIN=$ANDROID_NDK_HOME/toolc......
  • Android中的ConstrainLayout的用法(上)
    Android中的ConstraintLayout(约束布局)是一种灵活的布局方式,它允许开发者通过定义视图之间的相对位置来创建复杂的用户界面。以下是对ConstraintLayout的详细解释:一、基本介绍ConstraintLayout是在2016年GoogleI/O大会上发布的布局方式,旨在解决复杂的页面层级嵌套过多的问题......
  • Android 车载应用开发指南 - CAN Bus 协议详解
    ​在现代车载应用开发中,CAN(ControllerAreaNetwork)总线协议扮演着不可或缺的角色。作为一个汽车内部网络的标准协议,CANBus已经成为了车载系统通信的基础。而在Android车载应用开发的过程中,理解并利用好CANBus协议是必不可少的。那么,CANBus到底是什么?它又是如何在车载......