Android 에서 Layout Inflater 를 사용 할 때 주의해 야 할 구덩이 들

7747 단어 androidlayoutinflater
머리말
평소에 개발 하 는 과정 에서 우 리 는 Layout Inflater 라 는 종 류 를 자주 사용한다.예 를 들 어 Fragment$onCreateViewRecyclerView.Adapter$onCreateViewHolder 에서 모두 사용한다.그것 의 용법 도 LayoutInflater.inflate(resourceId, root, attachToRoot) 일 뿐 첫 번 째 매개 변 수 는 할 말 이 없 지만 두 번 째 매개 변수 와 세 번 째 매개 변 수 를 결합 하면 어느 정도 현혹 성 을 가 져 올 것 이다.그 전에 가끔 인터페이스 구조 에 문제 가 생 긴 것 을 발견 할 수 있 습 니 다.한참 을 조사 한 후에 우연히 이 두 개의 매개 변 수 를 바 꾸 었 습 니 다.문 제 를 해결 한 후에 지나 갔습니다.이것 이 왜 그런 지 생각 하지 않 았 습 니 다.그리고 다음 에 이런 곤경 을 반복 할 수 있 습 니 다.
그래서 여기 서 정리 해서 나중에 계속 구덩이 에 빠 지지 않도록 하려 고 합 니 다.
먼저 inflate 방법의 주석 을 보십시오.

/**
 * Inflate a new view hierarchy from the specified xml resource. Throws
 * {@link InflateException} if there is an error.
 * 
 * @param resource ID for an XML layout resource to load (e.g.,
 *  <code>R.layout.main_page</code>)
 * @param root Optional view to be the parent of the generated hierarchy (if
 *  <em>attachToRoot</em> is true), or else simply an object that
 *  provides a set of LayoutParams values for root of the returned
 *  hierarchy (if <em>attachToRoot</em> is false.)
 * @param attachToRoot Whether the inflated hierarchy should be attached to
 *  the root parameter? If false, root is only used to create the
 *  correct subclass of LayoutParams for the root view in the XML.
 * @return The root View of the inflated hierarchy. If root was supplied and
 *   attachToRoot is true, this is root; otherwise it is the root of
 *   the inflated XML file.
 */
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)
먼저 알 아야 할 것 은 하나의 View 의 측정 결 과 는 자신의 layot 만 있 는 것 이 아니 라 는 것 입 니 다.width 와 layotheight(즉 LayoutParams)는 부모 용기 가 주 는 제약(MeasureSpec)과 자신의 LayoutParams 가 공동으로 결정 합 니 다.
이 공감 대 를 형성 한 후에 우 리 는 그것 의 인 자 를 다시 한 번 보 자.
  • root:레이아웃 파일 에 부모 용 기 를 제공 합 니 다.레이아웃 파일 에는 부모 용기 가 없 는 요소 가 있 기 때문에 측정 작업 을 수행 할 수 있 도록 부모 용 기 를 제공 해 야 합 니 다.루트 가 비어 있 으 면 루트 요소 의 layotxxx 가 모두 효력 을 상실 하여 전체 레이아웃 에 영향 을 줍 니 다.또한 루트 가 비어 있 으 면 attachToRoot 도 의미 가 없다.
  • attachToRoot:true 라면 만 든 레이아웃 시스템 은 부모 용기 에 추가 할 수 있 습 니 다.false 를 위해 서 는 이 레이아웃 이 측정 등 작업 을 순조롭게 끝 낼 수 있 도록 제약 을 제공 할 뿐 입 니 다.부모 용기 에 레이아웃 을 추가 하려 면 필요 에 따라 addView 방법 을 수 동 으로 호출 해 야 합 니 다.
  • 반환 값:root != null && attachToRoot 이면 돌아 오 는 View 는 들 어 오 는 root 입 니 다.그렇지 않 으 면 레이아웃 파일 로 만 든 View 대상 을 되 돌려 줍 니 다.
  • 몇 가지 예 를 들 어 설명 하 는 것 이 비교적 이해 하기 쉽다.Activity 의 레이아웃 은 LinearLayout 입 니 다.추가 할 레이아웃 은 다음 과 같 습 니 다.
    
    <?xml version="1.0" encoding="utf-8"?>
    <TextView xmlns:android="http://schemas.android.com/apk/res/android"
      android:id="@+id/item_text"
      android:layout_width="200dp"
      android:layout_height="50dp"
      android:layout_marginLeft="16dp"
      android:layout_marginTop="16dp"
      android:background="@color/colorAccent"
      android:gravity="center"
      android:text="item: text"/>
    정상 적 인 상황
    
    //      
    View inflatedView = LayoutInflater.from(this).inflate(R.layout.item_text, mLinearLayout, true);
    Log.d(TAG, "inflated view is " + inflatedView);
    
    //      
    View inflatedView = LayoutInflater.from(this).inflate(R.layout.item_text, mLinearLayout, false);
    Log.d(TAG, "inflated view is " + inflatedView);
    mLinearLayout.addView(inflatedView);
    시각 적 인 결 과 는 모두 같다.

    그러나 Log 는 조금 다 릅 니 다.이것 이 바로 attachToRoot 의 서로 다른 값 으로 인 한 것 입 니 다.
    첫 번 째 방법의 Log
    
    D/MainActivity: inflated view is android.widget.LinearLayout{36e9aac V.E...... ......I. 0,0-0,0 #7f0c0051 app:id/linear}
    두 번 째 방법의 Log
    
    D/MainActivity: inflated view is android.support.v7.widget.AppCompatTextView{3c9d37b V.ED..... ......ID 0,0-0,0 #7f0c0054 app:id/item_text}
    또 주의해 야 할 점 은 첫 번 째 방법 에 mLinearLayout.addView(inflatedView) 을 더 하면 오류 가 발생 할 수 있다 는 것 이다.IllegalStateException: The specified child already has a parent....   。
    두 번 째 방법 이 이 말 이 없 으 면 화면 에 아무것도 보이 지 않 는 다.
    루트 가 null 인 경우
    
    mLinearLayout = (LinearLayout) findViewById(R.id.linear);
    View inflatedView = LayoutInflater.from(this).inflate(R.layout.item_text, null);
    Log.d(TAG, "inflated view is " + inflatedView);
    mLinearLayout.addView(inflatedView);

    이 때 레이아웃 파일 을 보십시오:
    
    <?xml version="1.0" encoding="utf-8"?>
    <TextView xmlns:android="http://schemas.android.com/apk/res/android"
      android:id="@+id/item_text"
      android:layout_width="200dp"
      android:layout_height="50dp"
      android:layout_marginLeft="16dp"
      android:layout_marginTop="16dp"
      android:background="@color/colorAccent"
      android:gravity="center"
      android:text="item: text"/>
    어렵 지 않 게 모든 layotxxx 의 속성 이 모두 효력 을 잃 었 습 니 다.
    RecyclerView 의 Inflater
    위 에서 말 했 듯 이 레이아웃 을 만 들 때 루트 에 레이아웃 을 추가 하고 두 가지 방법 이 있 습 니 다.그러나 우 리 는 onCreate View Holder 에 레이아웃 을 추가 할 때 이렇게 썼 습 니 다.
    
    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
     View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_text, parent, false);
     return new MyViewHolder(view);
    }
    세 번 째 매개 변수 가 true 를 전달 하면 잘못 보고 할 수 있 습 니 다.왜 일 까요?
    
    java.lang.IllegalStateException: The specified child already has a parent.
    직관 적 으로 보면 서브 뷰 의 추가 와 삭 제 는 RecyclerView 가 관리 하 는 것 이 므 로 우리 가 추가 할 필요 가 없다 는 것 이다.그래도 RecyclerView 코드 로 이해 하 는 게 좋 을 것 같 아 요.
    LinearLayoutManager 의 경우 RecyclerView 는 하위 View 를 만 들 때 LinearLayoutManager$layoutChunk 방법 으로 호출 됩 니 다.
    
    void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
      LayoutState layoutState, LayoutChunkResult result) {
     //        Adapter$onCreateViewHolder
     View view = layoutState.next(recycler);
     if (view == null) {
      if (DEBUG && layoutState.mScrapList == null) {
       throw new RuntimeException("received null view when unexpected");
      }
      // if we are laying out views in scrap, this may return null which means there is
      // no more items to layout.
      result.mFinished = true;
      return;
     }
     LayoutParams params = (LayoutParams) view.getLayoutParams();
     if (layoutState.mScrapList == null) {
      if (mShouldReverseLayout == (layoutState.mLayoutDirection
        == LayoutState.LAYOUT_START)) {
       addView(view);
      } else {
       addView(view, 0);
      }
     } else {
      if (mShouldReverseLayout == (layoutState.mLayoutDirection
        == LayoutState.LAYOUT_START)) {
       addDisappearingView(view);
      } else {
       addDisappearingView(view, 0);
      }
     }
    
     //          
    }
    초기 화 할 때 View view = layoutState.next(recycler) 에서 우리 가 익숙 한 onCreateViewHolder 방법 으로 호출 됩 니 다.그리고 우 리 는 안에서 inflate 를 하 는 과정 에서 세 번 째 매개 변 수 는 true 를 전달 하고 서브 View 를 RecyclerView 에 추가 합 니 다.그러나 View 를 얻 은 후 addView 로 호출 되 었 습 니 다.
    총결산
    이상 은 이 글 의 전체 내용 입 니 다.본 논문 의 내용 이 여러분 의 안 드 로 이 드 개발 자 들 에 게 어느 정도 도움 이 되 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 댓 글 을 남 겨 주 셔 서 저희 에 대한 지지 에 감 사 드 립 니 다.

    좋은 웹페이지 즐겨찾기