morristech
12/11/2018 - 9:24 PM

Android Repeated Texture Fix

Android Repeated Texture Fix

public class DrawableUtils {

	/**
	 * @see #fixTexturesInLayerDrawable(Drawable)
	 */
	public static void fixBackgroundTextures(View view) {
		fixTexturesInLayerDrawable(view.getBackground());
	}

	/**
	 * <p>
	 * This will go through {@link LayerDrawable}s and all their children marked
	 * with {@link R.id#textureRepeat}, trying to find {@link BitmapDrawable}
	 * children and then setting them to a repeat {@link TileMode}.<br >
	 * 
	 * <p>
	 * Usage in XML:
	 * 
	 * <p>
	 * <code>
	 * &lt;layer-list&gt;
	 * &lt;item android:id="@+id/textureRepeat"&gt;
	 *   &lt;bitmap
	 *       android:src="@drawable/some_texture"
	 *       android:tileMode="repeat" /&gt;
	 * &lt;/item&gt;
	 * &lt;/layer-list&gt;
	 * 
	 * </code>
	 * 
	 * <p>
	 * This is a workaround for http://b.android.com/15340, based on
	 * http://stackoverflow.com/a/5852198/132047
	 * 
	 * <p>
	 * Repeated bitmap drawables have bugs pre Android 4.0 in some cases, which
	 * causes the texture to stretch instead of repeat.
	 * 
	 * <p>
	 * This fix doesn't work with {@link StateListDrawable}s. For such
	 * drawables, you'll need manual addition of the states that hold texture
	 */
	public static void fixTexturesInLayerDrawable(Drawable drawable) {
		if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
			if (drawable instanceof LayerDrawable) {

				LayerDrawable layer = (LayerDrawable) drawable;

				for (int i = 0; i < layer.getNumberOfLayers(); i++) {
					if (layer.getId(i) == R.id.textureRepeat) {
						fixTexturesInLayerDrawable(layer.getDrawable(i));
					}
				}
			} else if (drawable instanceof BitmapDrawable) {
				fixRepeatableBitmapDrawable((BitmapDrawable) drawable);
			} else {
				if (BuildConfig.DEBUG) {
					throw new UnsupportedOperationException("Can only fix BitmapDrawable and layer items in LayerDrawable, not " + drawable.getClass().getName());
				}
			}
		}
	}

	public static void fixRepeatableBitmapDrawable(BitmapDrawable drawable) {
		if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
			drawable.mutate();
			drawable.setTileModeXY(TileMode.REPEAT, TileMode.REPEAT);
		}
	}

	/**
	 * Manual fix of {@link R.drawable#my_button_background_selector}
	 * 
	 * See {@link #fixTexturesInLayerDrawable(Drawable)}
	 */
	public static void fixMyButtonBackgroundSelector(View view) {
		StateListDrawable stateListDrawable = (StateListDrawable) view.getBackground();

		int[] selectedStateSet = { android.R.attr.state_selected };
		Drawable selectedDrawable = view.getContext().getResources().getDrawable(R.drawable.my_button_background_selected);
		fixTexturesInLayerDrawable(selectedDrawable);
		stateListDrawable.addState(selectedStateSet, selectedDrawable);

		int[] defaultStateSet = {};
		Drawable defaultDrawable = view.getContext().getResources().getDrawable(R.drawable.my_button_background_default);
		fixTexturesInLayerDrawable(defaultDrawable);
		stateListDrawable.addState(defaultStateSet, defaultDrawable);
	}
}