先看效果:

主要处理:

使用PullToRefreshScrollView修改内部的scrollView源码,更换成可以固定顶部的自定义scrollView

@Override
    protected ScrollView createRefreshableView(Context context, AttributeSet attrs) {
        ScrollView scrollView;
        if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {
            //TODO  修改代码 添加滑动固定顶部标题sv
            scrollView = new InternalScrollViewSDK9(context, attrs);
//          scrollView = new FloatScrollView(context, attrs);
        } else {
            scrollView = new ScrollView(context, attrs);
        }

        scrollView.setId(R.id.scrollview);
        return scrollView;
    }

下面是scrollview的代码:

        public static final String FLOAT = "float";

        private ArrayList<View> stickyViews;
        private View currentlyStickingView;
        private float stickyViewTopOffset;
        private int stickyViewLeftOffset;
        private boolean redirectTouchesToStickyView;
        private boolean clippingToPadding;
        private boolean clipToPaddingHasBeenSet;

        private final Runnable invalidateRunnable = new Runnable() {

            @Override
            public void run() {
                if (currentlyStickingView != null) {
                    int l = getLeftForViewRelativeOnlyChild(currentlyStickingView);
                    int t = getBottomForViewRelativeOnlyChild(currentlyStickingView);
                    int r = getRightForViewRelativeOnlyChild(currentlyStickingView);
                    int b = (int) (getScrollY() + (currentlyStickingView.getHeight() + stickyViewTopOffset));
                    invalidate(l, t, r, b);
                }
                postDelayed(this, 16);
            }
        };

        public void setup() {
            stickyViews = new ArrayList<View>();
        }

        private int getLeftForViewRelativeOnlyChild(View v) {
            int left = v.getLeft();
            while (v.getParent() != getChildAt(0)) {
                v = (View) v.getParent();
                left += v.getLeft();
            }
            return left;
        }

        private int getTopForViewRelativeOnlyChild(View v) {
            int top = v.getTop();
            while (v.getParent() != getChildAt(0)) {
                v = (View) v.getParent();
                top += v.getTop();
            }
            return top;
        }

        private int getRightForViewRelativeOnlyChild(View v) {
            int right = v.getRight();
            while (v.getParent() != getChildAt(0)) {
                v = (View) v.getParent();
                right += v.getRight();
            }
            return right;
        }

        private int getBottomForViewRelativeOnlyChild(View v) {
            int bottom = v.getBottom();
            while (v.getParent() != getChildAt(0)) {
                v = (View) v.getParent();
                bottom += v.getBottom();
            }
            return bottom;
        }

        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            super.onLayout(changed, l, t, r, b);
            if (!clipToPaddingHasBeenSet) {
                clippingToPadding = true;
            }
            notifyHierarchyChanged();
        }

        @Override
        public void setClipToPadding(boolean clipToPadding) {
            super.setClipToPadding(clipToPadding);
            clippingToPadding = clipToPadding;
            clipToPaddingHasBeenSet = true;
        }

        @Override
        public void addView(View child) {
            super.addView(child);
            findStickyViews(child);
        }

        @Override
        public void addView(View child, int index) {
            super.addView(child, index);
            findStickyViews(child);
        }

        @Override
        public void addView(View child, int index, android.view.ViewGroup.LayoutParams params) {
            super.addView(child, index, params);
            findStickyViews(child);
        }

        @Override
        public void addView(View child, int width, int height) {
            super.addView(child, width, height);
            findStickyViews(child);
        }

        @Override
        public void addView(View child, android.view.ViewGroup.LayoutParams params) {
            super.addView(child, params);
            findStickyViews(child);
        }

        @Override
        protected void dispatchDraw(Canvas canvas) {
            super.dispatchDraw(canvas);
            if (currentlyStickingView != null) {
                canvas.save();
                canvas.translate(getPaddingLeft() + stickyViewLeftOffset, getScrollY() + stickyViewTopOffset + (clippingToPadding ? getPaddingTop() : 0));

                canvas.clipRect(0, (clippingToPadding ? -stickyViewTopOffset : 0), getWidth(), currentlyStickingView.getHeight());
                currentlyStickingView.draw(canvas);
                canvas.restore();
            }
        }

        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            if (ev.getAction() == MotionEvent.ACTION_DOWN) {
                redirectTouchesToStickyView = true;
            }

            if (redirectTouchesToStickyView) {
                redirectTouchesToStickyView = currentlyStickingView != null;
                if (redirectTouchesToStickyView) {
                    redirectTouchesToStickyView =
                            ev.getY() <= (currentlyStickingView.getHeight() + stickyViewTopOffset) &&
                                    ev.getX() >= getLeftForViewRelativeOnlyChild(currentlyStickingView) &&
                                    ev.getX() <= getRightForViewRelativeOnlyChild(currentlyStickingView);
                }
            } else if (currentlyStickingView == null) {
                redirectTouchesToStickyView = false;
            }
            if (redirectTouchesToStickyView) {
                topForViewRelativeOnlyChild = getTopForViewRelativeOnlyChild(currentlyStickingView);
                ev.offsetLocation(0, -1 * ((getScrollY() + stickyViewTopOffset) - topForViewRelativeOnlyChild));
            }
            return super.dispatchTouchEvent(ev);
        }

        private boolean hasNotDoneActionDown = true;

        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            if (redirectTouchesToStickyView) {
                ev.offsetLocation(0, ((getScrollY() + stickyViewTopOffset) - getTopForViewRelativeOnlyChild(currentlyStickingView)));
            }

            if (ev.getAction() == MotionEvent.ACTION_DOWN || ev.getAction() == MotionEvent.ACTION_MOVE) {
                hasNotDoneActionDown = false;
            }

            if (hasNotDoneActionDown) {
                MotionEvent down = MotionEvent.obtain(ev);
                down.setAction(MotionEvent.ACTION_DOWN);
                super.onTouchEvent(down);
                hasNotDoneActionDown = false;
            }

            if (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_CANCEL) {
                hasNotDoneActionDown = true;
            }

            return super.onTouchEvent(ev);
        }

        @Override
        protected void onScrollChanged(int l, int t, int oldl, int oldt) {
            super.onScrollChanged(l, t, oldl, oldt);
            doTheStickyThing();
        }

        private void doTheStickyThing() {
            View viewThatShouldStick = null;
            View approachingView = null;
            for (View v : stickyViews) {
                int viewTop = getTopForViewRelativeOnlyChild(v) - getScrollY() + (clippingToPadding ? 0 : getPaddingTop());
                if (viewTop <= 0) {
                    if (viewThatShouldStick == null || viewTop > (getTopForViewRelativeOnlyChild(viewThatShouldStick) - getScrollY() + (clippingToPadding ? 0 : getPaddingTop()))) {
                        viewThatShouldStick = v;
                    }
                } else {
                    if (approachingView == null || viewTop < (getTopForViewRelativeOnlyChild(approachingView) - getScrollY() + (clippingToPadding ? 0 : getPaddingTop()))) {
                        approachingView = v;
                    }
                }
            }
            if (viewThatShouldStick != null) {
                stickyViewTopOffset = approachingView == null ? 0 : Math.min(0, getTopForViewRelativeOnlyChild(approachingView) - getScrollY() + (clippingToPadding ? 0 : getPaddingTop()) - viewThatShouldStick.getHeight());
                if (viewThatShouldStick != currentlyStickingView) {
                    if (currentlyStickingView != null) {
                        stopStickingCurrentlyStickingView();
                    }
                    stickyViewLeftOffset = getLeftForViewRelativeOnlyChild(viewThatShouldStick);
                    startStickingView(viewThatShouldStick);
                }
            } else if (currentlyStickingView != null) {
                stopStickingCurrentlyStickingView();
            }
        }

        private void startStickingView(View viewThatShouldStick) {
            currentlyStickingView = viewThatShouldStick;
        }

        private void stopStickingCurrentlyStickingView() {
            currentlyStickingView = null;
            removeCallbacks(invalidateRunnable);
        }

        private void notifyHierarchyChanged() {
            if (currentlyStickingView != null) {
                stopStickingCurrentlyStickingView();
            }
            stickyViews.clear();
            findStickyViews(getChildAt(0));
            doTheStickyThing();
            invalidate();
        }

        private void findStickyViews(View v) {
            if (v instanceof ViewGroup) {
                ViewGroup vg = (ViewGroup) v;
                for (int i = 0; i < vg.getChildCount(); i++) {
                    String tag = getStringTagForView(vg.getChildAt(i));
                    if (tag != null && tag.contains(FLOAT)) {
                        stickyViews.add(vg.getChildAt(i));
                    } else if (vg.getChildAt(i) instanceof ViewGroup) {
                        findStickyViews(vg.getChildAt(i));
                    }
                }
            } else {
                String tag = (String) v.getTag();
                if (tag != null && tag.contains(FLOAT)) {
                    stickyViews.add(v);
                }
            }
        }

        private String getStringTagForView(View v) {
            Object tagObject = v.getTag();
            return String.valueOf(tagObject);
        }
    }

完美滑动顶部固定ScrollView,合并可以上拉,下拉加载更多的更多相关文章

  1. Android Demo 下拉刷新+加载更多+滑动删除

    小伙伴们在逛淘宝或者是各种app上,都可以看到这样的功能,下拉刷新和加载更多以及滑动删除,刷新,指刷洗之后使之变新,比喻突破旧的而创造出新的,比如在手机上浏览新闻的时候,使用下拉刷新的功能,我们可以第 ...

  2. 【转】Android循环滚动广告条的完美实现,封装方便,平滑过渡,从网络加载图片,点击广告进入对应网址

    Android循环滚动广告条的完美实现,封装方便,平滑过渡,从网络加载图片,点击广告进入对应网址 关注finddreams,一起分享,一起进步: http://blog.csdn.net/finddr ...

  3. listview下拉刷新 上拉(滑动分页)加载更多

    最 近做的类似于微博的项目中,有个Android功能要使用到listview的向下拉刷新来刷新最新消息,向上拉刷新(滑动分页)来加载更多.新浪微博就是使用这种方式的典型.当用户从网络上读取微博的时候, ...

  4. RecyclerView 判断滑到底部 顶部 预加载 更多 分页 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  5. Android 实现下拉刷新和上拉加载更多的RECYCLERVIEW和SCROLLVIEW

    PullRefreshRecyclerView.java /** * 类说明:下拉刷新上拉加载更多的RecyclerView * Author: gaobaiq * Date: 2016/5/9 18 ...

  6. scroll-view组件实现下拉刷新, 拉到底加载更多

    官方文档已声明,即使在page.json和app.json中开启下拉刷新,scroll-view组件也是不支持的.但我们可以通过曲线救国的方法来实现 实现代码 // wxml <scroll-v ...

  7. 从微信小程序到鸿蒙js开发【13】——list加载更多&回到顶部

    鸿蒙入门指南,小白速来!从萌新到高手,怎样快速掌握鸿蒙开发?[课程入口] 目录: 1.list加载更多 2.list回到顶部 3.<从微信小程序到鸿蒙js开发>系列文章合集 1.list加 ...

  8. 微信小程序 scroll-view 完成上拉加载更多

    我们经常在软件客户端上看到这么一个功能,当我们阅读信息浏览到文章的末尾时,通常会加载出更多的信息.比如,我们在简书客户端上浏览推荐文章时,浏览到屏幕的末尾,此时又加载出了另一页的推荐文章,即实现了上拉 ...

  9. ScrollView嵌套ListView嵌套GridView的上下拉以及加载更多

    ScrollView 效果 ScrollView 说明 一个ScrollView 嵌套ListView 嵌套GridView的上拉加载更多,下拉刷新的demo. 主要是重写了GridView和Lsit ...

随机推荐

  1. 通讯协议序列化解读(二) protostuff详解教程

    上一篇文章 通讯协议序列化解读(一):http://www.cnblogs.com/tohxyblog/p/8974641.html  前言:上一面文章我们介绍了java序列化,以及谷歌protobu ...

  2. python3.6 使用 pymysql 连接 Mysql 数据库及 简单的增删改查操作

    1.通过 pip 安装 pymysql 进入 cmd  输入  pip install pymysql   回车等待安装完成: 安装完成后出现如图相关信息,表示安装成功. 2.测试连接 import ...

  3. ReactNative 4Android源码分析二: 《JNI智能指针之实现篇》

    文/Tamic http://blog.csdn.net/sk719887916/article/details/53462268 回顾 上一篇介绍了<ReactNative4Android源码 ...

  4. Java集合框架总结

    java集合框架主要分为实现了Collection接口的List和Set.映射接口Map. |-- List 有序,元素都有索引,可重复. |-- Set 无序,不可以存储重复的元素. |-- Map ...

  5. MySQL命令行SQL脚本的导入导出小结(数据库的备份与还原)

    1.设置环境变量 要想在命令行下各处都能执行mysql命令,必须在系统变量Path中添加mysql的命令所在的目录.例如我安装的是集成PHP环境的mysql,在D盘xampps下,则我需要将" ...

  6. 新浪微博Oauth2.0授权认证及SDK、API的使用(Android)

    ---------------------------------------------------------------------------------------------- [版权申明 ...

  7. Playground中格式注释语法

    类似于Ruby的ruby document,Xcode的Playground自身也提供一些嵌入文档中的格式注释的语法. 我们先定义一个简单的类: class A{ } 按住opt点击class A,你 ...

  8. Spark技术内幕:Sort Based Shuffle实现解析

    在Spark 1.2.0中,Spark Core的一个重要的升级就是将默认的Hash Based Shuffle换成了Sort Based Shuffle,即spark.shuffle.manager ...

  9. Dynamics CRM2016 查询数据的三种方式的性能对比

    之前写过一个博客,对非声明验证方式下连接组织服务的两种方式的性能进行了对比,但当时只是对比了实例化组织服务的时间,并没有对查询数据的时间进行对比,那有朋友也在我的博客中留言了反映了查询的时间问题,一直 ...

  10. Android 在 SElinux下 如何获得对一个内核节点的访问权限

    点击打开链接 Android 5.0下,因为采取了SEAndroid/SElinux的安全机制,即使拥有root权限,或者对某内核节点设置为777的权限,仍然无法在JNI层访问. 本文将以用户自定义的 ...