Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save NikolaDespotoski/1fa18066c72e4f5ac11c85d40d14c34c to your computer and use it in GitHub Desktop.
Save NikolaDespotoski/1fa18066c72e4f5ac11c85d40d14c34c to your computer and use it in GitHub Desktop.
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.design.widget.CoordinatorLayout;
import android.support.v4.view.WindowInsetsCompat;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Created by Nikola on 4/8/2016.
*/
public class MultiCoordinatorLayoutBehavior<V extends View> extends CoordinatorLayout.Behavior<V> {
static final Class<?>[] CONSTRUCTOR_PARAMS = new Class<?>[]{
Context.class,
AttributeSet.class
};
static final ThreadLocal<Map<String, Constructor<CoordinatorLayout.Behavior>>> sConstructors =
new ThreadLocal<>();
static final ThreadLocal<Map<CoordinatorLayout.Behavior, List<Method>>> sMethod =
new ThreadLocal<>();
private List<CoordinatorLayout.Behavior> mBehaviors = new ArrayList<>();
public MultiCoordinatorLayoutBehavior() {
super();
}
public MultiCoordinatorLayoutBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.MultiCoordinatorLayoutBehavior);
int resourceId = a.getResourceId(R.styleable.MultiCoordinatorLayoutBehavior_behaviors, 0);
a.recycle();
if (resourceId != 0) {
String[] stringArray = context.getResources().getStringArray(resourceId);
insantiateBehaviors(context, attrs, stringArray);
}
}
static CoordinatorLayout.Behavior parseBehavior(Context context, AttributeSet attrs, String name) {
if (TextUtils.isEmpty(name)) {
return null;
}
final String fullName;
if (name.startsWith(".")) {
fullName = context.getPackageName() + name;
} else if (name.indexOf('.') >= 0) {
fullName = name;
} else {
fullName = '.' + name;
}
try {
Map<String, Constructor<CoordinatorLayout.Behavior>> constructors = sConstructors.get();
if (constructors == null) {
constructors = new HashMap<>();
sConstructors.set(constructors);
}
Constructor<CoordinatorLayout.Behavior> c = constructors.get(fullName);
if (c == null) {
final Class<CoordinatorLayout.Behavior> clazz = (Class<CoordinatorLayout.Behavior>) Class.forName(fullName, true,
context.getClassLoader());
c = clazz.getConstructor(CONSTRUCTOR_PARAMS);
c.setAccessible(true);
constructors.put(fullName, c);
}
return c.newInstance(context, attrs);
} catch (Exception e) {
throw new RuntimeException("Could not inflate Behavior subclass " + fullName, e);
}
}
@Override
public Parcelable onSaveInstanceState(CoordinatorLayout parent, V child) {
return super.onSaveInstanceState(parent, child);
}
@Override
public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent ev) {
return super.onInterceptTouchEvent(parent, child, ev);
}
@Override
public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent ev) {
return super.onTouchEvent(parent, child, ev);
}
@Override
public int getScrimColor(CoordinatorLayout parent, V child) {
return super.getScrimColor(parent, child);
}
@Override
public float getScrimOpacity(CoordinatorLayout parent, V child) {
return super.getScrimOpacity(parent, child);
}
@Override
public boolean blocksInteractionBelow(CoordinatorLayout parent, V child) {
return super.blocksInteractionBelow(parent, child);
}
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, V child, View dependency) {
return super.layoutDependsOn(parent, child, dependency);
}
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, V child, View dependency) {
return super.onDependentViewChanged(parent, child, dependency);
}
@Override
public void onDependentViewRemoved(CoordinatorLayout parent, V child, View dependency) {
super.onDependentViewRemoved(parent, child, dependency);
}
@Override
public boolean isDirty(CoordinatorLayout parent, V child) {
return super.isDirty(parent, child);
}
@Override
public boolean onMeasureChild(CoordinatorLayout parent, V child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
return super.onMeasureChild(parent, child, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);
}
@Override
public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) {
return super.onLayoutChild(parent, child, layoutDirection);
}
@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, V child, View directTargetChild, View target, int nestedScrollAxes) {
return super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
}
@Override
public void onNestedScrollAccepted(CoordinatorLayout coordinatorLayout, V child, View directTargetChild, View target, int nestedScrollAxes) {
super.onNestedScrollAccepted(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
}
@Override
public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) {
super.onStopNestedScroll(coordinatorLayout, child, target);
}
@Override
public void onNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
}
@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx, int dy, int[] consumed) {
super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
}
@Override
public boolean onNestedFling(CoordinatorLayout coordinatorLayout, V child, View target, float velocityX, float velocityY, boolean consumed) {
invokeMethod("onNestedFling", coordinatorLayout, child, target, velocityX, velocityY, consumed);
return true;
}
@Override
public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, V child, View target, float velocityX, float velocityY) {
return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);
}
@Override
public WindowInsetsCompat onApplyWindowInsets(CoordinatorLayout coordinatorLayout, V child, WindowInsetsCompat insets) {
return super.onApplyWindowInsets(coordinatorLayout, child, insets);
}
@Override
public void onRestoreInstanceState(CoordinatorLayout parent, V child, Parcelable state) {
super.onRestoreInstanceState(parent, child, state);
}
private void insantiateBehaviors(Context context, AttributeSet attrs, @NonNull String[] behaviors) {
for (int i = behaviors.length - 1; i >= 0; i--) {
CoordinatorLayout.Behavior behavior = parseBehavior(context, attrs, behaviors[i]);
if (behavior != null) {
mBehaviors.add(behavior);
}
}
}
private void invokeMethod(String methodName, Object... params) {
for (int i = mBehaviors.size() - 1; i >= 0; i--) {
CoordinatorLayout.Behavior behavior = mBehaviors.get(i);
Map<CoordinatorLayout.Behavior, List<Method>> methodsMap = sMethod.get();
List<Method> methods = methodsMap.get(behavior);
if (methods == null) {
methods = resolveAllMethods(behavior);
methodsMap.put(behavior, methods);
}
Method method = getMethod(methods, methodName);
if(method != null && method.isAccessible()){
try {
method.invoke(behavior, params);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
private List<Method> resolveAllMethods(CoordinatorLayout.Behavior behavior) {
Method[] declaredMethods = behavior.getClass().getDeclaredMethods();
return Arrays.asList(declaredMethods);
}
private Method getMethod(List<Method> methods, String methodName) {
for (int i = methods.size() - 1; i >= 0; i--) {
Method method = methods.get(i);
if (method.getName().toLowerCase().equals(methodName)) return method;
}
return null;
}
<declare-styleable name="MultiCoordinatorLayoutBehavior">
<attr name="behaviors" format="reference"/>
</declare-styleable>
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment