Last active
September 13, 2022 05:53
-
-
Save grandstaish/8ef494348f4c3358031f to your computer and use it in GitHub Desktop.
LayoutInflaterFactory examples
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package nz.bradcampbell.app.presentation; | |
import android.content.Context; | |
import android.support.v4.view.LayoutInflaterFactory; | |
import android.util.AttributeSet; | |
import android.view.View; | |
import java.lang.reflect.Constructor; | |
import java.lang.reflect.InvocationTargetException; | |
import java.util.HashMap; | |
import java.util.HashSet; | |
import java.util.Map; | |
import java.util.Set; | |
/** | |
* This layout inflater inflates all custom views in the "nz.bradcampbell.app.presentation.customviews" | |
* package so that you don't need to use fully qualified classnames in the XML layout files. | |
*/ | |
public class CustomViewsLayoutInflaterFactory implements LayoutInflaterFactory { | |
private static final String CUSTOM_VIEWS_PACKAGE = "nz.bradcampbell.app.presentation.customviews."; | |
private static final Set<String> namesVisited = new HashSet<>(); | |
private static final Map<String, Constructor<? extends View>> constructorMap = new HashMap<>(); | |
private static final Class<?>[] constructorSignature = new Class[] { | |
Context.class, AttributeSet.class }; | |
private AppCompatActivity appCompatActivity; | |
public CustomViewsLayoutInflaterFactory(AppCompatActivity appCompatActivity) { | |
this.appCompatActivity = appCompatActivity; | |
} | |
@Override | |
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) { | |
View result = null; | |
if (!namesVisited.contains(name)) { | |
namesVisited.add(name); | |
try { | |
// Class not found in the cache, see if it's real, and try to add it | |
Class<? extends View> clazz = context.getClassLoader() | |
.loadClass(CUSTOM_VIEWS_PACKAGE + name).asSubclass(View.class); | |
constructorMap.put(name, clazz.getConstructor(constructorSignature)); | |
} catch (ClassNotFoundException e) { | |
// This will get picked up by the system later. No need to handle here. | |
} catch (NoSuchMethodException e) { | |
// This is expected and will happen for any custom view that doesn't have | |
// the custom constructor required | |
} | |
} | |
Constructor<? extends View> constructor = constructorMap.get(name); | |
if (constructor != null) { | |
try { | |
constructor.setAccessible(true); | |
result = constructor.newInstance(context, attrs); | |
} catch (InvocationTargetException e) { | |
throw new RuntimeException(e); | |
} catch (InstantiationException e) { | |
throw new RuntimeException(e); | |
} catch (IllegalAccessException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
if (result == null) { | |
// Try to let the Activity handle it (inflating fragments from XML) | |
result = appCompatActivity.onCreateView(name, context, attrs); | |
} | |
if (result == null) { | |
// Get themed views from app compat | |
result = appCompatActivity.getDelegate().createView(parent, name, context, attrs); | |
} | |
return result; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* An example of using cloneInContext | |
*/ | |
public class MainActivityCloneInContext extends AppCompatActivity { | |
private LayoutInflater layoutInflater; | |
@Override protected void onCreate(Bundle savedInstanceState) { | |
super.onCreate(savedInstanceState); | |
layoutInflater = getLayoutInflater().cloneInContext(this); | |
LayoutInflaterCompat.setFactory(layoutInflater, new CustomViewsLayoutInflaterFactory()); | |
setContentView(R.layout.activity_main); | |
} | |
@NonNull @Override public LayoutInflater getLayoutInflater() { | |
return layoutInflater; | |
} | |
@Override public Object getSystemService(@NonNull String name) { | |
if (name.equals(LAYOUT_INFLATER_SERVICE)) { | |
if (layoutInflater == null) { | |
layoutInflater = (LayoutInflater) super.getSystemService(name); | |
} | |
return layoutInflater; | |
} | |
return super.getSystemService(name); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package nz.bradcampbell.app.presentation; | |
import android.content.Context; | |
import android.support.v4.view.LayoutInflaterFactory; | |
import android.util.AttributeSet; | |
import android.view.View; | |
import java.lang.reflect.Constructor; | |
import java.lang.reflect.InvocationTargetException; | |
import java.util.HashMap; | |
import java.util.HashSet; | |
import java.util.Map; | |
import java.util.Set; | |
import static nz.bradcampbell.app.util.Strings.startsWith; | |
/** | |
* This layout inflater will inflate any view with the constructor signature as follows: | |
* <init>(Context, AttributeSet, MainActivityComponent) | |
*/ | |
public class MainActivityComponentViewFactory implements LayoutInflaterFactory { | |
private static final String PRESENTATION_PACKAGE = "nz.bradcampbell.app.presentation"; | |
private static final Set<String> namesVisited = new HashSet<>(); | |
private static final Map<String, Constructor<? extends View>> constructorMap = new HashMap<>(); | |
private static final Class<?>[] constructorSignature = new Class[] { | |
Context.class, AttributeSet.class, MainActivityComponent.class }; | |
private MainActivityComponent component; | |
public MainActivityComponentViewFactory(MainActivityComponent component) { | |
this.component = component; | |
} | |
@Override | |
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) { | |
View result = null; | |
if (startsWith(name, PRESENTATION_PACKAGE)) { | |
if (!namesVisited.contains(name)) { | |
namesVisited.add(name); | |
try { | |
// Class not found in the cache, see if it's real, and try to add it | |
Class<? extends View> clazz = context.getClassLoader() | |
.loadClass(name).asSubclass(View.class); | |
constructorMap.put(name, clazz.getConstructor(constructorSignature)); | |
} catch (ClassNotFoundException e) { | |
// This will get picked up by the system later. No need to handle here. | |
} catch (NoSuchMethodException e) { | |
// This is expected and will happen for any custom view that doesn't have | |
// the custom constructor required | |
} | |
} | |
Constructor<? extends View> constructor = constructorMap.get(name); | |
if (constructor != null) { | |
try { | |
constructor.setAccessible(true); | |
result = constructor.newInstance(context, attrs, component); | |
} catch (InvocationTargetException e) { | |
throw new RuntimeException(e); | |
} catch (InstantiationException e) { | |
throw new RuntimeException(e); | |
} catch (IllegalAccessException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
} | |
if (result == null) { | |
// Try to let the Activity handle it (inflating fragments from XML) | |
result = component.getActivity().onCreateView(name, context, attrs); | |
} | |
if (result == null) { | |
// Get themed views from app compat | |
result = component.getAppCompatDelegate().createView(parent, name, context, attrs); | |
} | |
return result; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* An example of using the Activity as the factory | |
*/ | |
public class MainActivityNoFactory extends AppCompatActivity { | |
@Override protected void onCreate(Bundle savedInstanceState) { | |
super.onCreate(savedInstanceState); | |
setContentView(R.layout.activity_main); | |
} | |
@Override | |
public View onCreateView(View parent, String name, @NonNull Context context, @NonNull AttributeSet attrs) { | |
View result = super.onCreateView(parent, name, context, attrs); | |
// Custom view inflation here | |
if (result == null) { | |
if (TextUtils.equals(name, "CustomView")) { | |
result = new CustomView(context, attrs); | |
} | |
} | |
return result; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* This is just sample code that shows how you can avoid reflection for inflating certain views, | |
* perhaps in performance critical situations. | |
*/ | |
public class MyLayoutInflaterFactory implements LayoutInflaterFactory { | |
private AppCompatActivity appCompatActivity; | |
public MyLayoutInflaterFactory(AppCompatActivity appCompatActivity) { | |
this.appCompatActivity = appCompatActivity; | |
} | |
@Override | |
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) { | |
View result = null; | |
if (TextUtils.equals(name, "DebugDrawerLayout")) { | |
result = new DebugDrawerLayout(context, attrs); | |
} else if (TextUtils.equals(name, "ScrimInsetsFrameLayout") { | |
result = new ScrimInsetsFrameLayout(context, attrs); | |
} | |
// and so on... | |
if (result == null) { | |
// Try to let the Activity handle it (inflating fragments from XML) | |
result = appCompatActivity.onCreateView(name, context, attrs); | |
} | |
if (result == null) { | |
// Get themed views from app compat | |
result = appCompatActivity.getDelegate().createView(parent, name, context, attrs); | |
} | |
return result; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
good job! learn a lot, thanks