完美滑动顶部固定ScrollView,合并可以上拉,下拉加载更多
先看效果:
主要处理:
使用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,合并可以上拉,下拉加载更多的更多相关文章
- Android Demo 下拉刷新+加载更多+滑动删除
小伙伴们在逛淘宝或者是各种app上,都可以看到这样的功能,下拉刷新和加载更多以及滑动删除,刷新,指刷洗之后使之变新,比喻突破旧的而创造出新的,比如在手机上浏览新闻的时候,使用下拉刷新的功能,我们可以第 ...
- 【转】Android循环滚动广告条的完美实现,封装方便,平滑过渡,从网络加载图片,点击广告进入对应网址
Android循环滚动广告条的完美实现,封装方便,平滑过渡,从网络加载图片,点击广告进入对应网址 关注finddreams,一起分享,一起进步: http://blog.csdn.net/finddr ...
- listview下拉刷新 上拉(滑动分页)加载更多
最 近做的类似于微博的项目中,有个Android功能要使用到listview的向下拉刷新来刷新最新消息,向上拉刷新(滑动分页)来加载更多.新浪微博就是使用这种方式的典型.当用户从网络上读取微博的时候, ...
- RecyclerView 判断滑到底部 顶部 预加载 更多 分页 MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
- Android 实现下拉刷新和上拉加载更多的RECYCLERVIEW和SCROLLVIEW
PullRefreshRecyclerView.java /** * 类说明:下拉刷新上拉加载更多的RecyclerView * Author: gaobaiq * Date: 2016/5/9 18 ...
- scroll-view组件实现下拉刷新, 拉到底加载更多
官方文档已声明,即使在page.json和app.json中开启下拉刷新,scroll-view组件也是不支持的.但我们可以通过曲线救国的方法来实现 实现代码 // wxml <scroll-v ...
- 从微信小程序到鸿蒙js开发【13】——list加载更多&回到顶部
鸿蒙入门指南,小白速来!从萌新到高手,怎样快速掌握鸿蒙开发?[课程入口] 目录: 1.list加载更多 2.list回到顶部 3.<从微信小程序到鸿蒙js开发>系列文章合集 1.list加 ...
- 微信小程序 scroll-view 完成上拉加载更多
我们经常在软件客户端上看到这么一个功能,当我们阅读信息浏览到文章的末尾时,通常会加载出更多的信息.比如,我们在简书客户端上浏览推荐文章时,浏览到屏幕的末尾,此时又加载出了另一页的推荐文章,即实现了上拉 ...
- ScrollView嵌套ListView嵌套GridView的上下拉以及加载更多
ScrollView 效果 ScrollView 说明 一个ScrollView 嵌套ListView 嵌套GridView的上拉加载更多,下拉刷新的demo. 主要是重写了GridView和Lsit ...
随机推荐
- Bootstrap3 表单-被支持的控件:文本域
支持多行文本的表单控件.可根据需要改变 rows 属性. <textarea class="form-control" rows="3"></ ...
- Sqoop-1.4.5用户手册
本文以Sqoop User Guide (v1.4.5)为主,对Sqoop-1.4.5的用户手册进行翻译,同时会结合一些实际操作中的注意事项一并写入.由于原文档很长,本文首先会以实际使用到的部分为主, ...
- ubuntu安装qq
安装的版本是国际版 1.安装依赖库 sudo apt-get install libgtk2.0-0:i386 sudo apt-get install lib32ncurses5 2.下载 下载链接 ...
- Spring配置优化_构造器注入+自动装配
2014-05-16 09:01:08上课内容: 依赖注入的第二种注入方式:构造器注入 创建带参数的构造方法,参数类型为注入类的类型 项目要先添加Spring支持: package com; publ ...
- SVN与eclipse整合与使用、SVN与Apache整合
SVN与eclipse整合 下载SVN插件(http://subclipse.tigris.org) http://subclipse.tigris.org/servlets/ProjectDocum ...
- Java基本语法-----java变量
1.变量的概述 用于存储可变数据的容器. 2.变量存在的意义 计算机主要用于处理生活中的数据,由于生活中存在大量的可变数据,那么计算机就必须具备存储可变数据的能力. 比如: 1.时间每一秒都在发生变化 ...
- 安卓自定义日期控件(仿QQ,IOS7)续
本篇是在原来的基础上修改了界面效果,使其更加接近ios7,qq等日期选择控件,看图: 源码地址:http://download.csdn.net/detail/baiyuliang2013/87601 ...
- C语言--static修饰变量
Static在C语言里面有两个作用,第一个是修饰变量,第二个是修饰函数. 1.Static修饰变量 按照作用范围的不同,变量分为局部变量和全局变量.如果用static修饰变量,不论这个变量是全局的还是 ...
- Linux技巧:一次删除一百万个文件的最快方法
最初的测评 昨天,我看到一个非常有趣的删除一个目录下的海量文件的方法.这个方法来自http://www.quora.com/How-can-someone-rapidly-delete-400-000 ...
- JPA(三)之实体关系一对多(多对一)
1.背景介绍: 对于购买商品时,订单信息(Order)和订单商品信息(OrderItem)的关系就是一对多的关系. 2.实体bean: Order.java代码 ? 1 2 3 4 5 6 7 ...