LayoutInflaterCompat.java revision fa79d6fc148a0642f240377b8ce82acfee7bb890
1fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes/* 2fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes * Copyright (C) 2014 The Android Open Source Project 3fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes * 4fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes * Licensed under the Apache License, Version 2.0 (the "License"); 5fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes * you may not use this file except in compliance with the License. 6fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes * You may obtain a copy of the License at 7fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes * 8fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes * http://www.apache.org/licenses/LICENSE-2.0 9fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes * 10fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes * Unless required by applicable law or agreed to in writing, software 11fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes * distributed under the License is distributed on an "AS IS" BASIS, 12fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes * See the License for the specific language governing permissions and 14fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes * limitations under the License. 15fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes */ 16fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes 17fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banespackage android.support.v4.view; 18fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes 194bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikasimport android.content.Context; 20fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banesimport android.os.Build; 214bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikasimport android.support.annotation.NonNull; 224bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikasimport android.support.annotation.RequiresApi; 234bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikasimport android.util.AttributeSet; 244bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikasimport android.util.Log; 25fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banesimport android.view.LayoutInflater; 264bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikasimport android.view.View; 274bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas 284bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikasimport java.lang.reflect.Field; 29fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes 30fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes/** 314bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas * Helper for accessing features in {@link LayoutInflater} 32fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes * introduced after API level 4 in a backwards compatible fashion. 33fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes */ 34c5847d13e40f5d52459f5c0dab32dc08f1a9a683Chris Banespublic final class LayoutInflaterCompat { 354bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas private static final String TAG = "LayoutInflaterCompatHC"; 36fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes 374bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas private static Field sLayoutInflaterFactory2Field; 384bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas private static boolean sCheckedField; 394bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas 404bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas @SuppressWarnings("deprecation") 414bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas static class Factory2Wrapper implements LayoutInflater.Factory2 { 424bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas final LayoutInflaterFactory mDelegateFactory; 434bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas 444bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas Factory2Wrapper(LayoutInflaterFactory delegateFactory) { 454bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas mDelegateFactory = delegateFactory; 464bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas } 47fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes 48fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes @Override 494bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas public View onCreateView(String name, Context context, AttributeSet attrs) { 504bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas return mDelegateFactory.onCreateView(null, name, context, attrs); 51fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes } 5256594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki 5356594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki @Override 544bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas public View onCreateView(View parent, String name, Context context, 554bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas AttributeSet attributeSet) { 564bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas return mDelegateFactory.onCreateView(parent, name, context, attributeSet); 574bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas } 584bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas 59fa79d6fc148a0642f240377b8ce82acfee7bb890Aurimas Liutikas @Override 604bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas public String toString() { 614bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas return getClass().getName() + "{" + mDelegateFactory + "}"; 6256594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki } 63fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes } 64fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes 654bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas /** 664bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas * For APIs < 21, there was a framework bug that prevented a LayoutInflater's 674bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas * Factory2 from being merged properly if set after a cloneInContext from a LayoutInflater 684bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas * that already had a Factory2 registered. We work around that bug here. If we can't we 694bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas * log an error. 704bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas */ 714bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas static void forceSetFactory2(LayoutInflater inflater, LayoutInflater.Factory2 factory) { 724bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas if (!sCheckedField) { 734bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas try { 744bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas sLayoutInflaterFactory2Field = LayoutInflater.class.getDeclaredField("mFactory2"); 754bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas sLayoutInflaterFactory2Field.setAccessible(true); 764bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas } catch (NoSuchFieldException e) { 774bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas Log.e(TAG, "forceSetFactory2 Could not find field 'mFactory2' on class " 784bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas + LayoutInflater.class.getName() 794bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas + "; inflation may have unexpected results.", e); 804bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas } 814bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas sCheckedField = true; 824bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas } 834bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas if (sLayoutInflaterFactory2Field != null) { 844bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas try { 854bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas sLayoutInflaterFactory2Field.set(inflater, factory); 864bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas } catch (IllegalAccessException e) { 874bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas Log.e(TAG, "forceSetFactory2 could not set the Factory2 on LayoutInflater " 884bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas + inflater + "; inflation may have unexpected results.", e); 894bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas } 90fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes } 91fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes } 92fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes 934bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas static class LayoutInflaterCompatBaseImpl { 944bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas @SuppressWarnings("deprecation") 954bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas public void setFactory(LayoutInflater inflater, LayoutInflaterFactory factory) { 964bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas final LayoutInflater.Factory2 factory2 = factory != null 974bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas ? new Factory2Wrapper(factory) : null; 984bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas setFactory2(inflater, factory2); 994bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas } 1004bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas 1014bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas public void setFactory2(LayoutInflater inflater, LayoutInflater.Factory2 factory) { 1024bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas inflater.setFactory2(factory); 1034bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas 1044bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas final LayoutInflater.Factory f = inflater.getFactory(); 1054bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas if (f instanceof LayoutInflater.Factory2) { 1064bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas // The merged factory is now set to getFactory(), but not getFactory2() (pre-v21). 1074bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas // We will now try and force set the merged factory to mFactory2 1084bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas forceSetFactory2(inflater, (LayoutInflater.Factory2) f); 1094bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas } else { 1104bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas // Else, we will force set the original wrapped Factory2 1114bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas forceSetFactory2(inflater, factory); 1124bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas } 1134bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas } 1144bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas 1154bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas @SuppressWarnings("deprecation") 1164bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas public LayoutInflaterFactory getFactory(LayoutInflater inflater) { 1174bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas LayoutInflater.Factory factory = inflater.getFactory(); 1184bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas if (factory instanceof Factory2Wrapper) { 1194bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas return ((Factory2Wrapper) factory).mDelegateFactory; 1204bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas } 1214bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas return null; 1224bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas } 1234bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas } 1244bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas 1254bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas @RequiresApi(21) 1264bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas static class LayoutInflaterCompatApi21Impl extends LayoutInflaterCompatBaseImpl { 1274bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas @SuppressWarnings("deprecation") 1284bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas @Override 1294bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas public void setFactory(LayoutInflater inflater, LayoutInflaterFactory factory) { 1304bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas inflater.setFactory2(factory != null ? new Factory2Wrapper(factory) : null); 1314bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas } 1324bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas 13310850534c9e2949857133437cf1cd9af82721ab1Adam Powell @Override 1344bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas public void setFactory2(LayoutInflater inflater, LayoutInflater.Factory2 factory) { 1354bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas inflater.setFactory2(factory); 13610850534c9e2949857133437cf1cd9af82721ab1Adam Powell } 13710850534c9e2949857133437cf1cd9af82721ab1Adam Powell } 13810850534c9e2949857133437cf1cd9af82721ab1Adam Powell 1394bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas static final LayoutInflaterCompatBaseImpl IMPL; 140fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes static { 141c69882cb9b130902c1554ef5d3e3b06d776cd796Alan Viverette if (Build.VERSION.SDK_INT >= 21) { 1424bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas IMPL = new LayoutInflaterCompatApi21Impl(); 143fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes } else { 1444bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas IMPL = new LayoutInflaterCompatBaseImpl(); 145fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes } 146fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes } 147fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes 148fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes /* 149fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes * Hide the constructor. 150fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes */ 151fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes private LayoutInflaterCompat() { 152fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes } 153fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes 154fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes /** 155fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes * Attach a custom Factory interface for creating views while using 156fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes * this LayoutInflater. This must not be null, and can only be set once; 157fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes * after setting, you can not change the factory. 158fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes * 159fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes * @see LayoutInflater#setFactory(android.view.LayoutInflater.Factory) 1604bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas * 1614bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas * @deprecated Use {@link #setFactory2(LayoutInflater, LayoutInflater.Factory2)} instead to set 1624bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas * and {@link LayoutInflater#getFactory2()} to get the factory. 163fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes */ 1644bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas @Deprecated 1654bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas public static void setFactory( 1664bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas @NonNull LayoutInflater inflater, @NonNull LayoutInflaterFactory factory) { 167fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes IMPL.setFactory(inflater, factory); 168fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes } 169fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes 17056594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki /** 1714bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas * Attach a custom {@link LayoutInflater.Factory2} for creating views while using 1724bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas * this {@link LayoutInflater}. This must not be null, and can only be set once; 1734bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas * after setting, you can not change the factory. 1744bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas * 1754bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas * @see LayoutInflater#setFactory2(android.view.LayoutInflater.Factory2) 1764bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas */ 1774bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas public static void setFactory2( 1784bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas @NonNull LayoutInflater inflater, @NonNull LayoutInflater.Factory2 factory) { 1794bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas IMPL.setFactory2(inflater, factory); 1804bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas } 1814bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas 1824bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas /** 18356594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki * Return the current {@link LayoutInflaterFactory} (or null). This is 18456594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki * called on each element name. If the factory returns a View, add that 18556594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki * to the hierarchy. If it returns null, proceed to call onCreateView(name). 18656594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki * 18756594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki * @return The {@link LayoutInflaterFactory} associated with the 18856594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki * {@link LayoutInflater}. Will be {@code null} if the inflater does not 18956594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki * have a {@link LayoutInflaterFactory} but a raw {@link LayoutInflater.Factory}. 19056594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki * @see LayoutInflater#getFactory() 1914bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas * 1924bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas * @deprecated Use {@link #setFactory2(LayoutInflater, LayoutInflater.Factory2)} to set and 1934bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas * {@link LayoutInflater#getFactory2()} to get the factory. 19456594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki */ 1954bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas @Deprecated 19656594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki public static LayoutInflaterFactory getFactory(LayoutInflater inflater) { 19756594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki return IMPL.getFactory(inflater); 19856594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki } 199fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes} 200