Created
July 14, 2016 15:49
-
-
Save moagrius/dd1db0caa673d347a59c6bcbb3401298 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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