前言
平时我们在使用被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的可见性了。需要通过代码动态控制,如果各位大佬有好的解决方案,欢迎在评论区留言,或者私信给我。