自定义Group,解决Group setVisibility后,子View再次设置setVisibility无效的问题

水深无声 2023-10-03 09:58 107阅读 0赞

前言

平时我们在使用被ConstraintLayout包裹的Group时,会遇到这样一个问题:设置Group的 app:constraint_referenced_ids=”view_1,view_2” ,然后设置Group的可见性为View.GONE,那么此时再次设置view_1或者view_2的可见性,为Visible。那么此时看到的现象是view_1或view_2依然不可见。

正文

我们一步一步来看下这个操作:

1、首先在xml文件里面创建两个View,然后使用Group给Group设置app:constraint_referenced_ids=”view_1,view_2”:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res-auto"
  4. xmlns:tools="http://schemas.android.com/tools"
  5. android:layout_width="match_parent"
  6. android:layout_height="match_parent"
  7. tools:context=".activity.InputSoftDemoActivity">
  8. <View
  9. android:id="@+id/view_1"
  10. android:layout_width="100dp"
  11. android:layout_height="200dp"
  12. android:background="@color/ali_feedback_red"
  13. app:layout_constraintLeft_toLeftOf="parent"
  14. app:layout_constraintTop_toTopOf="parent" />
  15. <View
  16. android:id="@+id/view_2"
  17. android:layout_width="100dp"
  18. android:layout_height="200dp"
  19. android:background="@color/ali_feedback_black"
  20. app:layout_constraintLeft_toRightOf="@id/view_1"
  21. app:layout_constraintTop_toTopOf="parent" />
  22. <androidx.constraintlayout.widget.Group
  23. android:id="@+id/group_1"
  24. android:layout_width="wrap_content"
  25. android:layout_height="wrap_content"
  26. android:visibility="gone"
  27. app:constraint_referenced_ids="view_1,view_2"
  28. tools:ignore="MissingConstraints" />
  29. </androidx.constraintlayout.widget.ConstraintLayout>

2、在代码中设置Group可见性为View.GONE,view_1的可见性为Visible,我们看下现象:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTAyNTc5MzE_size_16_color_FFFFFF_t_70

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTAyNTc5MzE_size_16_color_FFFFFF_t_70 1

啥都没有,怎么个情况?明明设置了view_1可见,再看一眼布局里面是否给view_1设置了背景:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTAyNTc5MzE_size_16_color_FFFFFF_t_70 2

确实是有背景色的,那怎么没显示出来?

我们先看一下Log打印:

20210703105917901.png

由于Group设置了View.GONE,那么

  1. ==A==view_1.getVisibility()===
  2. ==B==view_2.getVisibility()===
  3. 打印的结果为false,这没问题。

然后我们又调用了

  1. view_1.setVisibility(View.VISIBLE);

此时

  1. ==C==view_1.getVisibility()===
  2. 打印的结果是true,感觉也没问题。

那么为什么view_1,还是没显示出来呢?

这时候,我们看一下Group的setVisibility的源码:

  1. public void setVisibility(int visibility) {
  2. super.setVisibility(visibility);
  3. this.applyLayoutFeatures();
  4. }

通过源码我们可以看到,里面很简单,就两行代码:

先看第一行:

  1. super.setVisibility(visibility);

这个感觉没啥可讲的,由于Group本身继承自ConstraintHelper,ConstraintHelper继承自View。

这里相当于调用View的setVisibility方法,设置了Group自己本身的visibility属性的值

再看第二行:

  1. this.applyLayoutFeatures();

这个实际是调用了Group的父类ConstraintHelper里面的applyLayoutFeatures方法:

  1. protected void applyLayoutFeatures() {
  2. ViewParent parent = this.getParent();
  3. if (parent != null && parent instanceof ConstraintLayout) {
  4. this.applyLayoutFeatures((ConstraintLayout)parent);
  5. }
  6. }
  7. protected void applyLayoutFeatures(ConstraintLayout container) {
  8. //这里是获取Group的可见性,因为上面调用了super.setVisibility,所以这里获取到的可见性,也就是咱们外部调用Group的setVisibility时传进来的值。
  9. int visibility = this.getVisibility();
  10. float elevation = 0.0F;
  11. if (VERSION.SDK_INT >= 21) {
  12. elevation = this.getElevation();
  13. }
  14. for(int i = 0; i < this.mCount; ++i) {
  15. int id = this.mIds[i];
  16. View view = container.getViewById(id);
  17. if (view != null) {
  18. view.setVisibility(visibility);
  19. if (elevation > 0.0F && VERSION.SDK_INT >= 21) {
  20. view.setTranslationZ(view.getTranslationZ() + elevation);
  21. }
  22. }
  23. }
  24. }

关键在于这里:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTAyNTc5MzE_size_16_color_FFFFFF_t_70 3

这里实际上相当于,获取到Group的 app:constraint_referenced_ids 引用的view数组,并且遍历这个数组,把这个数组中的每一个View的可见性都设置为Group的可见性一样的值。

所以我们在调用Group的setVisibility的方法时,能够控制app:constraint_referenced_ids 引用的view的可见性。

看到这里我们仿佛明白了,原来我们在外面设置的

  1. group_1.setVisibility(View.GONE);其实也是给view_1设置了View.GONE

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTAyNTc5MzE_size_16_color_FFFFFF_t_70 4

但是有个疑问,你这里虽然是给view_1设置了View.GONE了。可是我在下面又手动调了一次

  1. view_1.setVisibility(View.VISIBLE);
  2. 而且
  3. ==C==view_1.getVisibility()=== 的打印结果是true

那不就是说,我又设置了view_1可见了吗,那为什么view_1还是没有显示出来?

。。。

又陷入了沉思。。。

别着急:

我们借鉴一下大佬的这篇文章:https://blog.csdn.net/SummerCloudXT/article/details/100602713

于是我们知道了:

Group在使用后,会对它所管理的所有view的显示进行重新赋值,重新设置它所管理的ID显示效果。当我们设置为Gone的时候,ConstraintLayout会重新布局,会重新调用这个方法,而我们设置的值会被Group的拦截掉,所以就没有效了。

回到我们的布局里,当view_1由View.GONE状态被我们手动设置为View.VISIBLE状态时,ConstraintLayout会重新布局,也就会重新调用这个 applyLayoutFeatures 方法。然后重新获取Group的可见性,设置给view_1。而Group的可见性没有变还是View.GONE,所以又把View.GONE设置给了view_1。所以导致view_1还是不可见。

我们来验证一下上面的结论是不是正确的:

我们延迟1秒钟,再次打印一下view_1的可见性:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTAyNTc5MzE_size_16_color_FFFFFF_t_70 5

看下log打印:

20210703114147484.png

果然,view_1又变为了不可见。

好的,经过以上分析,问题我们找到了,接下来就是怎么解决问题了。

1、重写一个类CustomGroup继承自Group:

  1. package com.example.mytestapplication.custom;
  2. import android.content.Context;
  3. import android.content.res.TypedArray;
  4. import android.os.Build;
  5. import android.os.Handler;
  6. import android.os.Looper;
  7. import android.util.AttributeSet;
  8. import android.view.View;
  9. import android.view.ViewParent;
  10. import androidx.constraintlayout.widget.ConstraintLayout;
  11. import androidx.constraintlayout.widget.Group;
  12. import com.example.mytestapplication.R;
  13. import java.lang.reflect.Field;
  14. /**
  15. * @ClassName :CustomGroup
  16. * @Description :
  17. * @Author : SheYi
  18. * @Date :2021/6/25 12:00 下午
  19. */
  20. public class CustomGroup extends Group {
  21. public CustomGroup(Context context) {
  22. this(context, null);
  23. }
  24. public CustomGroup(Context context, AttributeSet attrs) {
  25. this(context, attrs, 0);
  26. }
  27. public CustomGroup(Context context, AttributeSet attrs, int defStyleAttr) {
  28. super(context, attrs, defStyleAttr);
  29. }
  30. @Override
  31. public void setVisibility(int visibility) {
  32. setReferenceViewsVisibility(visibility);
  33. }
  34. /**
  35. * 设置constraint_referenced_ids数组中的view的可见性
  36. *
  37. * @param visibility
  38. */
  39. private void setReferenceViewsVisibility(int visibility) {
  40. ViewParent parent = this.getParent();
  41. if (parent != null && parent instanceof ConstraintLayout) {
  42. ConstraintLayout container = (ConstraintLayout) parent;
  43. for (int i = 0; i < this.mCount; ++i) {
  44. int id = this.mIds[i];
  45. View view = container.getViewById(id);
  46. if (view != null) {
  47. view.setVisibility(visibility);
  48. }
  49. }
  50. }
  51. }
  52. @Override
  53. protected void applyLayoutFeatures(ConstraintLayout container) {
  54. float elevation = 0.0F;
  55. if (Build.VERSION.SDK_INT >= 21) {
  56. elevation = this.getElevation();
  57. }
  58. for (int i = 0; i < this.mCount; ++i) {
  59. int id = this.mIds[i];
  60. View view = container.getViewById(id);
  61. if (view != null) {
  62. if (elevation > 0.0F && Build.VERSION.SDK_INT >= 21) {
  63. view.setTranslationZ(view.getTranslationZ() + elevation);
  64. }
  65. }
  66. }
  67. }
  68. }

在CustomGroup中重写applyLayoutFeatures方法,主要把里面的这一行代码的逻辑去掉:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTAyNTc5MzE_size_16_color_FFFFFF_t_70 6

还重写了setVisibility方法:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTAyNTc5MzE_size_16_color_FFFFFF_t_70 7

经过重写applyLayoutFeatures方法和setVisibility方法后,view_1设置为View.VISIBLE后,重走applyLayoutFeatures,也不会改变view_1的visible属性了。只有在手动调用Group的setVisibility方法时才会设置view_1的visible属性。

接下来,我们看下改变后的运行结果:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTAyNTc5MzE_size_16_color_FFFFFF_t_70 8

20210711171111348.png

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTAyNTc5MzE_size_16_color_FFFFFF_t_70 9

首先我们设置了Group的Visibility为View.GONE ,然后设置了view_1为View.VISIBLE。结果可以看到view_1确实变为可见了。设置view_2同样道理,这里就不多说了。好了,到这里一个设置完自己的可见性后,又可以设置引用的View可见性的Group就介绍完了。

弊端:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTAyNTc5MzE_size_16_color_FFFFFF_t_70 10

经过以上测试,在xml设置Group的可见性,已经不能控制通过constraint_referenced_ids属个性引用的view的可见性了。需要通过代码动态控制,如果各位大佬有好的解决方案,欢迎在评论区留言,或者私信给我。

发表评论

表情:
评论列表 (有 0 条评论,107人围观)

还没有评论,来说两句吧...

相关阅读