• 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效果图:

  • 相关阅读:
    函数传参总结
    集合操作总结
    深浅拷贝总结
    三级列表展示
    文件操作总结
    vue-router之嵌套路由
    vue-router之动态路由
    Sublime编辑VUE实现代码高亮
    Windows系统下Vue开发环境搭建详解版
    C#调用快递鸟电子面单API实现批量打印电子面单功能
  • 原文地址:https://www.cnblogs.com/lmf-techniques/p/6595707.html
Copyright © 2020-2023  润新知