package net.avenwu.support.util; import android.annotation.TargetApi; import android.content.Context; import android.os.Build; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import org.xmlpull.v1.XmlPullParser; import java.lang.reflect.Field; import java.lang.reflect.Method; /** * modified from CalligraphyLayoutInflater(Created by chris on 19/12/2013) * <p/> * Created by chaobin on 10/8/15. */ public class TypefaceLayoutInflator extends LayoutInflater { public TypefaceLayoutInflator(Context context) { super(context); } public TypefaceLayoutInflator(LayoutInflater original, Context newContext) { super(original, newContext); } @Override public LayoutInflater cloneInContext(Context newContext) { return new TypefaceLayoutInflator(this, newContext); } @Override protected View onCreateView(View parent, String name, AttributeSet attrs) throws ClassNotFoundException { View view = super.onCreateView(parent, name, attrs); onViewCreatedInternal(view, getContext(), attrs); return view; } @Override protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException { View view = super.onCreateView(name, attrs); onViewCreatedInternal(view, getContext(), attrs); return view; } @Override public void setFactory2(Factory2 factory) { // Only set our factory and wrap calls to the Factory2 trying to be set! if (!(factory instanceof WrapperFactory2)) { // LayoutInflaterCompat.setFactory(this, new WrapperFactory2(factory2, mCalligraphyFactory)); super.setFactory2(new WrapperFactory2(factory)); } else { super.setFactory2(factory); } } @Override public void setFactory(LayoutInflater.Factory factory) { // Only set our factory and wrap calls to the Factory trying to be set! if (!(factory instanceof WrapperFactory)) { super.setFactory(new WrapperFactory(factory, this)); } else { super.setFactory(factory); } } @Override public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) { setPrivateFactoryInternal(); return super.inflate(parser, root, attachToRoot); } private boolean mSetPrivateFactory = false; private void setPrivateFactoryInternal() { // Already tried to set the factory. if (mSetPrivateFactory) return; // Reflection (Or Old Device) skip. // if (!CalligraphyConfig.get().isReflection()) return; // Skip if not attached to an activity. if (!(getContext() instanceof Factory2)) { mSetPrivateFactory = true; return; } final Method setPrivateFactoryMethod = ReflectionUtils .getMethod(LayoutInflater.class, "setPrivateFactory"); if (setPrivateFactoryMethod != null) { ReflectionUtils.invokeMethod(this, setPrivateFactoryMethod, new PrivateWrapperFactory2((Factory2) getContext(), this)); } mSetPrivateFactory = true; } private static class WrapperFactory implements LayoutInflater.Factory { private final Factory mFactory; private final TypefaceLayoutInflator mInflater; public WrapperFactory(Factory factory, TypefaceLayoutInflator inflator) { mFactory = factory; mInflater = inflator; } @Override public View onCreateView(String name, Context context, AttributeSet attrs) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { try { View view = mInflater.createCustomViewInternal( null, mFactory.onCreateView(name, context, attrs), name, context, attrs); onViewCreatedInternal(view, context, attrs); return view; } catch (Exception e) { e.printStackTrace(); } } View view = mFactory.onCreateView(name, context, attrs); onViewCreatedInternal(view, context, attrs); return view; } } static void onViewCreatedInternal(View view, final Context context, AttributeSet attrs) { if (view instanceof TextView) { TypefaceUtils.setTypeface(context, (TextView) view, "fonts/RobotoCondensed-Regular.ttf"); } } /** * Nasty method to inflate custom layouts that haven't been handled else where. If this fails it * will fall back through to the PhoneLayoutInflater method of inflating custom views where * Calligraphy will NOT have a hook into. * * @param parent parent view * @param view view if it has been inflated by this point, if this is not null this method * just returns this value. * @param name name of the thing to inflate. * @param viewContext Context to inflate by if parent is null * @param attrs Attr for this view which we can steal fontPath from too. * @return view or the View we inflate in here. */ private View createCustomViewInternal(View parent, View view, String name, Context viewContext, AttributeSet attrs) throws Exception { // I by no means advise anyone to do this normally, but Google have locked down access to // the createView() method, so we never get a callback with attributes at the end of the // createViewFromTag chain (which would solve all this unnecessary rubbish). // We at the very least try to optimise this as much as possible. // We only call for customViews (As they are the ones that never go through onCreateView(...)). // We also maintain the Field reference and make it accessible which will make a pretty // significant difference to performance on Android 4.0+. // If CustomViewCreation is off skip this. if (view == null && name.indexOf('.') > -1) { if (mConstructorArgs == null) mConstructorArgs = ReflectionUtils.getField(LayoutInflater.class, "mConstructorArgs"); final Object[] mConstructorArgsArr = (Object[]) ReflectionUtils.getValue(mConstructorArgs, this); final Object lastContext = mConstructorArgsArr[0]; // The LayoutInflater actually finds out the correct context to use. We just need to set // it on the mConstructor for the internal method. // Set the constructor ars up for the createView, not sure why we can't pass these in. mConstructorArgsArr[0] = viewContext; ReflectionUtils.setValue(mConstructorArgs, this, mConstructorArgsArr); try { view = createView(name, null, attrs); } catch (ClassNotFoundException ignored) { } finally { mConstructorArgsArr[0] = lastContext; ReflectionUtils.setValue(mConstructorArgs, this, mConstructorArgsArr); } } return view; } private Field mConstructorArgs = null; @TargetApi(11) private static class WrapperFactory2 implements Factory2 { protected final Factory2 mFactory2; public WrapperFactory2(Factory2 factory2) { mFactory2 = factory2; } @Override public View onCreateView(String name, Context context, AttributeSet attrs) { View view = mFactory2.onCreateView(name, context, attrs); onViewCreatedInternal(view, context, attrs); return view; } @Override public View onCreateView(View parent, String name, Context context, AttributeSet attrs) { View view = mFactory2.onCreateView(parent, name, context, attrs); onViewCreatedInternal(view, context, attrs); return view; } } @TargetApi(11) private static class PrivateWrapperFactory2 extends WrapperFactory2 { private final TypefaceLayoutInflator mInflater; public PrivateWrapperFactory2(Factory2 factory2, TypefaceLayoutInflator inflater) { super(factory2); mInflater = inflater; } @Override public View onCreateView(View parent, String name, Context context, AttributeSet attrs) { try { View view = mInflater.createCustomViewInternal(parent, mFactory2.onCreateView(parent, name, context, attrs), name, context, attrs ); onViewCreatedInternal(view, context, attrs); return view; } catch (Exception e) { e.printStackTrace(); } return super.onCreateView(parent, name, context, attrs); } } }