hong's android

[안드로이드] Appcompatactivity 본문

Android/Android

[안드로이드] Appcompatactivity

_hong 2023. 2. 14. 17:43

Support Library,  Android x

두 라이브러리의 공통점은 하위 api 버전에 대해서도 호환성을 유지시켜준다.

Support Library는 v4, v7,v13 은 api 버전이 해당 숫자 이상이 되어야 한다는 의미이며, 대부분 min 19를 사용하면서 해당 버전을 명시하는 것은 의미가 없어졌다.  버전이 혼잡해질수록 호환성 문제가 발생했고 또한 Support library는 단일 라이브러리이므로 불 필요한 라이브러리도 포함되어 dex파일의 크기가 증가한다.

 

다양한 버전을 통합하고 자체적으로 관리하는 새로운 네임스페이스 Api 레벨 28부터 androidx를 구성.

 

Appacompatactivity 란?

특정 버전 이하 기기에서 뷰 호환성을 유지한다.

 

어떻게 Appcompatactivity는 뷰 호환성을 유지할까?

AppCompatDelegate의 구체 클래스에서 AppCompateViewInflater를 생성한다.  

 

// AppCompatDelegateImpl.java

class AppCompatDelegateImpl extends AppCompatDelegate
        implements MenuBuilder.Callback, LayoutInflater.Factory2 {
  @Override
  public View createView(View parent, final String name, @NonNull Context context,
      @NonNull AttributeSet attrs) {
    if (mAppCompatViewInflater == null) {
      TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
      
      // ViewInflater를 사용할 Class명을 취득
      String viewInflaterClassName =
          a.getString(R.styleable.AppCompatTheme_viewInflaterClass);
      
      // Theme에서 정의된 ViewInflater가 있다면 해당 View Inflater가 사용되며
      // 미정의한 경우 기본으로 AppCompatViewInflater를 사용
      if (viewInflaterClassName == null) {
        mAppCompatViewInflater = new AppCompatViewInflater();
      } else {
        try {
          Class<?> viewInflaterClass = Class.forName(viewInflaterClassName);
          mAppCompatViewInflater = 
              (AppCompatViewInflater) viewInflaterClass.getDeclaredConstructor()
                    .newInstance();
        } catch (Throwable t) {
          Log.i(TAG, "Failed to instantiate custom view inflater "
                + viewInflaterClassName + ". Falling back to default.", t);
          mAppCompatViewInflater = new AppCompatViewInflater();
        }
      }
    }    
    ...    
    return mAppCompatViewInflater.createView(parent, name, context, attrs, inheritContext,
            IS_PRE_LOLLIPOP, /* Only read android:theme pre-L (L+ handles this anyway) */
            true, /* Read read app:theme as a fallback at all times for legacy reasons */
            VectorEnabledTintResources.shouldBeUsed() /* Only tint wrap the context if enabled */
    );
  }
}

 

AppCompateViewInflater 내부에선 뷰 호환성이 필요한 뷰들을 AppCompat View로 대체된다.

 

Theme에서 정의된 ViewInflater가 있다면 해당 View Inflater가 사용되며, 해당 인플레이터  없다면 appcompatinflater가 사용된다. AppCompateViewInflater는 뷰의 이름에 따라 appcompat view로 대체한다.

<resources>
  <style name="Base.V14.Theme.MaterialComponents" parent="Base.V14.Theme.MaterialComponents.Bridge">
    <item name="viewInflaterClass">com.google.android.material.theme.MaterialComponentsViewInflater</item>
    ...
  </style>
  <style name="Base.V14.Theme.MaterialComponents.Light" parent="Base.V14.Theme.MaterialComponents.Light.Bridge">
    <item name="viewInflaterClass">com.google.android.material.theme.MaterialComponentsViewInflater</item>
    ...
  </style>
</resources>

<resources>
  <style name="Base.V14.Theme.MaterialComponents.Dialog" parent="Base.V14.Theme.MaterialComponents.Dialog.Bridge">
    <item name="viewInflaterClass">com.google.android.material.theme.MaterialComponentsViewInflater</item>
    ...
  </style>
  <style name="Base.V14.Theme.MaterialComponents.Light.Dialog" parent="Base.V14.Theme.MaterialComponents.Light.Dialog.Bridge">
    <item name="viewInflaterClass">com.google.android.material.theme.MaterialComponentsViewInflater</item>
      ...
  </style>
</resources>

 

만약 material 테마를 사용할 경우는 Appcompatinflater를 상속한 MaterialComponentsViewInflater에서 뷰의 이름에 따라 해당 materal 테마의 뷰로 변경된다.

 

Custom LayoutInflater

 

기존 코드를 변경하지않고 커스텀 LayoutInflater를 이용해서 기존의 뷰들을 원하는 커스텀 뷰로 변경 가능

 

class CustomViewInflater: AppCompatViewInflater() {
        
    override fun createView(context: Context?, name: String?, attrs: AttributeSet?): View? {
        return when(name) {
            "RecyclerView" -> CustomRecyclerView(context, attrs)
            //...
            else -> super.createView(context, name, attrs)
        }
    }

    override fun createTextView(context: Context?, attrs: AttributeSet?): AppCompatTextView {
        return CustomTextView(context, attrs)
    }
}
<style name="AppTheme" parent="Base.AppTheme">
<item name="viewInflaterClass">my.app.package.CustomViewInflater</item>
</style>

 

 

 

Reference.

1. https://medium.com/@hxlich/when-to-use-appcompat-views-to-write-custom-views-f5ca00df8d82

2. https://pluu.github.io/blog/android/2021/02/27/compatibility-sdk-library/