时间:2021-08-20 10:05:15 | 栏目:Android代码 | 点击:次
最近项目中需要实现一个GridView显示6*5=30项,并铺满整个界面,界面中还有自定义ActionBar等其他控件,所以需要获取剩下屏幕的高度。通过百度得知View有一个监听函数,亲测使用有效,特此记录,方便日后查阅。
gv_test.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { //给GridView设置Adapter,在adapter的getView中获取GridView的高度,在这个回调之前获取的高度都是0 //处理完后remove掉,至于为什么,后面有解释 gv_test.getViewTreeObserver() .removeOnGlobalLayoutListener(this); } });
通过源码追溯进去,找到ViewTreeObserver这个类,里面有很多interface,都是用来追踪View的各种状态变化的。
找到OnGlobalLayoutListener
/** * Interface definition for a callback to be invoked when the global layout state * or the visibility of views within the view tree changes. */ public interface OnGlobalLayoutListener { /** * Callback method to be invoked when the global layout state or the visibility of views * within the view tree changes */ public void onGlobalLayout(); }
注释的大概意思就是这个回调在布局状态和可见状态发生变化时回调,所以准确的说,这个不是监听View的加载完成,而是监听布局变化的。
我们来测试一下。
<?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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.example.myapplication.MainActivity"> <Button android:onClick="test" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="test"/> <TextView android:id="@+id/tv_test" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="测试"/> </LinearLayout>
public class MainActivity extends AppCompatActivity { TextView tv_test; private static final String TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv_test = (TextView)findViewById(R.id.tv_test); //app切换到后台,再点开会调用一次,屏幕关闭运行程序会调用两次 tv_test.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { Log.e(TAG, "onGlobalLayout: "); } }); } public void test(View v){ //改变可见性,调用一次 // tv_test.setVisibility(View.GONE); //改变文字布局,没有效果 // tv_test.setGravity(Gravity.CENTER); //修改控件大小,调用一次 // LinearLayout.LayoutParams para = (LinearLayout.LayoutParams) tv_test.getLayoutParams(); // para.height = 200; // para.weight = 100; // tv_test.setLayoutParams(para); //修改layoutgravity,这个是在LayoutParams中,调用一次 LinearLayout.LayoutParams para = (LinearLayout.LayoutParams) tv_test.getLayoutParams(); para.gravity = Gravity.CENTER_HORIZONTAL; tv_test.setLayoutParams(para); } }
运行程序,得到从android monitor中可以看到,启动后调用了三次onGlobalLayout,很奇怪,为什么是三次?后来有一次屏幕锁了,发现调用了两次。经过测试,app退到后台后重新进入会调用一次,屏幕锁屏后重新打开会调用两次(小米两次,努比亚1次),其中一次猜测是控件的可见性改变了。
通过按键的测试,分别修改控件的可见性和布局,都会调用一次,修改控件内部布局,不会调用,同时修改布局和可见性,只调用一次。
到此三次之谜依旧没有解决,不过,可以肯定的是,这个会重复
调用多次,使用的时候需要注意。解决的办法就是第一次回调后,就把回调remove掉,如:gv_test.getViewTreeObserver()
.removeOnGlobalLayoutListener(this);
如有错误,敬请雅正。