当前位置: 首页 > news >正文

企业网站优化公司怎么自己做导航网站

企业网站优化公司,怎么自己做导航网站,南宁做网站 的,山东省建设资格中心网站文章目录为什么要理解Android事件分发机制?滑动冲突类问题我们以什么开始?代码如下:activity xml 代码:Activity代码:item_user.xml代码修改后代码如下:Activity xmlactivity代码item_gift.xml问题出现了An…

文章目录

  • 为什么要理解Android事件分发机制?
      • 滑动冲突类问题
  • 我们以什么开始?
    • 代码如下:
      • activity xml 代码:
      • Activity代码:
      • item_user.xml代码
    • 修改后代码如下:
      • Activity xml
      • activity代码
      • item_gift.xml
    • 问题出现了
  • Android 事件分发机制
  • 实现我们的功能
    • 再看一下我们面临的问题
      • 尝试解决
        • 1、 从OnTouchListener入手
        • 2、重写RecyclerView的OnTouch方法
        • 3、重写RecyclerView的OnTouch方法并调用其onTouch方法。
      • 尝试其他方案
        • 1、 在onInterceptTouchEvent中直接返回false?
        • 2、直接在dispatchTouchEvent中处理可以吗?
        • 3、在OnTouch中依然直接返回false,同时自己重置scrollState可以吗?

为什么要理解Android事件分发机制?

我最开始去了解Android事件分发机制,是在遇到滑动冲突的时候,相信很多朋友也是在遇到滑动冲突时候去深入了解该机制的,但是实际上,除了解决滑动冲突,它还能处理很多复杂的问题。以下将列出一系列相关问题,后面会根据举几个笔者最近遇到的问题,以及解决的思路。

滑动冲突类问题

  1. RecyclerView 与 ScrollView嵌套使用,同方向上的滑动冲突
  2. ViewPager 与 水平滚动相关View的联合使用
    事件传递时机类问题:
  3. 如何让RecyclerView无法滚动?
  4. 如何让RecyclerView的子项无法点击?
  5. 如何在RecyclerView重叠使用时,设置了点击事件的View都可以响应?
    ……

我们以什么开始?

这里我先设定一个需求背景:
我有一个用户头像列表,上边会展示用户的基本信息:用户头像,头像装饰,用户名,点击用户头像可以弹出用户信息(这里用Toast代替)。目前实现的UI如下
在这里插入图片描述

代码如下:

activity xml 代码:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".event.EventDispatchActivity"><androidx.recyclerview.widget.RecyclerViewandroid:layout_marginTop="20dp"android:id="@+id/rv_users"android:layout_width="match_parent"android:layout_height="match_parent"/></FrameLayout>

Activity代码:

class EventDispatchActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_event_dispatch)val itemDecoration = object : ItemDecoration(){override fun getItemOffsets(outRect: Rect,view: View,parent: RecyclerView,state: RecyclerView.State) {super.getItemOffsets(outRect, view, parent, state)outRect.bottom = 40}}findViewById<RecyclerView>(R.id.rv_users)?.let {it.adapter = UserAdapter()it.layoutManager = GridLayoutManager(it.context, 4)it.addItemDecoration(itemDecoration)}}class UserAdapter : RecyclerView.Adapter<UserAdapter.ViewHolder>() {class ViewHolder(view: View): RecyclerView.ViewHolder(view) {val ivUser = itemView.findViewById<ImageView>(R.id.iv_user)val tvUser = itemView.findViewById<TextView>(R.id.tv_name)}override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {return ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_user, parent, false))}override fun onBindViewHolder(holder: ViewHolder, position: Int) {val name = "user $position"holder.ivUser.setOnClickListener {Toast.makeText(holder.ivUser.context, name, Toast.LENGTH_SHORT).show()}holder.tvUser.text = name}override fun getItemCount(): Int {return 16}}
}

item_user.xml代码

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"android:gravity="center_horizontal"><ImageViewandroid:layout_width="48dp"android:layout_height="48dp"android:id="@+id/iv_user"android:src="@drawable/user"app:tint="@color/purple_500" /><TextViewandroid:layout_marginTop="10dp"android:layout_width="wrap_content"android:layout_height="20dp"android:id="@+id/tv_name"android:text="用户名"android:singleLine="true"/></LinearLayout>

现在有一个新的需求,需要在每两个头像之间加上红包展示,如果点击红包可以弹出红包弹窗(此处用Toast代替)。方案当然可以选择,在原来的Item上加中间的红包,但是此处,我选择的方案是,新创建一个RecyclerView,专门展示红包。(不要考虑方案的优劣,只是当时情况需要选择此方案)新的UI展示如下:

在这里插入图片描述

修改后代码如下:

Activity xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".event.EventDispatchActivity"><androidx.recyclerview.widget.RecyclerViewandroid:layout_marginTop="20dp"android:id="@+id/rv_users"android:layout_width="match_parent"android:layout_height="match_parent"/><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/rv_user_gift"android:layout_marginTop="20dp"android:layout_width="match_parent"android:layout_height="match_parent"/></FrameLayout>

activity代码

class EventDispatchActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_event_dispatch)val itemDecoration = object : ItemDecoration(){override fun getItemOffsets(outRect: Rect,view: View,parent: RecyclerView,state: RecyclerView.State) {super.getItemOffsets(outRect, view, parent, state)outRect.bottom = 40}}findViewById<RecyclerView>(R.id.rv_users)?.let {it.adapter = UserAdapter()it.layoutManager = GridLayoutManager(it.context, 4)it.addItemDecoration(itemDecoration)}findViewById<RecyclerView>(R.id.rv_user_gift)?.let {it.adapter = GiftAdapter()it.layoutManager = GridLayoutManager(it.context, 2)it.addItemDecoration(itemDecoration)}}class UserAdapter : RecyclerView.Adapter<UserAdapter.ViewHolder>() {class ViewHolder(view: View): RecyclerView.ViewHolder(view) {val ivUser = itemView.findViewById<ImageView>(R.id.iv_user)val tvUser = itemView.findViewById<TextView>(R.id.tv_name)}override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {return ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_user, parent, false))}override fun onBindViewHolder(holder: ViewHolder, position: Int) {val name = "user $position"holder.ivUser.setOnClickListener {Toast.makeText(holder.ivUser.context, name, Toast.LENGTH_SHORT).show()}holder.tvUser.text = name}override fun getItemCount(): Int {return 16}}class GiftAdapter : RecyclerView.Adapter<GiftAdapter.ViewHolder>() {class ViewHolder(view: View): RecyclerView.ViewHolder(view) {val giftView = itemView.findViewById<ImageView>(R.id.iv_gift)}override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {return ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_gift, parent, false))}override fun onBindViewHolder(holder: ViewHolder, position: Int) {val gift = "gift $position"holder.giftView.setOnClickListener {Toast.makeText(holder.giftView.context, gift, Toast.LENGTH_SHORT).show()}}override fun getItemCount(): Int {return 8}}}

item_gift.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="78dp"android:orientation="vertical"android:gravity="center_horizontal"><ImageViewandroid:id="@+id/iv_gift"android:layout_width="36dp"android:layout_height="36dp"android:layout_marginTop="6dp"android:src="@drawable/gift"/></LinearLayout>

问题出现了

按照上面的实现,展示上是没问题的,但是用户操作上出现了问题。
首先说一下我想要的结果:

  1. 用户头像可点击,点击后可出用户信息弹窗
  2. 红包可点击,点击后可出红包弹窗

现在的问题是:
红包可点击(符合需求),但是用户头像不可点击(不符合需求)了。

问题原因推测:

  1. 只有用户列表时,用户信息是可以点击的
  2. 加上新的列表后用户信息点不了,应该是点击事件被顶层的红包层拦截了。
  3. 目标方案:顶层只有红包区域可点击,其他位置要正常传递给下面的头像。

但是,我应该从哪开始改起呢?带着上面的问题,我们开始研究分发机制,达到最终上面的效果。

Android 事件分发机制

Android 的触摸事件,是从屏幕硬件触发,最终到达目前正在展示的Activity的,至于这期间如何传递的,此处不做讨论,我们讨论的起点,从Activity接收到触摸事件开始。
流程图
参考文档:
Android 事件传递相关流程图
源码分析
参考文档:
Android 事件分发源码解析(基于API31)

实现我们的功能

事件传递相关的流程与 源码分析在上面的文档中已经看到了,接下来,我们来实现一下,文章开头提到的实现目标。

根据问题的前提,我们知道这是两个RecyclerView重叠摆放的问题导致的,当然我们可以自定义ViewGroup,重新定义两个View的摆放规则,但是那不在我们本次讨论范围内,我们只考虑当前的实现方案,如何最终实现这个功能。

再看一下我们面临的问题

最上层的RecyclerView会拦截所有的事件分发,所以最上层的RecyclerView 中的View会被分发到事件,而下边的RecyclerView不能接收到事件。

在这里插入图片描述

那么有没有什么办法,可以让上边的RecyclerView 只在有View的地方响应点击事件?

尝试解决

1、 从OnTouchListener入手

首先肯定是不会考虑重写一个View的,那么最简单的方案是设置一个OnTouchListener,但是这种方案可不可以呢?不可以。
原因:如果我在OnTouchListener的onTouch方法返回true,那么礼物的点击事件无法执行了,如果返回false,和没加一样。

2、重写RecyclerView的OnTouch方法

既然没法子通过OnTouchListener处理,那么修改onTouchEvent方法,直接返回false,也就是不处理点击事件,但是让RecyclerView只分发,不处理点击事件,这可行不可行呢?

class DispatchRecyclerView : RecyclerView {constructor(context: Context) : super(context)constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context,attrs,defStyleAttr)override fun onTouchEvent(e: MotionEvent?): Boolean {return false}
}

尝试修改了一下,是可行的。
但是出现了一个新的问题,多次极端滑动(一直往下滑,但实际上它下面没有内容了)之后,不再执行到RecyclerView的onTouch方法了,因为它的滑动状态一直没有被重置成IDLE,一直是DRAGING状态。DRAGING状态时,它的OnTouch里边返回false也没用,因为在拦截那一步,已经被处理成拦截了。

也就是:在某一状态下,RecyclerView的scrollState 变成了 DRAGGING,且,onInterceptTouchEvent反回了true。而RecyclerView 没有重写dispatchTouchEvent,那么哦我们知道,如果“onInterceptTouchEvent”返回了true那么它将不会进行事件分发。

原因是,在onInterceptToucEvent中,RecyclerView把scrollState置成了DRAGGING,在ACTION_UP的时候,是在onTouch里边重置的滚动状态,而我们直接在onTouch中返回了false,导致重置scrollState步骤没有执行到。

3、重写RecyclerView的OnTouch方法并调用其onTouch方法。

class DispatchRecyclerView : RecyclerView {constructor(context: Context) : super(context)constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context,attrs,defStyleAttr)override fun onTouchEvent(e: MotionEvent?): Boolean {super.onTouchEvent(e) // 执行操作return false // 同时让父布局继续往下分发}
}

当我们添加了该方法,发现问题解决了~

尝试其他方案

1、 在onInterceptTouchEvent中直接返回false?

在这个方法中返回false,不行,因为我们知道,即使不拦截,如果RecyclerView发现没有可以处理事件的子View,最后,仍然会回调自己的onTouchEvent。

2、直接在dispatchTouchEvent中处理可以吗?

其实是可以的,这是所有事件分发的入口,但是如果我们重写整个方法,需要把整个找子View的过程都写一遍,这得不偿失。

3、在OnTouch中依然直接返回false,同时自己重置scrollState可以吗?

这种方式是可以的,但需要写的代码可能会更多。

http://www.yayakq.cn/news/245668/

相关文章:

  • 阿里云网站建设需要多少钱wordpress模板地址
  • 景区加强网站建设深圳手机网站建设牛商网
  • 枣庄三合一网站开发公司如何查看一个网站用什么程序做的
  • 帝国网站源码手机详情页设计素材
  • 英文网站建设390冒用他人公司做网站
  • dw怎么做网站跳转有没有做兼职的网站吗
  • 网站建设公司盈利模式短视频获客
  • 做常识的网站wordpress博客站点
  • 河北自助建站系统平台网站开发和维护
  • 如何开发自己公司的网站工业互联网平台架构图
  • 电商网站设计的原则万网域名信息
  • wordpress 网站迁移如何注册网站主办者
  • 企业介绍微网站怎么做网站建设上机课
  • 如何做网站粘贴广告建wap手机网站
  • 17网一起做网店普宁seo的最终目的是?
  • 搜索引擎营销是目前最主要的网站推广营销wordpress 外卖
  • 外贸网站优化服务做外贸怎么连接国外网站
  • 湖北省住房建设厅网站网站上传该怎么做
  • 中国制造网内贸站邯郸做网站公司哪家好
  • 装饰公司怎么做微网站wordpress群站域名
  • 桂林建设信息网站网络营销的概念是什么
  • 公司外贸网站怎么做微博营销的定义
  • 开发一个icp网站需要多少钱网站flash
  • 给企业开发网站注册公司哪个网站
  • 做任务的网站源码郑州做网站报价站域名多少钱
  • 行业做门户网站挣钱吗滨州网站建设制作
  • led 网站建设兰山做网站
  • 玉山县住房城乡建设局网站wordpress自定义内容插件
  • 贝尔利网站四川网络推广公司哪家好
  • 深圳华企网站建设福田做棋牌网站建设找哪家效益快