Skip to content

Instantly share code, notes, and snippets.

@billmote
Forked from patrickhammond/CheckBoxHelper.java
Last active August 29, 2015 14:10
Show Gist options
  • Save billmote/c15a3733f39c5095287f to your computer and use it in GitHub Desktop.
Save billmote/c15a3733f39c5095287f to your computer and use it in GitHub Desktop.
import android.graphics.Rect;
import android.view.TouchDelegate;
import android.view.View;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.CheckBox;
/**
* Enlarges the effective hit target of a checkbox to more closely match the dimensions of the
* provided root view. If the provided root isn't actually an ancestor view of the checkbox, bad
* things will happen.
*/
public class CheckBoxHelper {
public static void enlargeCheckBoxHitTarget(final View rootView, final CheckBox checkBox) {
ViewTreeObserverHelper.notifyWhenMeasured(rootView, new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect origHitRect = new Rect();
checkBox.getHitRect(origHitRect);
int relativeCheckTop = ViewHelper.getRelativeTop(rootView, checkBox);
int relativeCheckBottom = ViewHelper.getRelativeBottom(rootView, checkBox);
int relativeCheckLeft = ViewHelper.getRelativeLeft(rootView, checkBox);
int additionalTop = relativeCheckTop;
int additionalBottom = rootView.getMeasuredHeight() - relativeCheckBottom;
int additionalLeft = relativeCheckLeft;
int additionalRight = additionalLeft; // Semi-arbitrary, right is same as left
Rect delegateHitRect = new Rect(
origHitRect.left - additionalLeft,
origHitRect.top - additionalTop,
origHitRect.right + additionalLeft + additionalRight,
origHitRect.bottom + additionalTop + additionalBottom);
rootView.setTouchDelegate(new TouchDelegate(delegateHitRect, checkBox));
}
});
}
}
Before (white space around the checkbox is not touchable):
____________________
| |
| [.] Lorem ipsum |
|____________________|
After (dots around the checkbox are touchable):
____________________
|....... |
|..[.]..Lorem ipsum |
|......._____________|
View convertView = // for example, something from an adapter
CheckBox checkBox = // a checkbox that exists inside of convertview
CheckBoxHelper.enlargeCheckBoxHitTarget(convertView, checkBox);
import android.view.View;
public class ViewHelper {
/**
* Similar to View.getLeft(), but you specify what parent the value is relative to. If you
* provide a root view that isn't actually an ancestor view, bad things will happen.
*/
public static int getRelativeLeft(View relativeRootView, View view) {
if (view.getParent() == relativeRootView) {
return view.getLeft();
} else {
return view.getLeft() + getRelativeLeft(relativeRootView, (View) view.getParent());
}
}
/**
* Similar to View.getTop(), but you specify what parent the value is relative to. If you
* provide a root view that isn't actually an ancestor view, bad things will happen.
*/
public static int getRelativeTop(View relativeRootView, View view) {
if (view.getParent() == relativeRootView) {
return view.getTop();
} else {
return view.getTop() + getRelativeTop(relativeRootView, (View) view.getParent());
}
}
/**
* Similar to View.getRight(), but you specify what parent the value is relative to. If you
* provide a root view that isn't actually an ancestor view, bad things will happen.
*/
public static int getRelativeRight(View relativeRootView, View view) {
if (view.getParent() == relativeRootView) {
return view.getRight();
} else {
return getRelativeRight(relativeRootView, (View) view.getParent()) - view.getRight();
}
}
/**
* Similar to View.getBottom(), but you specify what parent the value is relative to. If you
* provide a root view that isn't actually an ancestor view, bad things will happen.
*/
public static int getRelativeBottom(View relativeRootView, View view) {
if (view.getParent() == relativeRootView) {
return view.getBottom();
} else {
return getRelativeBottom(relativeRootView, (View) view.getParent()) - view.getBottom();
}
}
}
import android.annotation.SuppressLint;
import android.os.Build;
import android.view.View;
import android.view.ViewTreeObserver;
@SuppressLint("NewApi")
public class ViewTreeObserverHelper {
public static void notifyWhenMeasured(final View view, final ViewTreeObserver.OnGlobalLayoutListener listener) {
ViewTreeObserver vto = view.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
listener.onGlobalLayout();
// Need to get a fresh ViewTreeObserver
ViewTreeObserver freshVto = view.getViewTreeObserver();
if (Build.VERSION.SDK_INT < 16) {
deprecatedRemoveOnGlobalLayoutListener(freshVto);
} else {
freshVto.removeOnGlobalLayoutListener(this);
}
}
/**
* Deprecated because it was inconsistently named.
*
* This is in its own method to isolate the scope of the suppressed warning.
*/
@SuppressWarnings("deprecation")
private void deprecatedRemoveOnGlobalLayoutListener(ViewTreeObserver freshVto) {
freshVto.removeGlobalOnLayoutListener(this);
}
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment