Skip to content

Instantly share code, notes, and snippets.

@moagrius
Created July 14, 2016 15:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save moagrius/dd1db0caa673d347a59c6bcbb3401298 to your computer and use it in GitHub Desktop.
Save moagrius/dd1db0caa673d347a59c6bcbb3401298 to your computer and use it in GitHub Desktop.
package example.viewcontrollers;
import android.app.Activity;
import android.view.View;
import android.view.ViewGroup;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
/**
* Created by michaeldunn on 7/13/16.
*
* This is a simple version of view binding via annotatino since ButterKnife started taking itself too seriously.
*/
public class SteakKnife {
/**
* Instances of this interface can be passed to the #bind method to determine how an ID is translated into a
* View instance of the appropriate type.
*/
public interface Finder {
<T extends View> T findAndCast(int resourceId);
}
/**
* Convenience class that uses Activity.findViewById and casts the return.
*/
private static class FindInActivity implements Finder {
private Activity mActivity;
public FindInActivity(Activity activity){
mActivity = activity;
}
@SuppressWarnings("unchecked")
public <T extends View> T findAndCast(int id) {
return (T) mActivity.findViewById(id);
}
}
/**
* Convenience class that uses ViewGroup.findViewById and casts the returns.
*/
private static class FindInView implements Finder {
private ViewGroup mViewGroup;
public FindInView(ViewGroup viewGroup){
mViewGroup = viewGroup;
}
@SuppressWarnings("unchecked")
public <T extends View> T findAndCast(int id) {
return (T) mViewGroup.findViewById(id);
}
}
/**
* The single-in method of binding. Currently, it loops through all members of #instance and checks for annotations
* attached to that member - it'd be more efficient to instead loop through annotations and find the associate member.
*
* Inaccessible (private, package-private, protected) members are temporarily set accessible in order to set the
* value, otherwise only public members could be updated.
*
* @param instance Any object instance whose annotated members will be populated.
* @param finder The Finder implementation used to convert a annoation's value (integer ID) into a View instance.
*/
public static void bind(Object instance, Finder finder){
for (Field field : instance.getClass().getDeclaredFields()) {
if (field.isAnnotationPresent(BindView.class)) {
Annotation annotation = field.getAnnotation(BindView.class);
BindView bindView = (BindView) annotation;
int resourceId = bindView.value();
if (resourceId > 0) {
try {
boolean isAccessible = field.isAccessible();
if (!isAccessible) {
field.setAccessible(true);
}
field.set(instance, finder.findAndCast(resourceId));
if (!isAccessible) {
field.setAccessible(false);
}
} catch (IllegalAccessException e) {
//
}
}
}
}
}
/**
* Convenience method that calls #bind with a Finder instance for use on a ViewGroup but decorates an arbitrary
* Object instance.
*
* @inheritDoc
*/
public static void bind(Object instance, ViewGroup viewGroup){
bind(instance, new FindInView(viewGroup));
}
/**
* Convenience method that calls #bind with a Finder instance for use on an Activity.
*
* @inheritDoc
*/
public static void bind(final Activity activity){
bind(activity, new FindInActivity(activity));
}
/**
* Convenience method that calls #bind with a Finder instance for use on an ViewGroup.
*
* @inheritDoc
*/
public static void bind(ViewGroup viewGroup){
bind(viewGroup, viewGroup);
}
/**
* Helper method to pre-cast Views to the type of the declared instance.
*
* @param <T> The Class reference (View subclass) the View should be cast to.
* @param viewGroup The ViewGroup that will be searched for Views with matching #id.
* @param id The id of the View to be located within this Activity's view tree.
* @return The View with the matching id.
*/
@SuppressWarnings("unchecked")
public static <T extends View> T findAndCast(ViewGroup viewGroup, int id) {
return (T) viewGroup.findViewById(id);
}
/**
* Helper method to pre-cast Views to the type of the declared instance.
*
* @param <T> The Class reference (View subclass) the View should be cast to.
* @param activity The Activity whose content view will be searched for Views with matching #id.
* @param id The id of the View to be located within this Activity's view tree.
* @return The View with the matching id.
*/
@SuppressWarnings("unchecked")
public static <T extends View> T findAndCast(Activity activity, int id) {
return (T) activity.findViewById(id);
}
/**
* Annotation interface. By using `value`, we send and receive a single, unnamed argument.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface BindView {
/**
* The single value passed to the annotation, which should be an integer id of a View.
*/
int value();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment