Skip to content

Commit 07cb674

Browse files
committed
Merge pull request androidannotations#976 from yDelouis/975_InjectListOfViews
Inject List of views
2 parents de742bd + 66811eb commit 07cb674

8 files changed

Lines changed: 250 additions & 14 deletions

File tree

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/**
2+
* Copyright (C) 2010-2014 eBusiness Information, Excilys Group
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* 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, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
package org.androidannotations.annotations;
17+
18+
import java.lang.annotation.ElementType;
19+
import java.lang.annotation.Retention;
20+
import java.lang.annotation.RetentionPolicy;
21+
import java.lang.annotation.Target;
22+
23+
/**
24+
* Use it on a {@link java.util.List} of {@link android.view.View} or {@link android.view.View} subtype
25+
* fields in a view related (ie {@link org.androidannotations.annotations.EActivity}, {@link org.androidannotations.annotations.EFragment},
26+
* {@link org.androidannotations.annotations.EViewGroup}, ...) annotated class.
27+
* <p/>
28+
* The annotation value should be an array of R.id.* fields.
29+
* <p/>
30+
* Your code related to injected views should go in an {@link org.androidannotations.annotations.AfterViews}
31+
* annotated method.
32+
* <p/>
33+
* <blockquote>
34+
*
35+
* Example :
36+
*
37+
* <pre>
38+
* &#064;EActivity(R.layout.main)
39+
* public class MyActivity extends Activity {
40+
*
41+
* // Injects R.id.myEditText
42+
* &#064;ViewsById({R.id.edit1, R.id.edit2})
43+
* List<EditText> myEditTexts;
44+
*
45+
* &#064;ViewsById({R.id.myTextView1, R.id.myOtherTextView})
46+
* List<TextView> textViews;
47+
*
48+
* &#064;AfterViews
49+
* void updateTextWithDate() {
50+
* for (TextView textView : textViews) {
51+
* textView.setText(&quot;Date: &quot; + new Date());
52+
* }
53+
* }
54+
* }
55+
* </pre>
56+
*
57+
* </blockquote>
58+
*
59+
* @see org.androidannotations.annotations.AfterViews
60+
*/
61+
@Retention(RetentionPolicy.CLASS)
62+
@Target(ElementType.FIELD)
63+
public @interface ViewsById {
64+
65+
int[] value() default ResId.DEFAULT_VALUE;
66+
67+
String[] resName() default "";
68+
}

‎AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/handler/AnnotationHandlers.java‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ public AnnotationHandlers(ProcessingEnvironment processingEnvironment) {
5353
add(new PrefHandler(processingEnvironment));
5454
add(new RoboGuiceHandler(processingEnvironment));
5555
add(new ViewByIdHandler(processingEnvironment));
56+
add(new ViewsByIdHandler(processingEnvironment));
5657
add(new FragmentByIdHandler(processingEnvironment));
5758
add(new FragmentByTagHandler(processingEnvironment));
5859
add(new FromHtmlHandler(processingEnvironment));
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/**
2+
* Copyright (C) 2010-2014 eBusiness Information, Excilys Group
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* 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, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
package org.androidannotations.handler;
17+
18+
import com.sun.codemodel.JClass;
19+
import com.sun.codemodel.JFieldRef;
20+
import org.androidannotations.annotations.ViewsById;
21+
import org.androidannotations.helper.*;
22+
import org.androidannotations.holder.EComponentWithViewSupportHolder;
23+
import org.androidannotations.holder.FoundViewHolder;
24+
import org.androidannotations.model.AndroidSystemServices;
25+
import org.androidannotations.model.AnnotationElements;
26+
import org.androidannotations.process.IsValid;
27+
import org.androidannotations.rclass.IRClass;
28+
29+
import javax.annotation.processing.ProcessingEnvironment;
30+
import javax.lang.model.element.Element;
31+
import javax.lang.model.element.TypeElement;
32+
import javax.lang.model.type.DeclaredType;
33+
import javax.lang.model.type.TypeMirror;
34+
import java.util.List;
35+
36+
import static com.sun.codemodel.JExpr._new;
37+
import static com.sun.codemodel.JExpr.ref;
38+
39+
public class ViewsByIdHandler extends BaseAnnotationHandler<EComponentWithViewSupportHolder> {
40+
41+
private IdAnnotationHelper helper;
42+
private APTCodeModelHelper codeModelHelper = new APTCodeModelHelper();
43+
44+
public ViewsByIdHandler(ProcessingEnvironment processingEnvironment) {
45+
super(ViewsById.class, processingEnvironment);
46+
}
47+
48+
@Override
49+
public void setAndroidEnvironment(IRClass rClass, AndroidSystemServices androidSystemServices, AndroidManifest androidManifest) {
50+
super.setAndroidEnvironment(rClass, androidSystemServices, androidManifest);
51+
helper = new IdAnnotationHelper(processingEnv, getTarget(), rClass);
52+
}
53+
54+
@Override
55+
public void validate(Element element, AnnotationElements validatedElements, IsValid valid) {
56+
validatorHelper.enclosingElementHasEnhancedViewSupportAnnotation(element, validatedElements, valid);
57+
58+
validatorHelper.isDeclaredType(element, valid);
59+
60+
validatorHelper.extendsListOfView(element, valid);
61+
62+
validatorHelper.resIdsExist(element, IRClass.Res.ID, IdValidatorHelper.FallbackStrategy.NEED_RES_ID, valid);
63+
64+
validatorHelper.isNotPrivate(element, valid);
65+
}
66+
67+
@Override
68+
public void process(Element element, EComponentWithViewSupportHolder holder) {
69+
JFieldRef elementRef = ref(element.getSimpleName().toString());
70+
71+
TypeMirror viewType = extractViewClass(element);
72+
JClass viewClass = codeModelHelper.typeMirrorToJClass(viewType, holder);
73+
74+
instantiateArrayList(elementRef, viewType, holder);
75+
clearList(elementRef, holder);
76+
77+
List<JFieldRef> idsRefs = helper.extractAnnotationFieldRefs(processHolder, element, IRClass.Res.ID, true);
78+
for (JFieldRef idRef : idsRefs) {
79+
addViewToListIfNotNull(elementRef, viewClass, idRef, holder);
80+
81+
}
82+
}
83+
84+
private void instantiateArrayList(JFieldRef elementRef, TypeMirror viewType, EComponentWithViewSupportHolder holder) {
85+
TypeElement arrayListTypeElement = helper.typeElementFromQualifiedName(CanonicalNameConstants.ARRAYLIST);
86+
DeclaredType arrayListType = helper.getTypeUtils().getDeclaredType(arrayListTypeElement, viewType);
87+
JClass arrayListClass = codeModelHelper.typeMirrorToJClass(arrayListType, holder);
88+
89+
holder.getInitBody().assign(elementRef, _new(arrayListClass));
90+
}
91+
92+
private TypeMirror extractViewClass(Element element) {
93+
DeclaredType elementType = (DeclaredType) element.asType();
94+
List<? extends TypeMirror> elementTypeArguments = elementType.getTypeArguments();
95+
96+
TypeMirror viewType = helper.typeElementFromQualifiedName(CanonicalNameConstants.VIEW).asType();
97+
if (!elementTypeArguments.isEmpty()) {
98+
viewType = elementTypeArguments.get(0);
99+
}
100+
return viewType;
101+
}
102+
103+
private void clearList(JFieldRef elementRef, EComponentWithViewSupportHolder holder) {
104+
holder.getOnViewChangedBodyBeforeFindViews().add(elementRef.invoke("clear"));
105+
}
106+
107+
private void addViewToListIfNotNull(JFieldRef elementRef, JClass viewClass, JFieldRef idRef, EComponentWithViewSupportHolder holder) {
108+
FoundViewHolder foundViewHolder = holder.getFoundViewHolder(idRef, viewClass);
109+
foundViewHolder.getIfNotNullBlock().invoke(elementRef, "add").arg(foundViewHolder.getView(viewClass));
110+
}
111+
112+
}

‎AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/ValidatorHelper.java‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,20 @@ public void extendsOrmLiteDaoWithValidModelParameter(Element element, IsValid va
475475
}
476476
}
477477

478+
public void extendsListOfView(Element element, IsValid valid) {
479+
DeclaredType elementType = (DeclaredType) element.asType();
480+
List<? extends TypeMirror> elementTypeArguments = elementType.getTypeArguments();
481+
482+
TypeMirror viewType = annotationHelper.typeElementFromQualifiedName(CanonicalNameConstants.VIEW).asType();
483+
484+
if (!elementType.toString().equals(CanonicalNameConstants.LIST)
485+
&& elementTypeArguments.size() == 1
486+
&& !annotationHelper.isSubtype(elementTypeArguments.get(0), viewType)) {
487+
valid.invalidate();
488+
annotationHelper.printAnnotationError(element, "%s can only be used on a "+CanonicalNameConstants.LIST+ " of elements extending " + CanonicalNameConstants.VIEW);
489+
}
490+
}
491+
478492
public void hasASqlLiteOpenHelperParameterizedType(Element element, IsValid valid) {
479493
TypeMirror helperType = annotationHelper.extractAnnotationParameter(element, "helper");
480494

‎AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/holder/EComponentWithViewSupportHolder.java‎

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public abstract class EComponentWithViewSupportHolder extends EComponentHolder {
3434

3535
protected ViewNotifierHelper viewNotifierHelper;
3636
private JBlock onViewChangedBody;
37+
private JBlock onViewChangedBodyBeforeFindViews;
3738
private JVar onViewChangedHasViewsParam;
3839
private Map<String, FoundViewHolder> foundViewsHolders = new HashMap<String, FoundViewHolder>();
3940
protected JMethod findNativeFragmentById;
@@ -55,6 +56,13 @@ public JBlock getOnViewChangedBody() {
5556
return onViewChangedBody;
5657
}
5758

59+
public JBlock getOnViewChangedBodyBeforeFindViews() {
60+
if (onViewChangedBodyBeforeFindViews == null) {
61+
setOnViewChanged();
62+
}
63+
return onViewChangedBodyBeforeFindViews;
64+
}
65+
5866
public JVar getOnViewChangedHasViewsParam() {
5967
if (onViewChangedHasViewsParam == null) {
6068
setOnViewChanged();
@@ -67,6 +75,7 @@ protected void setOnViewChanged() {
6775
JMethod onViewChanged = getGeneratedClass().method(PUBLIC, codeModel().VOID, "onViewChanged");
6876
onViewChanged.annotate(Override.class);
6977
onViewChangedBody = onViewChanged.body();
78+
onViewChangedBodyBeforeFindViews = onViewChangedBody.block();
7079
onViewChangedHasViewsParam = onViewChanged.param(HasViews.class, "hasViews");
7180
JClass notifierClass = refClass(OnViewChangedNotifier.class);
7281
getInitBody().staticInvoke(notifierClass, "registerOnViewChangedListener").arg(_this());
@@ -86,14 +95,13 @@ public void assignFindViewById(JFieldRef idRef, JClass viewClass, JFieldRef fiel
8695
JExpression assignExpression;
8796

8897
if (foundViewHolder != null) {
89-
assignExpression = foundViewHolder.getView();
98+
assignExpression = foundViewHolder.getView(viewClass);
9099
} else {
91100
assignExpression = findViewById(idRef);
92-
foundViewsHolders.put(idRefString, new FoundViewHolder(fieldRef, block));
93-
}
94-
95-
if (viewClass != null && viewClass != classes().VIEW) {
96-
assignExpression = cast(viewClass, assignExpression);
101+
if (viewClass != null && viewClass != classes().VIEW) {
102+
assignExpression = cast(viewClass, assignExpression);
103+
}
104+
foundViewsHolders.put(idRefString, new FoundViewHolder(this, viewClass, fieldRef, block));
97105
}
98106

99107
block.assign(fieldRef, assignExpression);
@@ -120,7 +128,7 @@ protected FoundViewHolder createFoundViewAndIfNotNullBlock(JFieldRef idRef, JCla
120128
}
121129

122130
JVar view = block.decl(viewClass, "view", findViewExpression);
123-
return new FoundViewHolder(view, block);
131+
return new FoundViewHolder(this, viewClass, view, block);
124132
}
125133

126134
public JMethod getFindNativeFragmentById() {

‎AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/holder/FoundViewHolder.java‎

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,23 @@
1616
package org.androidannotations.holder;
1717

1818
import com.sun.codemodel.JBlock;
19+
import com.sun.codemodel.JClass;
1920
import com.sun.codemodel.JExpression;
2021

2122
import static com.sun.codemodel.JExpr._null;
23+
import static com.sun.codemodel.JExpr.cast;
2224

2325
public class FoundViewHolder {
2426

27+
private GeneratedClassHolder holder;
28+
private JClass viewClass;
2529
private JExpression view;
2630
private JBlock ifNotNullBlock;
2731
private boolean ifNotNullCreated = false;
2832

29-
public FoundViewHolder(JExpression view, JBlock block) {
33+
public FoundViewHolder(GeneratedClassHolder holder, JClass viewClass, JExpression view, JBlock block) {
34+
this.holder = holder;
35+
this.viewClass = viewClass;
3036
this.view = view;
3137
this.ifNotNullBlock = block;
3238
}
@@ -35,6 +41,14 @@ public JExpression getView() {
3541
return view;
3642
}
3743

44+
public JExpression getView(JClass viewClass) {
45+
if (this.viewClass.equals(viewClass) || holder.classes().VIEW.equals(viewClass)) {
46+
return view;
47+
} else {
48+
return cast(viewClass, view);
49+
}
50+
}
51+
3852
public JBlock getIfNotNullBlock() {
3953
if (!ifNotNullCreated) {
4054
ifNotNullBlock = ifNotNullBlock._if(view.ne(_null()))._then();

‎AndroidAnnotations/functional-test-1-5-tests/src/test/java/org/androidannotations/test15/ViewsInjectedActivityTest.java‎

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,14 @@
1616
package org.androidannotations.test15;
1717

1818
//import static org.fest.assertions.Assertions.assertThat;
19-
import static org.androidannotations.test15.MyAssertions.assertThat;
20-
2119
import org.fest.assertions.Assertions;
2220
import org.junit.Before;
2321
import org.junit.Test;
2422
import org.junit.runner.RunWith;
2523

24+
import static org.androidannotations.test15.MyAssertions.assertThat;
25+
26+
2627
@RunWith(AndroidAnnotationsTestRunner.class)
2728
public class ViewsInjectedActivityTest {
2829

@@ -61,4 +62,14 @@ public void countAfterSetContentViewCalls() {
6162
Assertions.assertThat(activity.counter).isEqualTo(2);
6263
}
6364

65+
@Test
66+
public void listOfViewAreInjected() {
67+
Assertions.assertThat(activity.views).hasSize(2);
68+
}
69+
70+
@Test
71+
public void listOfTextViewAreInjected() {
72+
Assertions.assertThat(activity.textViews).hasSize(2);
73+
}
74+
6475
}

‎AndroidAnnotations/functional-test-1-5/src/main/java/org/androidannotations/test15/ViewsInjectedActivity.java‎

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,16 @@
1515
*/
1616
package org.androidannotations.test15;
1717

18-
import org.androidannotations.annotations.AfterViews;
19-
import org.androidannotations.annotations.EActivity;
20-
import org.androidannotations.annotations.ViewById;
21-
2218
import android.app.Activity;
2319
import android.view.View;
2420
import android.widget.Button;
2521
import android.widget.TextView;
22+
import org.androidannotations.annotations.AfterViews;
23+
import org.androidannotations.annotations.EActivity;
24+
import org.androidannotations.annotations.ViewById;
25+
import org.androidannotations.annotations.ViewsById;
26+
27+
import java.util.List;
2628

2729
@EActivity(R.layout.views_injected)
2830
public class ViewsInjectedActivity extends Activity {
@@ -40,6 +42,12 @@ public class ViewsInjectedActivity extends Activity {
4042
@ViewById
4143
TextView myTextView;
4244

45+
@ViewsById({R.id.my_text_view, R.id.myButton})
46+
List<View> views;
47+
48+
@ViewsById({R.id.my_text_view, R.id.someView})
49+
List<TextView> textViews;
50+
4351
@AfterViews
4452
void incrementCounter() {
4553
counter++;

0 commit comments

Comments
 (0)