diff options
Diffstat (limited to 'library/main/src/com/android/setupwizardlib/TemplateLayout.java')
-rw-r--r-- | library/main/src/com/android/setupwizardlib/TemplateLayout.java | 411 |
1 files changed, 202 insertions, 209 deletions
diff --git a/library/main/src/com/android/setupwizardlib/TemplateLayout.java b/library/main/src/com/android/setupwizardlib/TemplateLayout.java index 0108880..c53e176 100644 --- a/library/main/src/com/android/setupwizardlib/TemplateLayout.java +++ b/library/main/src/com/android/setupwizardlib/TemplateLayout.java @@ -20,256 +20,249 @@ import android.annotation.TargetApi; import android.content.Context; import android.content.res.TypedArray; import android.os.Build.VERSION_CODES; +import androidx.annotation.Keep; +import androidx.annotation.LayoutRes; +import androidx.annotation.StyleRes; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.widget.FrameLayout; - -import androidx.annotation.Keep; -import androidx.annotation.LayoutRes; -import androidx.annotation.StyleRes; - import com.android.setupwizardlib.template.Mixin; import com.android.setupwizardlib.util.FallbackThemeWrapper; - import java.util.HashMap; import java.util.Map; /** - * A generic template class that inflates a template, provided in the constructor or in - * {@code android:layout} through XML, and adds its children to a "container" in the template. When + * A generic template class that inflates a template, provided in the constructor or in {@code + * android:layout} through XML, and adds its children to a "container" in the template. When * inflating this layout from XML, the {@code android:layout} and {@code suwContainer} attributes * are required. */ public class TemplateLayout extends FrameLayout { - /** - * The container of the actual content. This will be a view in the template, which child views - * will be added to when {@link #addView(View)} is called. - */ - private ViewGroup mContainer; - - private Map<Class<? extends Mixin>, Mixin> mMixins = new HashMap<>(); + /** + * The container of the actual content. This will be a view in the template, which child views + * will be added to when {@link #addView(View)} is called. + */ + private ViewGroup container; - public TemplateLayout(Context context, int template, int containerId) { - super(context); - init(template, containerId, null, R.attr.suwLayoutTheme); - } + private final Map<Class<? extends Mixin>, Mixin> mixins = new HashMap<>(); - public TemplateLayout(Context context, AttributeSet attrs) { - super(context, attrs); - init(0, 0, attrs, R.attr.suwLayoutTheme); - } + public TemplateLayout(Context context, int template, int containerId) { + super(context); + init(template, containerId, null, R.attr.suwLayoutTheme); + } - @TargetApi(VERSION_CODES.HONEYCOMB) - public TemplateLayout(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - init(0, 0, attrs, defStyleAttr); - } + public TemplateLayout(Context context, AttributeSet attrs) { + super(context, attrs); + init(0, 0, attrs, R.attr.suwLayoutTheme); + } - // All the constructors delegate to this init method. The 3-argument constructor is not - // available in LinearLayout before v11, so call super with the exact same arguments. - private void init(int template, int containerId, AttributeSet attrs, int defStyleAttr) { - final TypedArray a = getContext().obtainStyledAttributes(attrs, - R.styleable.SuwTemplateLayout, defStyleAttr, 0); - if (template == 0) { - template = a.getResourceId(R.styleable.SuwTemplateLayout_android_layout, 0); - } - if (containerId == 0) { - containerId = a.getResourceId(R.styleable.SuwTemplateLayout_suwContainer, 0); - } - inflateTemplate(template, containerId); + @TargetApi(VERSION_CODES.HONEYCOMB) + public TemplateLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(0, 0, attrs, defStyleAttr); + } - a.recycle(); + // All the constructors delegate to this init method. The 3-argument constructor is not + // available in LinearLayout before v11, so call super with the exact same arguments. + private void init(int template, int containerId, AttributeSet attrs, int defStyleAttr) { + final TypedArray a = + getContext().obtainStyledAttributes(attrs, R.styleable.SuwTemplateLayout, defStyleAttr, 0); + if (template == 0) { + template = a.getResourceId(R.styleable.SuwTemplateLayout_android_layout, 0); } - - /** - * Registers a mixin with a given class. This method should be called in the constructor. - * - * @param cls The class to register the mixin. In most cases, {@code cls} is the same as - * {@code mixin.getClass()}, but {@code cls} can also be a super class of that. In - * the latter case the the mixin must be retrieved using {@code cls} in - * {@link #getMixin(Class)}, not the subclass. - * @param mixin The mixin to be registered. - * @param <M> The class of the mixin to register. This is the same as {@code cls} - */ - protected <M extends Mixin> void registerMixin(Class<M> cls, M mixin) { - mMixins.put(cls, mixin); + if (containerId == 0) { + containerId = a.getResourceId(R.styleable.SuwTemplateLayout_suwContainer, 0); } + inflateTemplate(template, containerId); - /** - * Same as {@link android.view.View#findViewById(int)}, but may include views that are managed - * by this view but not currently added to the view hierarchy. e.g. recycler view or list view - * headers that are not currently shown. - */ - // Returning generic type is the common pattern used for findViewBy* methods - @SuppressWarnings("TypeParameterUnusedInFormals") - public <T extends View> T findManagedViewById(int id) { - return findViewById(id); - } + a.recycle(); + } - /** - * Get a {@link Mixin} from this template registered earlier in - * {@link #registerMixin(Class, Mixin)}. - * - * @param cls The class marker of Mixin being requested. The actual Mixin returned may be a - * subclass of this marker. Note that this must be the same class as registered in - * {@link #registerMixin(Class, Mixin)}, which is not necessarily the - * same as the concrete class of the instance returned by this method. - * @param <M> The type of the class marker. - * @return The mixin marked by {@code cls}, or null if the template does not have a matching - * mixin. - */ - @SuppressWarnings("unchecked") - public <M extends Mixin> M getMixin(Class<M> cls) { - return (M) mMixins.get(cls); - } + /** + * Registers a mixin with a given class. This method should be called in the constructor. + * + * @param cls The class to register the mixin. In most cases, {@code cls} is the same as {@code + * mixin.getClass()}, but {@code cls} can also be a super class of that. In the latter case + * the mixin must be retrieved using {@code cls} in {@link #getMixin(Class)}, not the + * subclass. + * @param mixin The mixin to be registered. + * @param <M> The class of the mixin to register. This is the same as {@code cls} + */ + protected <M extends Mixin> void registerMixin(Class<M> cls, M mixin) { + mixins.put(cls, mixin); + } - @Override - public void addView(View child, int index, ViewGroup.LayoutParams params) { - mContainer.addView(child, index, params); - } + /** + * Same as {@link android.view.View#findViewById(int)}, but may include views that are managed by + * this view but not currently added to the view hierarchy. e.g. recycler view or list view + * headers that are not currently shown. + */ + // Returning generic type is the common pattern used for findViewBy* methods + @SuppressWarnings("TypeParameterUnusedInFormals") + public <T extends View> T findManagedViewById(int id) { + return findViewById(id); + } - private void addViewInternal(View child) { - super.addView(child, -1, generateDefaultLayoutParams()); - } + /** + * Get a {@link Mixin} from this template registered earlier in {@link #registerMixin(Class, + * Mixin)}. + * + * @param cls The class marker of Mixin being requested. The actual Mixin returned may be a + * subclass of this marker. Note that this must be the same class as registered in {@link + * #registerMixin(Class, Mixin)}, which is not necessarily the same as the concrete class of + * the instance returned by this method. + * @param <M> The type of the class marker. + * @return The mixin marked by {@code cls}, or null if the template does not have a matching + * mixin. + */ + @SuppressWarnings("unchecked") + public <M extends Mixin> M getMixin(Class<M> cls) { + return (M) mixins.get(cls); + } - private void inflateTemplate(int templateResource, int containerId) { - final LayoutInflater inflater = LayoutInflater.from(getContext()); - final View templateRoot = onInflateTemplate(inflater, templateResource); - addViewInternal(templateRoot); + @Override + public void addView(View child, int index, ViewGroup.LayoutParams params) { + container.addView(child, index, params); + } - mContainer = findContainer(containerId); - if (mContainer == null) { - throw new IllegalArgumentException("Container cannot be null in TemplateLayout"); - } - onTemplateInflated(); - } + private void addViewInternal(View child) { + super.addView(child, -1, generateDefaultLayoutParams()); + } - /** - * This method inflates the template. Subclasses can override this method to customize the - * template inflation, or change to a different default template. The root of the inflated - * layout should be returned, and not added to the view hierarchy. - * - * @param inflater A LayoutInflater to inflate the template. - * @param template The resource ID of the template to be inflated, or 0 if no template is - * specified. - * @return Root of the inflated layout. - */ - protected View onInflateTemplate(LayoutInflater inflater, @LayoutRes int template) { - return inflateTemplate(inflater, 0, template); - } + private void inflateTemplate(int templateResource, int containerId) { + final LayoutInflater inflater = LayoutInflater.from(getContext()); + final View templateRoot = onInflateTemplate(inflater, templateResource); + addViewInternal(templateRoot); - /** - * Inflate the template using the given inflater and theme. The fallback theme will be applied - * to the theme without overriding the values already defined in the theme, but simply providing - * default values for values which have not been defined. This allows templates to add - * additional required theme attributes without breaking existing clients. - * - * <p>In general, clients should still set the activity theme to the corresponding theme in - * setup wizard lib, so that the content area gets the correct styles as well. - * - * @param inflater A LayoutInflater to inflate the template. - * @param fallbackTheme A fallback theme to apply to the template. If the values defined in the - * fallback theme is already defined in the original theme, the value in - * the original theme takes precedence. - * @param template The layout template to be inflated. - * @return Root of the inflated layout. - * - * @see FallbackThemeWrapper - */ - protected final View inflateTemplate(LayoutInflater inflater, @StyleRes int fallbackTheme, - @LayoutRes int template) { - if (template == 0) { - throw new IllegalArgumentException("android:layout not specified for TemplateLayout"); - } - if (fallbackTheme != 0) { - inflater = LayoutInflater.from( - new FallbackThemeWrapper(inflater.getContext(), fallbackTheme)); - } - return inflater.inflate(template, this, false); + container = findContainer(containerId); + if (container == null) { + throw new IllegalArgumentException("Container cannot be null in TemplateLayout"); } + onTemplateInflated(); + } - protected ViewGroup findContainer(int containerId) { - if (containerId == 0) { - // Maintain compatibility with the deprecated way of specifying container ID. - containerId = getContainerId(); - } - return (ViewGroup) findViewById(containerId); + /** + * Inflate the template using the given inflater and theme. The fallback theme will be applied to + * the theme without overriding the values already defined in the theme, but simply providing + * default values for values which have not been defined. This allows templates to add additional + * required theme attributes without breaking existing clients. + * + * <p>In general, clients should still set the activity theme to the corresponding theme in setup + * wizard lib, so that the content area gets the correct styles as well. + * + * @param inflater A LayoutInflater to inflate the template. + * @param fallbackTheme A fallback theme to apply to the template. If the values defined in the + * fallback theme is already defined in the original theme, the value in the original theme + * takes precedence. + * @param template The layout template to be inflated. + * @return Root of the inflated layout. + * @see FallbackThemeWrapper + */ + protected final View inflateTemplate( + LayoutInflater inflater, @StyleRes int fallbackTheme, @LayoutRes int template) { + if (template == 0) { + throw new IllegalArgumentException("android:layout not specified for TemplateLayout"); } - - /** - * This is called after the template has been inflated and added to the view hierarchy. - * Subclasses can implement this method to modify the template as necessary, such as caching - * views retrieved from findViewById, or other view operations that need to be done in code. - * You can think of this as {@link View#onFinishInflate()} but for inflation of the - * template instead of for child views. - */ - protected void onTemplateInflated() { + if (fallbackTheme != 0) { + inflater = + LayoutInflater.from(new FallbackThemeWrapper(inflater.getContext(), fallbackTheme)); } + return inflater.inflate(template, this, false); + } + + /** + * This method inflates the template. Subclasses can override this method to customize the + * template inflation, or change to a different default template. The root of the inflated layout + * should be returned, and not added to the view hierarchy. + * + * @param inflater A LayoutInflater to inflate the template. + * @param template The resource ID of the template to be inflated, or 0 if no template is + * specified. + * @return Root of the inflated layout. + */ + protected View onInflateTemplate(LayoutInflater inflater, @LayoutRes int template) { + return inflateTemplate(inflater, 0, template); + } - /** - * @return ID of the default container for this layout. This will be used to find the container - * ViewGroup, which all children views of this layout will be placed in. - * @deprecated Override {@link #findContainer(int)} instead. - */ - @Deprecated - protected int getContainerId() { - return 0; + protected ViewGroup findContainer(int containerId) { + if (containerId == 0) { + // Maintain compatibility with the deprecated way of specifying container ID. + containerId = getContainerId(); } + return (ViewGroup) findViewById(containerId); + } - /* Animator support */ + /** + * This is called after the template has been inflated and added to the view hierarchy. Subclasses + * can implement this method to modify the template as necessary, such as caching views retrieved + * from findViewById, or other view operations that need to be done in code. You can think of this + * as {@link View#onFinishInflate()} but for inflation of the template instead of for child views. + */ + protected void onTemplateInflated() {} - private float mXFraction; - private ViewTreeObserver.OnPreDrawListener mPreDrawListener; + /** + * @return ID of the default container for this layout. This will be used to find the container + * ViewGroup, which all children views of this layout will be placed in. + * @deprecated Override {@link #findContainer(int)} instead. + */ + @Deprecated + protected int getContainerId() { + return 0; + } - /** - * Set the X translation as a fraction of the width of this view. Make sure this method is not - * stripped out by proguard when using this with {@link android.animation.ObjectAnimator}. You - * may need to add - * <code> - * -keep @androidx.annotation.Keep class * - * </code> - * to your proguard configuration if you are seeing mysterious {@link NoSuchMethodError} at - * runtime. - */ - @Keep - @TargetApi(VERSION_CODES.HONEYCOMB) - public void setXFraction(float fraction) { - mXFraction = fraction; - final int width = getWidth(); - if (width != 0) { - setTranslationX(width * fraction); - } else { - // If we haven't done a layout pass yet, wait for one and then set the fraction before - // the draw occurs using an OnPreDrawListener. Don't call translationX until we know - // getWidth() has a reliable, non-zero value or else we will see the fragment flicker on - // screen. - if (mPreDrawListener == null) { - mPreDrawListener = new ViewTreeObserver.OnPreDrawListener() { - @Override - public boolean onPreDraw() { - getViewTreeObserver().removeOnPreDrawListener(mPreDrawListener); - setXFraction(mXFraction); - return true; - } - }; - getViewTreeObserver().addOnPreDrawListener(mPreDrawListener); - } - } - } + /* Animator support */ + + private float xFraction; + private ViewTreeObserver.OnPreDrawListener preDrawListener; - /** - * Return the X translation as a fraction of the width, as previously set in - * {@link #setXFraction(float)}. - * - * @see #setXFraction(float) - */ - @Keep - @TargetApi(VERSION_CODES.HONEYCOMB) - public float getXFraction() { - return mXFraction; + /** + * Set the X translation as a fraction of the width of this view. Make sure this method is not + * stripped out by proguard when using this with {@link android.animation.ObjectAnimator}. You may + * need to add <code> + * -keep @androidx.annotation.Keep class * + * </code> to your proguard configuration if you are seeing mysterious {@link NoSuchMethodError} + * at runtime. + */ + @Keep + @TargetApi(VERSION_CODES.HONEYCOMB) + public void setXFraction(float fraction) { + xFraction = fraction; + final int width = getWidth(); + if (width != 0) { + setTranslationX(width * fraction); + } else { + // If we haven't done a layout pass yet, wait for one and then set the fraction before + // the draw occurs using an OnPreDrawListener. Don't call translationX until we know + // getWidth() has a reliable, non-zero value or else we will see the fragment flicker on + // screen. + if (preDrawListener == null) { + preDrawListener = + new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + getViewTreeObserver().removeOnPreDrawListener(preDrawListener); + setXFraction(xFraction); + return true; + } + }; + getViewTreeObserver().addOnPreDrawListener(preDrawListener); + } } + } + + /** + * Return the X translation as a fraction of the width, as previously set in {@link + * #setXFraction(float)}. + * + * @see #setXFraction(float) + */ + @Keep + @TargetApi(VERSION_CODES.HONEYCOMB) + public float getXFraction() { + return xFraction; + } } |