之前的一遍学习笔记主要就Android滑动冲突中,在不同方向的滑动所造成冲突进行了了解,这样的冲突非常easy理解,当然也非常easy解决。今天,就同方向的滑动所造成的冲突进行一下了解,这里就先以垂直方向的滑动冲突为背景,这也是日常开发中最常见的一种情况。

这里先看一张效果图

由于GIF 图片大小的限制。截图效果不是非常好

上图是在购物软件上常见的上拉查看图文详情,关于这中动画效果的实现。事实上实现总体的效果,办法是有非常多的,网上有非常多相关的样例,可是对某些细节的处理不是非常清晰。比方,下拉之后显示的部分(比如底部的图文详情)又是一个类ScrollView的控件(比方WebView)的话,又会产生新的问题。这里就下面拉查看图文详情为背景做一下同方向滑动冲突的分析。

总体思路

这里看下图

首先。关于这张图做一些设定:

  • 黑色的框代表手机屏幕
  • 绿色的框代表一个外层的ScrollView
  • 两个红色的框代表嵌套在里面的两个类ScrollView控件1,这里我们就临时简称为 SUp,SDown

好了,接下来就分析一下实现整个流程的过程。

这里必须明白的一点。不管何时。SUp和SDown可见的部分始终是手机屏幕的高度。知道了这一点,我们就能够按下面步骤展开

首先,我们确保外部的ScrollView不拦截滑动事件,这样SUp必定获得此次事件,然后依据其Action_Move事件,当其为向下滑动自身滑动距离+屏幕高度=其自身高度 时。就可以觉得SUp滑动到了底部,此时外部ScrollView可拦截滑动事件,从而保证能够继续向下滑动,这个时候底部SDown就显示出来了。

同理,这时候外部ScrollView不同意外部ScrollView拦截滑动事件。由SDown处理。依据其Action_move事件,当其为向上滑动,且自身可滑动距离为0时。就说明SDown已经滑动到了顶部,这时外部ScrollView又能够获得拦截滑动事件的权利,从而保证整个视图能够向上继续滑动。此时SUp再次显示,有開始新一轮循环拦截。

这样总体的一个流程就能够实现动图中的效果。

好了。说完原理。还是看代码。

代码实现

SUp实现

public class UpScrollView extends ScrollView {
/**
* 屏幕高度
*/
private int mScreenHeight;
/**
* 上一次的坐标
*/
private float mLastY;
/**
* 当前View滑动距离
*/
private int mScrollY;
/**
* 当前View内子控件高度
*/
private int mChildH; public UpScrollView(Context context) {
super(context);
init(context);
} public UpScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
} public UpScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context); } private void init(Context context) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics dm = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(dm);
mScreenHeight = dm.heightPixels;
} @Override
public boolean onTouchEvent(MotionEvent ev) {
//默认设定顶层View不拦截 getParent().getParent().requestDisallowInterceptTouchEvent(true); switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:
float y = ev.getY();
float deltaY = y - mLastY; mChildH = this.getChildAt(0).getMeasuredHeight(); int translateY = mChildH - mScrollY; //向上滑动时。假设translateY等于屏幕高度时,即表明滑动究竟部。可又顶层View控制滑动
if (deltaY < 0 && translateY == mScreenHeight) {
getParent().getParent().requestDisallowInterceptTouchEvent(false);
}
break;
default:
break; } return super.onTouchEvent(ev);
} @Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
mScrollY = t;
}
}

这里在ACTION_MOVE里做了减法,事实上道理是一样的。

onScrollChanged 是在View类中实现。查看其API能够看到其第二个參数t解释

  • @param t Current vertical scroll origin.

即为当前View此次滑动的距离

SDown实现

public class MyWebView extends WebView {
public float oldY;
private int t; public MyWebView(Context context) {
super(context);
init();
} public MyWebView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
} public MyWebView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
} private void init() {
WebSettings settings = getSettings();
settings.setJavaScriptEnabled(true);
setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
} @Override
public boolean onTouchEvent(MotionEvent ev) {
getParent().getParent().requestDisallowInterceptTouchEvent(true);
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
oldY = ev.getY();
break;
case MotionEvent.ACTION_MOVE: float Y = ev.getY();
float Ys = Y - oldY;
if (Ys > 0 && t == 0) {
getParent().getParent().requestDisallowInterceptTouchEvent(false);
}
break; } return super.onTouchEvent(ev);
} @Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
this.t = t;
super.onScrollChanged(l, t, oldl, oldt);
} }

看以看到,这里底部的View并没有继承ScrollView。而是选择继承了WebView。这里仅仅是为了方便。当然继承ScrollView也是没有问题。这里仅仅是须要按实际情况考虑。由于底部图文详情的内容就是一个WebView载入数据。

这个类的实现,依照之前说的原理应该非常好理解。

外部ScrollView

public class CustomerScrollViews extends ScrollView {
/**
* 屏幕高度
*/
private int mScreenHeight; private UpScrollView upScrollView;
private MyWebView myWebView;
private boolean init = false; private float fator = 0.2f;
private int factorHeight; private boolean upShow = true; public CustomerScrollViews(Context context) {
super(context);
init(context);
} public CustomerScrollViews(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
} public CustomerScrollViews(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context); } private void init(Context context) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics dm = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(dm);
mScreenHeight = dm.heightPixels;
factorHeight = (int) (mScreenHeight * fator);
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (!init) { LinearLayout parentView = (LinearLayout) getChildAt(0);
//获得内部的两个子view
upScrollView = (UpScrollView) parentView.getChildAt(0);
myWebView = (MyWebView) parentView.getChildAt(2);
// //并设定其高度为屏幕高度
upScrollView.getLayoutParams().height = mScreenHeight;
myWebView.getLayoutParams().height = mScreenHeight;
init = true;
} super.onMeasure(widthMeasureSpec, heightMeasureSpec);
} @Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (changed) {
scrollTo(0, 0);
}
} @Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_UP:
int scrollY = getScrollY();
if (upShow) {
if (scrollY <= factorHeight) {
smoothScrollTo(0, 0);
} else {
smoothScrollTo(0, mScreenHeight);
upShow = false; }
} else {
int scrollpadding = mScreenHeight - scrollY;
if (scrollpadding >= factorHeight) {
this.smoothScrollTo(0, 0);
upShow = true; } else {
this.smoothScrollTo(0, mScreenHeight);
} } return true; }
return super.onTouchEvent(ev);
} }

这个类的实现。就非常灵活了。在onMeasure方法中初始化完内部的View之后,在OnTouch方法中就能够依据实际需求完毕不同的逻辑实现,这里仅仅是为了仿照查看图文详情的效果。对整个视图通过ScrollView的smoothScrollTo方法进行位移变化。这个逻辑非常简单。

这里重点说一下一个地方:

upScrollView = (UpScrollView) parentView.getChildAt(0);
myWebView = (MyWebView) parentView.getChildAt(2);

你可能会奇怪中间的child(1)去了哪里?这里还要从MainActivity的布局文件说起

dual_scrollview_activity_layout1.xml

<?

xml version="1.0" encoding="utf-8"?

>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"> <com.example.dreamwork.activity.superscrollview.CustomerScrollViews
android:layout_width="match_parent"
android:layout_height="match_parent"> <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"> <com.example.dreamwork.activity.superscrollview.UpScrollView android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical"> <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"> <ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitXY"
android:src="@drawable/taobao" />
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitXY"
android:src="@drawable/taobao" /> <TextView
android:textSize="20sp"
android:padding="10dp"
android:gravity="center"
android:layout_marginTop="20dp"
android:layout_marginBottom="60dp"
android:text="查看图文详情"
android:layout_width="match_parent"
android:layout_height="wrap_content" /> </LinearLayout>
</com.example.dreamwork.activity.superscrollview.UpScrollView> <include layout="@layout/selector_tab_items" /> <com.example.dreamwork.activity.superscrollview.MyWebView
android:id="@+id/web"
android:layout_width="match_parent"
android:layout_height="match_parent"/> </LinearLayout>
</com.example.dreamwork.activity.superscrollview.CustomerScrollViews> </LinearLayout>

整个布局文件能够看出,我们在CustomerScrollViews这个最外层的自己定义ScrollView内部又放置了两个自己定义的ScrollView(就如我们看到的原理图那样)。仅仅只是在这两个ScrollView类控件的中间通过layout又放置一个LinearLayout,里面的内容就是在动图中看到的那个中间的写着qq,baidu字样的用于切换WebView内容的一个View。这里就不贴代码了。

这样,你就能够理解之前的child(1)为什么被跳过了吧。

使用

public class DualScrollViewActivity1 extends Activity implements View.OnClickListener {

    private MyWebView webView;

    private TextView sinaTv, qqTv, baiduTv;
private View line1, line2, line3; private final String BAIDU = "http://www.baidu.com";
private final String QQ = "http://www.qq.com";
private final String SINA = "http://sina.cn"; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
InitView();
sinaTv.performClick();
} private void InitView() {
setContentView(R.layout.dual_scrollview_activity_layout1);
webView = V.f(this, R.id.web); sinaTv = V.f(this, R.id.one);
sinaTv.setOnClickListener(this);
qqTv = V.f(this, R.id.two);
qqTv.setOnClickListener(this);
baiduTv = V.f(this, R.id.three);
baiduTv.setOnClickListener(this); line1 = V.f(this, R.id.line1);
line2 = V.f(this, R.id.line2);
line3 = V.f(this, R.id.line3);
} @Override
public void onClick(View v) {
reset();
String url = "";
switch (v.getId()) {
case R.id.one:
line1.setVisibility(View.VISIBLE);
url = SINA;
break;
case R.id.two:
line2.setVisibility(View.VISIBLE);
url = QQ;
break;
case R.id.three:
line3.setVisibility(View.VISIBLE);
url = BAIDU;
break;
default:
break;
} webView.loadUrl(url);
} private void reset(){
line1.setVisibility(View.GONE);
line2.setVisibility(View.GONE);
line3.setVisibility(View.GONE);
}
}

这里关于底部View内容更新,WebView 通过载入不同URL实现不同视图效果。仅仅是作为Demo測试。

这里对滑动冲突的解决方法。是由内而外的展开,默认使顶层View失去拦截能力。在由底部View的滑动距离,做出不同逻辑推断控制了顶层的拦截与否;这也是比較easy理解和实现的思路。当然。对于此类滑动冲突,有非常多不同思路,这里仅仅是列举其一。

实际开发开发中,这样的带有同方向滑动特性的控件嵌套时,产生的问题不仅仅是滑动冲突。有时候会有内容显示不全或不显示的情况。

最常见如ScrollView嵌套ListView。这样的情况仅仅需自己定义ListView使其高度计算为一个非常大的值,某种意义上让其失去了滑动的特性,可是这样也让ListView貌似失去了视图回收机制。这样的时候假设载入非常多非常多非常多图片,效果就会非常不理想。对于这样的情况,通过对ListView加入headView及footView也是一种解决的办法。但也得实际UI情况同意。

ScrollView嵌套RecyclerView时略微麻烦一点。须要自己定义ScrollView,还须要自己定义实现LinearLayoutManager。

由此可见。在Android中这样的带有相似特性的控件嵌套后,问题还是非常多的。对此,Google出了一个叫做NestedScrolling的东西。接下来准备研究一下,看看其在解决滑动冲突方面的优势。



  1. 这里说类ScrollView控件。就是说相似于ScrollView特性的控件。即自带滚动特性的控件。

关于Android滑动冲突的解决方法(二)的更多相关文章

  1. (转载)Android滑动冲突的完美解决

    Android滑动冲突的完美解决 作者:softwindy_brother 字体:[增加 减小] 类型:转载 时间:2017-01-24我要评论 这篇文章主要为大家详细介绍了Android滑动冲突的完 ...

  2. Android滑动冲突解决方法

    叙述 滑动冲突可以说是日常开发中比较常见的一类问题,也是比较让人头疼的一类问题,尤其是在使用第三方框架的时候,两个原本完美的控件,组合在一起之后,忽然发现整个世界都不好了. 关于滑动冲突 滑动冲突分类 ...

  3. Android滑动冲突解决

    (1).场景一:外部滑动方向跟内部滑动方向不一致,比如外部左右滑动,内部上下滑动   ViewPager+Fragment配合使用,会有滑动冲突,但是ViewPager内部处理了这种滑动冲突   如果 ...

  4. PullToRefreshListView中嵌套ViewPager滑动冲突的解决

    PullToRefreshListView中嵌套ViewPager滑动冲突的解决 最近恰好遇到PullToRefreshListView中需要嵌套ViewPager的情况,ViewPager 作为头部 ...

  5. Git冲突与解决方法【转】

    本文转载自:https://www.cnblogs.com/gavincoder/p/9071959.html Git冲突与解决方法 1.git冲突的场景 情景一:多个分支代码合并到一个分支时: 情景 ...

  6. 安装Android studio出现'tools.jar' seems to be not in Android Studio classpath......的解决方法

    安装Android studio出现'tools.jar' seems to be not in Android Studio classpath......的解决方法 原创 2015年07月31日 ...

  7. git 本地库推送远程库 版本冲突的解决方法

    参考: http://blog.csdn.net/shiren1118/article/details/7761203 github上的版本和本地版本冲突的解决方法 $ git push XXX ma ...

  8. svn冲突文件解决方法

    svn冲突文件解决方法 工具/原料 svn客户端 方法/步骤 1 通过SVN客户端更新需要的文件,如果出现有感叹号的文件,找到出现感叹号的文件. 2 选择感叹号文件,即冲突文件,单击鼠标右键对冲突文件 ...

  9. Hyper-V与VirtualBox或VMware虚拟机软件冲突的解决方法(VirtualBox只能创建32位虚拟机)

    Hyper-V与VirtualBox或VMware虚拟机软件冲突的解决方法 Hyper-V是微软的虚拟化软件,功能类似VirtualBox.VMware,可以用来创建虚拟机. 虚拟化软件都是基于CPU ...

随机推荐

  1. docker+Battery Historian 环境搭建(电量分析)

    docker 安装(windows) 1.  下载 https://docs.docker.com/docker-for-windows/install/  和 安装和添加环境变量(...) 2. 安 ...

  2. 关于EGE图形库在CodeBlocks下的配置

    非常感谢[地球]呵呵@的细心帮助,我差点都放弃了! 我这里使用的是Code::Blocks svn 10595 与 TDM-GCC 5.10 首先下载为 GCC 5.0 以上编译好的 EGE 文件,稍 ...

  3. [android 代码search地址]

    http://www.androidcodesearch.com/source/packages/apps/Settings/src/com/android/settings/SecuritySett ...

  4. [android开发篇]elicpse安装教程

    http://www.runoob.com/eclipse/eclipse-install.html Eclipse 安装(Neon 版本) Eclipse 最新版本 Eclipse Neon,这个首 ...

  5. Python模块安装路径初探

    在调用MySQL第三方工具mysqlrplsync是报错 mysqlrplsync --master=checksum:checksum@master_ip:3306 --slave=checksum ...

  6. ACM程序设计选修课——1081: 堆(BFS)

    1081: 堆 Time Limit: 1 Sec  Memory Limit: 128 MB Submit: 26  Solved: 9 Description Input Output Sampl ...

  7. 刷题总结——系列维护(ssoi)

    题目: 题解: 题解如上图,至于计算大于s的数字的数量和小于s数字的和用权值线段树或者树状数组维护就行了···注意离散化 另外发现cout和puts比printf快好多····· 代码: #inclu ...

  8. Web Services 教程

    Web Services 可使您的应用程序成为 Web 应用程序. Web Services 通过 Web 进行发布.查找和使用. Web Services 是应用程序组件 Web Services  ...

  9. [HDU-4825] Xor-Sum (01字典树)

    Problem Description Zeus 和 Prometheus 做了一个游戏,Prometheus 给 Zeus 一个集合,集合中包含了N个正整数,随后 Prometheus 将向 Zeu ...

  10. Pizza Delivery

    Pizza Delivery 时间限制: 2 Sec  内存限制: 128 MB 题目描述 Alyssa is a college student, living in New Tsukuba Cit ...