LayoutInflater Factory

1.Test

测试布局文件:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    
    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</FrameLayout>
1.1 当MainActivity继承普通的Activity:
public class MainActivity extends Activity {
	protected void onCreate(Bundle savedInstanceState) {
	
		super.onCreate(savedInstanceState);
		
		//为当前Activity的LayoutInflater设置Factory(或Factory2)
		LayoutInflater.from(this).setFactory((String name, Context context, AttributeSet attrs) -> {
            Log.e("TAG", "name = " + name);
            int n = attrs.getAttributeCount();
            for (int i = 0; i < n; i++) {
                Log.e("TAG", attrs.getAttributeName(i) + " , " + attrs.getAttributeValue(i));
            }
            return null;
        });
        
		//为当前Activity的LayoutInflater设置完Factory之后再去设置布局文件
        setContentView(R.layout.activity_main);
        
		//打印发现这是一个TextView类型的对象
		Log.e("Tag", findViewById(R.id.text).toString());		
	}
}

现象:
发现name打印的是控件的全名,例如:TextView,android.support.v7.widget.RecyclerView等
attrs里面保存了这个控件在xml文件定义的所有属性,例如layout_width,background,text等

1.2 当MainActivity继承AppCompatActivity:

现在的Actvity大都被AppCompatActivity取代,如果想要在AppCompatActivity中为LayoutInflater设置Factory:

public class MainActivity extends AppCompatActivity {
	protected void onCreate(Bundle savedInstanceState) {
		//onCreate会为当前Activity对应的LayoutInflater设置Factory
		super.onCreate(savedInstanceState);
		
		//为当前Activity的LayoutInflater设置Factory(或Factory2)会报错,因为LayoutInflater已经设置过Factory了
		LayoutInflater.from(this).setFactory((String name, Context context, AttributeSet attrs) -> {
          	...
            return null;
        });
        
        //为Activity的LayoutInflater设置Factory的另一种方式,setFactory已废弃,以后用setFactory2好了
	 	LayoutInflaterCompat.setFactory2(LayoutInflater.from(this), new LayoutInflater.Factory2() {
            @Override
            public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
                return null;
            }
            @Override
            public View onCreateView(String name, Context context, AttributeSet attrs) {
                return null;
            }
        });
        
        setContentView(R.layout.activity_main);
        
        //打印发现这是一只android.support.v7.widget.AppCompatTextView类型的对象
        Log.e("Tag", findViewById(R.id.text).toString());		
	}
}

现象:
抛出异常A factory has already been set on this LayoutInflater,说明AppCompatActivity已经设置过了Factory,无法重复设置
去掉setFactory的代码,检查TextView对象的类型,发现它的类型已经被替换为AppCompatTextView

1.3 如何为AppCompatActivity添加自定义的Factory:

如果想要在AppCompatActivity中为LayoutInflater添加自己的Factory,需要在onCreate之前添加:

public class MainActivity extends AppCompatActivity {
	protected void onCreate(Bundle savedInstanceState) {
		//在onCreate之前设置Factory
		LayoutInflater.from(this).setFactory((String name, Context context, AttributeSet attrs) -> {
          	...
            return null;
        });
        
        //系统设置的Factory会失效
		super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        //打印发现这是一只TextView, 说明系统为AppCompatActivity添加的Factory被我们屏蔽掉了
        Log.e("Tag", findViewById(R.id.text).toString());		
	}
}
1.4 如果想在AppCompatActivity自定义Factory,又想保留AppCompat控件的替换怎么办?

用AppCompatDelegate创建控件可以创建AppCompat控件,例如:

public class MainActivity extends AppCompatActivity {

	protected void onCreate(Bundle savedInstanceState) {
		 LayoutInflaterCompat.setFactory2(LayoutInflater.from(this), new LayoutInflater.Factory2() {
            @Override
            public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
            	 //用AppCompatDelegate创建View
                 AppCompatDelegate delegate = getDelegate();
                 view = delegate.createView(parent, name, context, attrs);
                 return view;
            }

            @Override
            public View onCreateView(String name, Context context, AttributeSet attrs) {
                return null;
            }
        });
        
		super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        //打印发现这是一只android.support.v7.widget.AppCompatTextView
        Log.e("Tag", findViewById(R.id.text).toString());		
	}
}
2.AppCompatActivity源码:
 //className = AppCompatActivity
 protected void onCreate(@Nullable Bundle savedInstanceState) {
      getDelegate().installViewFactory();
 }
 
 //className = AppCompatDelegateImplV9 
 public void installViewFactory() {
      LayoutInflater layoutInflater = LayoutInflater.from(mContext);
      if (layoutInflater.getFactory() == null) {
       	   LayoutInflaterCompat.setFactory2(layoutInflater, this);
      }
 }
 @override 	//override Factory2.createView
 public View createView(View parent, final String name, Context context, AttributeSet attrs) {
 	  return mAppCompatViewInflater.createView(parent, name, context, attrs, ...)
 }

 //className = AppCompatViewInflater
 final View createView(View parent, final String name,Context context, AttributeSet attrs,....) {
 	   switch (name) {
            case "TextView":
                view = new AppCompatTextView(context, attrs);
                verifyNotNull(view, name);
                break;
            case "ImageView":
                view = new AppCompatImageView(context, attrs);
                verifyNotNull(view, name);
                break;
            case "Button":
                view = new AppCompatButton(context, attrs);
                verifyNotNull(view, name);
                break;
            case "EditText":
                view = new AppCompatEditText(context, attrs);
                verifyNotNull(view, name);
                break;
            case ...
       }
 }

可以看到AppCompatActivity确实在onCreate方法中为当前Activity的LayoutInflater设置了Factory,并利用这个Factory自动地把许多控件替换成兼容性更强的控件;

3.LayoutInflater.Factory

先来看看LayoutInflater中的Factory:

public abstract class LayoutInflater {
 	private Factory mFactory;
    private Factory2 mFactory2;
    private Factory2 mPrivateFactory;
    private boolean mFactorySet;			//mFactory和mFactory2有一个被赋值就会置为true;
    
    public interface Factory {
        public View onCreateView(String name, Context context, AttributeSet attrs);
    }

    public interface Factory2 extends Factory {
        public View onCreateView(View parent, String name, Context context, AttributeSet attrs);
    }

	public void setFactory(@NotNull Factory factory) {
	  	if (mFactorySet) {
            throw new IllegalStateException("A factory has already been set on this LayoutInflater");
        }
        mFactorySet = true;
        if (mFactory == null) {
            mFactory = factory;
        } else {
            mFactory = new FactoryMerger(factory, null, mFactory, mFactory2);
        }
	}

	public void setFactory2(@NotNull Factory2 factory) {
		if (mFactorySet) {
            throw new IllegalStateException("A factory has already been set on this LayoutInflater");
        }
        mFactorySet = true;
        if (mFactory == null) {
            mFactory = mFactory2 = factory;
        } else {
            mFactory = mFactory2 = new FactoryMerger(factory, factory, mFactory, mFactory2);
        }
	}
}

Factory2比Factory的onCreateView方法多了一个parent参数,其他没有区别,而且Factory2可以完全替代Factory的所有功能;
Factory2和Factory只能选择一个来添加,且一个Inflater只能添加一次Factory

4.用Factory为某类型的控件统一设置属性:

例如把当前页面所有TextView的字体都统一修改:

public class MainActivity extends AppCompatActivity {

	protected void onCreate(Bundle savedInstanceState) {
		//在onCreate之前设置Factory
		LayoutInflaterCompat.setFactory2(LayoutInflater.from(this), new LayoutInflater.Factory2() {
		
            @Override
            public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
            	 View view = null;
               	 if (name.equals("TextView")) {
               	 	 //用AppCompatDelegate的方法可以创建
					 AppCompatDelegate delegate = getDelegate();
            		 View view = delegate.createView(parent, name, context, attrs);
            		 ((TextView) view).setTypeface(typeface);
            	 }
            	 //返回null将用其他方式创建View,不影响其他控件的正常创建
            	 return null;
            }

            @Override
            public View onCreateView(String name, Context context, AttributeSet attrs) {
                return null;
            }
        });
        
        //系统设置的Factory会失效
		super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);	
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值