Say goodbye to using InjectView and findViewById

Android's findViewById is really annoying. It's a template-like method that needs to be written in each Activity, Fragment, and Adapter. Declare that View and findView are always separated by an unknown line spacing; after setOnClickListener, always look for where the corresponding onClick method is.

Can't Android intelligently bind the View in the layout to the corresponding field?

The answer is: Android itself does not support it, but we can achieve the goal through some hacks.

Reflection based InjectView

findViewById accepts an int id parameter to find the corresponding View. Through annotation, we can put this int type id on the corresponding field declared, through Java reflection, traverse each field, find the corresponding id, and then complete the assignment to the field.

The following is the specific method:

Statement Annotation

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface InjectView {
    int value();
}

 

Test Annotation

@InjectView(R.id.aView) View aView;

 Annotation is an identifier that identifies aView as a field that InjectView cares about.

to inject

Field[] declaredFields = getClass().getDeclaredFields();
for (Field field : declaredFields) {
    InjectView annotation = field.getAnnotation(InjectView.class);
    if (annotation != null) {
        int id = annotation.value();
        View view = findViewById(id);
        field.setAccessible(true);
        try {
            field.set(this, view);
        } catch (IllegalAccessException e) {
            e.printStackTrace ();
        }
    }
}

 

After setContentView, we check whether all fields contain the @InjectView flag, and assign values ​​to the fields containing the @InjectView flag.

In the same way, we can even omit setContentView and implement it by injection. Suppose we have an @InjectLayout Annotation, we use this Annotation to identify the id corresponding to setContentView.

@InjectLayout(R.layout.activity_main)
public class MainActivity extends FragmentActivity {
  InjectLayout annotation = getClass().getAnnotation(InjectLayout.class);
  int id = annotation.value();
 try {
    Method setContentView = getClass().getMethod("setContentView", int.class);
    setContentView.invoke(this, id);
 } catch (Exception e) {
    e.printStackTrace ();
}

 

Encapsulate the above code into BaseActivity, and then you may no longer need to write the setContentView and findViewById methods in the activity.

Similarly, this approach also applies to Fragments, but some workarounds may be required. But not difficult.

open source project

There is a powerful open source project on Github that RoboGuiceimplements a complete set of injection functions, you can read its official WIKI to understand:

https://github.com/roboguice/roboguice/wiki

performance

As we all know, compared to normal method calls, reflection calls have disadvantages in execution efficiency, and reflection cannot be optimized at compile time, making the performance gap even more obvious. Although the average performance of Android phones is now much better than in the beginning, there are times when performance is often a very important point to consider. At this time, you may be entangled with the convenience of @InjectView and some performance losses caused by reflection, which is more important.

A more efficient InjectView – ButterKnife

This is an open source project, ButterKnife. The official introduction is View Injection through AnnotationProcessor, not reflection. There is no concern about performance.

Using ButterKnife is as simple as annotating the View as mentioned above, and then calling the appropriate ButterKnife.inject(…) method after setContentView (or after inflateView for Fragment ) .

In addition, ButterKnife's injection does not depend on reflection, we can safely use it in Adapter#getView:

 

public View getView(int position, View convertView, ViewGroup parent) {
        end ViewHolder viewHolder;
        if (convertView == null) {
            convertView = layoutInflater.inflate(R.layout.element_picture_event, null);
            viewHolder = new ViewHolder(convertView);
            convertView.setTag (viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }
        //do anything with view holder
}
...
class ViewHolder {
    @InjectViews({R.id.imageView0, R.id.imageView1, R.id.imageView2, R.id.imageView3}) ImageView[] imageViews;
    @InjectView(R.id.nicknameView) TextView nicknameView;
    @InjectView(R.id.infoView) TextView infoView;
    @InjectView(R.id.avatarView) ImageView avatarView;
    @InjectView(R.id.statusView) TextView statusView;
    public ViewHolder(View view) {
        ButterKnife.inject(this, view);
    }
}

 

For more usage, you can refer to the official instructions:

http://jakewharton.github.io/butterknife/

Configure the IDE

To use ButterKnife in the IDE, the IDE needs to be properly configured to run the program normally.

For example, in IntelliJ IDEA, the AnnotationProcessor configuration needs to be enabled. As shown in the figure:

image

For Eclipse, the official also gives the corresponding way to open AnnotationProcessor, you can refer to the following link:

http://jakewharton.github.io/butterknife/ide-eclipse.html

For the IDE's automatic formatting code, the Annotation may be forced to be displayed on a single line, for example:

 

@InjectView(R.id.ptrLayout)
PullToRefreshLayout ptrLayout;

 

If you don't like the above way, you can configure it in the IDE, for example in IDEA:

image

This ensures that @InjectView and the definition View are displayed on one line:

@InjectView(R.id.ptrLayout) PullToRefreshLayout ptrLayout;

 

Configuration in Ant

Because AnnotationProcessor is used, some additional configurations may be required when using some tools, such as ant packaging. For example, for ant, a target for processing Annotation needs to be added. The reference code is as follows:

<javac encoding="UTF-8"
       source="1.5" target="1.5"
       debug="false" extdirs="" includeantruntime="false"
       destdir="bin/classes"
       bootclasspathref="project.target.class.path"
       verbose="false"
       classpathref="project.javac.classpath"
       fork="false">
    <src path="src"/>
    <src path="gen"/>
    <compilerarg line=""/>
    <compilerarg line="-processorpath libs/butterknife-6.0.0.jar"/>
</javac>

 

For Maven or Gradle, I believe that using Annotation Processor + Maven or Gradle, you can Google to the corresponding solution.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=327007141&siteId=291194637