Android的FixScrollView自定义控件

需求模仿腾讯课堂视频播放详情页面,效果如图:

1外层滚动控件到顶部,内层控制滚动

2内层滚动到顶部,外层控制滚动

   

基本思路:是最外层有个父ScrollView,子tab页面中有ListView(React-native原生实现也是ScrollView),现在外部的ScrollView设定一个固定高度(屏幕高度+视频高度一半),接下来解决的难点是要使用原生的父ScrollView根据手势以及父ScrollView滚到底部判断是否把事件分发给子页面中ListView让他滚起来?

接下来要了解几个知识点,

①了解下Android事件分发的机制

②了解哪些触摸类型事件以及之间的联系

③如何在ViewGroup中寻找子控件(递归 找一个具体的控件大坑,尤其是再React-Native写的控件树形结构中寻找两个tab页面的ListView)

1View事件分发机制

1.1 三个重要函数(暂时只需要下面那幅图可以完成这个需求)

前面做了基础热身之后,我们现在开始学习View的事件分发机制。View的事件分发主要是由3个函数决定:dispatchTouchEvent 、 onInterceptTouchEvent以及 onTouchEvent。一个触摸事件,如果事件坐标处于ViewGroup所“管辖范围”,首先调用的是该ViewGroupdispatchTouchEvent函数,dispatchTouchEvent函数内部调用onInterceptTouchEvent函数,用于判断是否拦截该事件,如果拦截,则调用ViewGrouponTouchEvent。否则调用子ViewdispatchTouchEvent函数,可以参考如下图:

注意,上述图中,只是描述事件从ViewGroup往下传递过程,没有考虑子ViewonTouchEvent的返回值,即没有考虑事件从子View往上回传的过程。后面再介绍事件回传的过程。ViewGroup是否拦截事件,是通过onTnterceptTouchEvent返回值来确定,当返回true时,表示拦截该事件,那么该系列事件全部传递给ViewGrouponTouchEvent,如果返回false,则表示不拦截该系列事件,该系列事件全部交给子View来处理。为什么我们说是“该系列事件”,而不是说“该事件”呢?注意,View的事件体系中,从down->move->……->move->up。这一个过程为同一个事件系列,如果在onInterceptTouchEvent中返回false,那么所有的事件都不会再交给ViewGroup的的onTouchEvent。

2了解那些触摸事件

down(落下事件如果被消耗即返回true,那么后续move->...move->up事件不会转发)

move--手指屏幕移动事件(可以根据down的x,y值与move事件中判断手势是否向上或者向下滑动)

up--手指抬起事件

3如何在ViewGroup中寻找子控件

使用递归+instanceof可以父ScrollView找到一组类型相同的控件,想找某一个tab子页面中某一个ListView,太坑了!!!!!!太坑了!!!!!!太坑了!!!!!!

一开始的思路是切换tab页面的话其他tab页面Listview控件可见状态会不可见或者消失,完全不是这么回事,后来发现其实View的视图状态一直是可见的,不过那时候技术老大提醒说点击不同的tab时listview所在的屏幕位置发生变化通过x值可以区分也就是要坐标系中的横坐标,判断当前view“屏幕可见”一定是0<x<screenwidth(屏幕宽度),后面直接想用输出打印View的位置坐标,发现各种相似的方法,但是都不是整个屏幕中的坐标。那么有没有其他的方法了呢?采用Hierarchy Viewer去寻找不同tab页面的listview不同点,上个两个tab布局树形的图。

测试页面布局:

第二个tab中的listview的(x,y)坐标

第三个tab中的listviewx,y)坐标

 

后面通过Hierarchy Viewer工具找其他区别发现确实只能是通过控件坐标来弄。

其他代码不贴了,写下遍历控件树代码如下:

  private ScrollView findScrollView(ViewGroup group) {
        if (group != null) {
            for (int i = 0, j = group.getChildCount(); i < j; i++) {
                View child = group.getChildAt(i);
                if (child instanceof ScrollView) {
                    //获取view在整个屏幕中的坐标如果x==0的话代表这个scrollview是正在显示
                    int[] location = new int[2];
                    child.getLocationOnScreen(location);
                    System.out.print("locationx:" + location[0] + ",locationy:" + location[1]);
                    if (location[0] == 0)
                        return (ScrollView) child;
                    else
                        continue;

                } else if (child instanceof ViewGroup) {
                    ScrollView result = findScrollView((ViewGroup) child);
                    if (result != null)
                        return result;
                }
            }
        }
        return null;
    }

4控制发送事件

 @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (!mScrollEnabled) {
            return false;
        }

        int action = ev.getAction();
        if (action == MotionEvent.ACTION_DOWN) {
            //当手指按下的时候
            x1 = ev.getX();
            y1 = ev.getY();
            scrollView = findScrollView(this);
            isIntercept=false;
        }

        if ((action == MotionEvent.ACTION_MOVE) || (action == MotionEvent.ACTION_UP)) {
            //当手指移动或者抬起的时候计算其值
            x2 = ev.getX();
            y2 = ev.getY();
            //是否到底部 默认为已到底部
            isbottom = isAtBottom();
            //向上移动
            if (y1 - y2 > 0) {
                if (scrollView != null) {
                    int st = scrollView.getScrollY();
                    if (!isbottom) {
                        isIntercept = true;
                    } else if (isbottom) {
                        isIntercept = false;
                    }
                    return isIntercept;
                }
            } else if (y2 - y1 > 0) {
                if (scrollView != null) {
                    int st = scrollView.getScrollY();
                    if (!isbottom) {
                        isIntercept = true;
                    } else if (isbottom) {
                        if(st==0) {
                            isIntercept = true;
                        }else
                        {
                            isIntercept = false;
                        }
                    }
                    return isIntercept;
                }
            }
        }return isIntercept;
    }

5效果图:

时间: 2024-09-29 02:11:39

Android的FixScrollView自定义控件的相关文章

荐 android 如何打包自定义控件(转)

荐 android 如何打包自定义控件(转) 目录[-] 方式一:将项目打包成jar包 方式二:项目作为一个library 设计自定义的控件对android开发人员来说,是家常便饭了,但是多次做项目的经验证明了一个道理,自定义的控件,可以在其他项目中,多次使用,所以接下来我们来介绍2种常用的打包方式,并讨论他们的利于病. 我们可以假设想要自定义一个改变文字显示的button(纯属假设,这样简单的功能其实也用不着自定义) 首先写好布局文件mybutton.xml 1 2 3 4 5 6 7 8 9

Android开发技巧——自定义控件之自定义属性

Android开发技巧--自定义控件之自定义属性 掌握自定义控件是很重要的,因为通过自定义控件,能够:解决UI问题,优化布局性能,简化布局代码. 上一篇讲了如何通过xml把几个控件组织起来,并继承某个ViewGroup子类,把它们封装起来使用.这是我们接触到的最简单的一种自定制控件了.但许多时候,我们还需要在布局文件中使用它们的时候,能通过属性传入一些值,来影响最终的显示结果. 我们在做项目中经常会遇到的一个情况:一张图片加一个文本的组合.比如充值账户成功之后显示的一个界面,上面是一个表示成功的

Android应用之——自定义控件ToggleButton

我们经常会看到很多优秀的app上面都有一些很漂亮的控件,用户体验非常好,比如togglebutton就是一个很好的例子,IOS系统下面那个精致的togglebutton如今在android下面也可以实现了,而且还可以自定义它的颜色文字背景图,做出各种漂亮的开关按键出来.这里就用到了android里面一个比较常用的技术--自定义控件. 先来看下我们实现的自定义的togglebutton效果图:      自定义控件的步骤: 1.首先,定义一个类继承View 或者View的子类,这个取决于要定义的控

Android自助餐之自定义控件(一)从layout自定义控件

Android自助餐之自定义控件(一)从layout自定义控件 Android自助餐之自定义控件一从layout自定义控件 从layout自定义控件 从layout自定义控件 layout中新建一个layout <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android&qu

Android 手机卫士--自定义控件(获取焦点的TextView)

本文地址:http://www.cnblogs.com/wuyudong/p/5906735.html,转载请注明源地址. 本文将实现标题栏下面的textview中的文字跑马灯的效果,就是将一行文字水平循环滚动,效果如下: 实现代码如下: <!-- android:ellipsize="end"添加省略点的所在位置 --> <!-- 想让文字出现跑马灯效果,必须让其获取焦点 --> <!-- android:marqueeRepeatLimit="

Android中的自定义控件(二)

案例四: 自定义开关       功能介绍:本案例实现的功能是创建一个自定义的开关,可以自行决定开关的背景.当滑动开关时,开关的滑块可跟随手指移动.当手指松开后,滑块根据开关的状态,滑到最右边或者滑到最左边,同时保存开关的状态,将开关的状态回调给调用者.当然,上述功能系统给定的switch控件也可以实现.      实现步骤:        1. 写一个类继承view,重写两个参数的构造方法.在构造方法中指定工作空间,通过attrs.getAttributeResourceValue方法将jav

Android开发之自定义控件(一)---onMeasure详解

通过本篇博客你将学到以下知识点: ①自定义控件onMeasure的过程 ②彻底理解MeasureSpec ③了解View的绘制流程 ④对测量过程中需要的谷歌工程师给我们准备好的其它的一些方法的源码深入理解. 为了响应文章的开头,我们从一个"Hello World!"的小例子说起,这个例子我们自定义一个View让它显示"Hello World!"非常简单,代码如下 [java] view plain copy package com.example.customvie

[android] 切换按钮-自定义控件

准备两张图片,按钮背景,上面的小开关 创建一个类MyToggleBtn,继承View 实现三个构造方法,传递上下文, 实现构造方法,传递Context对象,在java代码中实例化时主要使用这个 实现构造方法,传递Context对象,AttributeSet对象,在布局文件中主要使用 View对象显示在屏幕上,有几个重要步骤 1.构造方法创建对象 2.测量view的大小 onSeasure(int,int) 3.确定view的位置,view自身有一些建议权,决定权在父view手中 onLayout

Android圆形图片--自定义控件

Android圆形图片控件效果图如下: 代码如下: RoundImageView.java package com.dxd.roundimageview; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.Canvas