先看效果:

主要处理:

使用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. Go 语言变量作用域

    作用域为已声明标识符所表示的常量.类型.变量.函数或包在源代码中的作用范围. Go 语言中变量可以在三个地方声明: 函数内定义的变量称为局部变量 函数外定义的变量称为全局变量 函数定义中的变量称为形式 ...

  2. Bootstrap3 栅格系统-简介

    Bootstrap 提供了一套响应式.移动设备优先的流式栅格系统,随着屏幕或视口(viewport)尺寸的增加,系统会自动分为最多12列.它包含了易于使用的预定义类,还有强大的mixin 用于生成更具 ...

  3. Android存储之SQLite数据库

    Android存储之SQLite数据库数据库 创建数据库 package --; import android.content.Context; import android.database.sql ...

  4. Android 5.0 调色 Palette调色功能

    Palette非常好用,也非常好玩. Palette的作用是从图像中提取突出的颜色,这样我们可以根据提取到的色值把它赋给Toolbar,标题,状态栏等,可以使我们的整个界面色调统一,效果非常好看. P ...

  5. ant编译mysql驱动

    修改驱动源码后需要重新编译构建,由于mysql编译需要两个jdk版本且还需要hibernate4和junit,这里记录下. 安装ant. 配置两个jdk,5和8.并修改build.xml配置,如下: ...

  6. Android图表库MPAndroidChart(四)——条形图的绘制过程过程,隐隐约约我看到了套路

    Android图表库MPAndroidChart(四)--条形图的绘制过程过程,隐隐约约我看到了套路 在学习本课程之前我建议先把我之前的博客看完,这样对整体的流程有一个大致的了解 Android图表库 ...

  7. 软件测试之BUG分析定位概述(QA如何分析定位BUG)

    你是否遇到这样的场景? QA发现问题后找到DEV说: 不好了,你的程序出问题了! DEV(追查半小时之后): 唉,是你们测试环境配置的问题 唉,是你们数据不一致 唉,是你们**程序版本不对 唉,是** ...

  8. Apache shiro集群实现 (七)分布式集群系统下---cache共享

    Apache shiro集群实现 (一) shiro入门介绍 Apache shiro集群实现 (二) shiro 的INI配置 Apache shiro集群实现 (三)shiro身份认证(Shiro ...

  9. 13 SQLiteOpenHelper SQLiteDatabase详解

    创建数据库: 1. 创建一个类继承SQLiteOpenHelper 2. 创建继承对象 new SQLiteOpenHelper() 3. 用创建的对象获取可写或者可读的SQLiteDatabase ...

  10. 【移动开发】plurals

    不同的语言对数量的语法规定有不同的规则.在英语里面,例如,1是特例.我们会直接写1book,而针对一个以上的我们会在book后加复数形式.这种区别对单数和复数来说是很普遍的,但是其他的语言做了更好的区 ...