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

 前言

平时我们在使用被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":

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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=".activity.InputSoftDemoActivity">

    <View
        android:id="@+id/view_1"
        android:layout_width="100dp"
        android:layout_height="200dp"
        android:background="@color/ali_feedback_red"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <View
        android:id="@+id/view_2"
        android:layout_width="100dp"
        android:layout_height="200dp"
        android:background="@color/ali_feedback_black"
        app:layout_constraintLeft_toRightOf="@id/view_1"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.constraintlayout.widget.Group
        android:id="@+id/group_1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="gone"
        app:constraint_referenced_ids="view_1,view_2"
        tools:ignore="MissingConstraints" />

</androidx.constraintlayout.widget.ConstraintLayout>

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

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

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

我们先看一下Log打印:

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

==A==view_1.getVisibility()===  

和

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

然后我们又调用了

view_1.setVisibility(View.VISIBLE);

此时

==C==view_1.getVisibility()=== 

打印的结果是true,感觉也没问题。

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

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

 public void setVisibility(int visibility) {
   super.setVisibility(visibility);
   this.applyLayoutFeatures();
}

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

先看第一行:

 super.setVisibility(visibility);

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

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

再看第二行:

this.applyLayoutFeatures();

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

protected void applyLayoutFeatures() {
    ViewParent parent = this.getParent();
    if (parent != null && parent instanceof ConstraintLayout) {
         this.applyLayoutFeatures((ConstraintLayout)parent);
   }

}
 protected void applyLayoutFeatures(ConstraintLayout container) {
        //这里是获取Group的可见性,因为上面调用了super.setVisibility,所以这里获取到的可见性,也就是咱们外部调用Group的setVisibility时传进来的值。
        int visibility = this.getVisibility();
        float elevation = 0.0F;
        if (VERSION.SDK_INT >= 21) {
            elevation = this.getElevation();
        }

        for(int i = 0; i < this.mCount; ++i) {
            int id = this.mIds[i];
            View view = container.getViewById(id);
            if (view != null) {
                view.setVisibility(visibility);
                if (elevation > 0.0F && VERSION.SDK_INT >= 21) {
                    view.setTranslationZ(view.getTranslationZ() + elevation);
                }
            }
        }

}

关键在于这里:

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

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

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

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

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

view_1.setVisibility(View.VISIBLE);

而且

==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的可见性:

看下log打印:

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

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

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

package com.example.mytestapplication.custom;

import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewParent;

import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.Group;

import com.example.mytestapplication.R;

import java.lang.reflect.Field;

/**
 * @ClassName :CustomGroup
 * @Description :
 * @Author : SheYi
 * @Date :2021/6/25  12:00 下午
 */
public class CustomGroup extends Group {

    public CustomGroup(Context context) {
        this(context, null);
    }

    public CustomGroup(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CustomGroup(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public void setVisibility(int visibility) {
        setReferenceViewsVisibility(visibility);
    }

    /**
     * 设置constraint_referenced_ids数组中的view的可见性
     *
     * @param visibility
     */
    private void setReferenceViewsVisibility(int visibility) {
        ViewParent parent = this.getParent();
        if (parent != null && parent instanceof ConstraintLayout) {
            ConstraintLayout container = (ConstraintLayout) parent;
            for (int i = 0; i < this.mCount; ++i) {
                int id = this.mIds[i];
                View view = container.getViewById(id);
                if (view != null) {
                    view.setVisibility(visibility);
                }
            }
        }
    }

    @Override
    protected void applyLayoutFeatures(ConstraintLayout container) {
        float elevation = 0.0F;
        if (Build.VERSION.SDK_INT >= 21) {
            elevation = this.getElevation();
        }

        for (int i = 0; i < this.mCount; ++i) {
            int id = this.mIds[i];
            View view = container.getViewById(id);
            if (view != null) {
                if (elevation > 0.0F && Build.VERSION.SDK_INT >= 21) {
                    view.setTranslationZ(view.getTranslationZ() + elevation);
                }
            }
        }
    }
}

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

还重写了setVisibility方法:

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

 

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

 

 

 

 

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

弊端:

 

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

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值