• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.setupwizardlib;
18 
19 import android.annotation.TargetApi;
20 import android.content.Context;
21 import android.content.res.TypedArray;
22 import android.os.Build.VERSION_CODES;
23 import android.util.AttributeSet;
24 import android.view.LayoutInflater;
25 import android.view.View;
26 import android.view.ViewGroup;
27 import android.view.ViewTreeObserver;
28 import android.widget.FrameLayout;
29 
30 import com.android.setupwizardlib.annotations.Keep;
31 
32 /**
33  * A generic template class that inflates a template, provided in the constructor or in
34  * {@code android:layout} through XML, and adds its children to a "container" in the template. When
35  * inflating this layout from XML, the {@code android:layout} and {@code suwContainer} attributes
36  * are required.
37  */
38 public class TemplateLayout extends FrameLayout {
39 
40     /**
41      * The container of the actual content. This will be a view in the template, which child views
42      * will be added to when {@link #addView(View)} is called.
43      */
44     private ViewGroup mContainer;
45 
TemplateLayout(Context context, int template, int containerId)46     public TemplateLayout(Context context, int template, int containerId) {
47         super(context);
48         init(template, containerId, null, R.attr.suwLayoutTheme);
49     }
50 
TemplateLayout(Context context, AttributeSet attrs)51     public TemplateLayout(Context context, AttributeSet attrs) {
52         super(context, attrs);
53         init(0, 0, attrs, R.attr.suwLayoutTheme);
54     }
55 
56     @TargetApi(VERSION_CODES.HONEYCOMB)
TemplateLayout(Context context, AttributeSet attrs, int defStyleAttr)57     public TemplateLayout(Context context, AttributeSet attrs, int defStyleAttr) {
58         super(context, attrs, defStyleAttr);
59         init(0, 0, attrs, defStyleAttr);
60     }
61 
62     // All the constructors delegate to this init method. The 3-argument constructor is not
63     // available in LinearLayout before v11, so call super with the exact same arguments.
init(int template, int containerId, AttributeSet attrs, int defStyleAttr)64     private void init(int template, int containerId, AttributeSet attrs, int defStyleAttr) {
65         final TypedArray a = getContext().obtainStyledAttributes(attrs,
66                 R.styleable.SuwTemplateLayout, defStyleAttr, 0);
67         if (template == 0) {
68             template = a.getResourceId(R.styleable.SuwTemplateLayout_android_layout, 0);
69         }
70         if (containerId == 0) {
71             containerId = a.getResourceId(R.styleable.SuwTemplateLayout_suwContainer, 0);
72         }
73         inflateTemplate(template, containerId);
74 
75         a.recycle();
76     }
77 
78     @Override
addView(View child, int index, ViewGroup.LayoutParams params)79     public void addView(View child, int index, ViewGroup.LayoutParams params) {
80         mContainer.addView(child, index, params);
81     }
82 
addViewInternal(View child)83     private void addViewInternal(View child) {
84         super.addView(child, -1, generateDefaultLayoutParams());
85     }
86 
inflateTemplate(int templateResource, int containerId)87     private void inflateTemplate(int templateResource, int containerId) {
88         final LayoutInflater inflater = LayoutInflater.from(getContext());
89         final View templateRoot = onInflateTemplate(inflater, templateResource);
90         addViewInternal(templateRoot);
91 
92         mContainer = findContainer(containerId);
93         if (mContainer == null) {
94             throw new IllegalArgumentException("Container cannot be null in TemplateLayout");
95         }
96         onTemplateInflated();
97     }
98 
99     /**
100      * This method inflates the template. Subclasses can override this method to customize the
101      * template inflation, or change to a different default template. The root of the inflated
102      * layout should be returned, and not added to the view hierarchy.
103      *
104      * @param inflater A LayoutInflater to inflate the template.
105      * @param template The resource ID of the template to be inflated, or 0 if no template is
106      *                 specified.
107      * @return Root of the inflated layout.
108      */
onInflateTemplate(LayoutInflater inflater, int template)109     protected View onInflateTemplate(LayoutInflater inflater, int template) {
110         if (template == 0) {
111             throw new IllegalArgumentException("android:layout not specified for TemplateLayout");
112         }
113         return inflater.inflate(template, this, false);
114     }
115 
findContainer(int containerId)116     protected ViewGroup findContainer(int containerId) {
117         if (containerId == 0) {
118             // Maintain compatibility with the deprecated way of specifying container ID.
119             containerId = getContainerId();
120         }
121         return (ViewGroup) findViewById(containerId);
122     }
123 
124     /**
125      * This is called after the template has been inflated and added to the view hierarchy.
126      * Subclasses can implement this method to modify the template as necessary, such as caching
127      * views retrieved from findViewById, or other view operations that need to be done in code.
128      * You can think of this as {@link View#onFinishInflate()} but for inflation of the
129      * template instead of for child views.
130      */
onTemplateInflated()131     protected void onTemplateInflated() {
132     }
133 
134     /**
135      * @return ID of the default container for this layout. This will be used to find the container
136      * ViewGroup, which all children views of this layout will be placed in.
137      * @deprecated Override {@link #findContainer(int)} instead.
138      */
139     @Deprecated
getContainerId()140     protected int getContainerId() {
141         return 0;
142     }
143 
144     /* Animator support */
145 
146     private float mXFraction;
147     private ViewTreeObserver.OnPreDrawListener mPreDrawListener;
148 
149     /**
150      * Set the X translation as a fraction of the width of this view. Make sure this method is not
151      * stripped out by proguard when using this with {@link android.animation.ObjectAnimator}. You
152      * may need to add
153      * <code>
154      *     -keep @com.android.setupwizardlib.annotations.Keep class *
155      * </code>
156      * to your proguard configuration if you are seeing mysterious {@link NoSuchMethodError} at
157      * runtime.
158      */
159     @Keep
160     @TargetApi(VERSION_CODES.HONEYCOMB)
setXFraction(float fraction)161     public void setXFraction(float fraction) {
162         mXFraction = fraction;
163         final int width = getWidth();
164         if (width != 0) {
165             setTranslationX(width * fraction);
166         } else {
167             // If we haven't done a layout pass yet, wait for one and then set the fraction before
168             // the draw occurs using an OnPreDrawListener. Don't call translationX until we know
169             // getWidth() has a reliable, non-zero value or else we will see the fragment flicker on
170             // screen.
171             if (mPreDrawListener == null) {
172                 mPreDrawListener = new ViewTreeObserver.OnPreDrawListener() {
173                     @Override
174                     public boolean onPreDraw() {
175                         getViewTreeObserver().removeOnPreDrawListener(mPreDrawListener);
176                         setXFraction(mXFraction);
177                         return true;
178                     }
179                 };
180                 getViewTreeObserver().addOnPreDrawListener(mPreDrawListener);
181             }
182         }
183     }
184 
185     /**
186      * Return the X translation as a fraction of the width, as previously set in
187      * {@link #setXFraction(float)}.
188      *
189      * @see #setXFraction(float)
190      */
191     @Keep
192     @TargetApi(VERSION_CODES.HONEYCOMB)
getXFraction()193     public float getXFraction() {
194         return mXFraction;
195     }
196 }
197