需要获取自定义ViewGroup下的任意层级的已知id的子View
首先在attrs中定义没有format的attr,例如:
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="ChatView"> <attr name="chatview_ptr" /> <attr name="chatview_msg_list" /> <attr name="chatview_input" /> </declare-styleable> </resources>
然后在layout中添加子view,例如:
<imui.jiguang.cn.imuisample.views.ChatView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/chat_view" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#f3f3f3" android:orientation="vertical" app:chatview_input="@+id/chat_input" app:chatview_msg_list="@+id/msg_list" app:chatview_ptr="@+id/pull_to_refresh_layout"> {包含id为chat_input、msg_list、pull_to_refresh_layout的子view} </imui.jiguang.cn.imuisample.views.ChatView>
然后在自定义ViewGroup的java文件中获取子View的id,例如:
private void init(Context context, AttributeSet attributeSet) { TypedArray a = context.obtainStyledAttributes(attributeSet, R.styleable.ChatView); int UNSET = -1; resIdPtr = a.getResourceId(R.styleable.ChatView_chatview_ptr, UNSET); if (resIdPtr == UNSET) { resIdPtr = a.getInt(R.styleable.ChatView_chatview_ptr, UNSET); } resIdMsgList = a.getResourceId(R.styleable.ChatView_chatview_msg_list, UNSET); if (resIdMsgList == UNSET) { resIdMsgList = a.getInt(R.styleable.ChatView_chatview_msg_list, UNSET); } resChatInput = a.getResourceId(R.styleable.ChatView_chatview_input, UNSET); if (resChatInput == UNSET) { resChatInput = a.getInt(R.styleable.ChatView_chatview_input, UNSET); } a.recycle(); }
然后在onAttachedToWindow()中,通过子view 的id获取子view的对象。
有个问题是ViewGroup的onAttachedToWindow()调用的时机是在Activity的onResume,可能会导致多余的findViewById子View的初始化被调用,不是太优,所以可以将findViewById(int id)放到一个initView()方法中,再将initView()方法用public暴露出来,给Activity在onCreate中调用,而不在自身的onAttachedToWindow()中调用。
像ConstraintLayout这样需要寻找子一层的所有未知id的子View
可以参照ConstraintLayout的源码那样,定义一个列表存储子View的id
private SparseArray<View> mChildrenByIds = new SparseArray<>(); private void init(Context context, AttributeSet attributeSet) { mChildrenByIds.put(getId(), this); } @Override public void setId(int id) { mChildrenByIds.remove(getId()); super.setId(id); mChildrenByIds.put(getId(), this); } @Override public void onViewAdded(View child) { super.onViewAdded(child); mChildrenByIds.put(child.getId(), child); } @Override public void onViewRemoved(View child) { super.onViewRemoved(child); mChildrenByIds.remove(child.getId()); } public View getViewById(int id) { return mChildrenByIds.get(id); }
然后在onAttachedToWindow()中通过getViewById(int id)得到子View对象