diff --git a/CalligraphySample/src/main/java/uk/co/chrisjenx/calligraphy/sample/CalligraphyApplication.java b/CalligraphySample/src/main/java/uk/co/chrisjenx/calligraphy/sample/CalligraphyApplication.java
index 7af7454..eab1578 100644
--- a/CalligraphySample/src/main/java/uk/co/chrisjenx/calligraphy/sample/CalligraphyApplication.java
+++ b/CalligraphySample/src/main/java/uk/co/chrisjenx/calligraphy/sample/CalligraphyApplication.java
@@ -16,6 +16,7 @@ public void onCreate() {
CalligraphyConfig.initDefault(new CalligraphyConfig.Builder()
.setDefaultFontPath("fonts/Roboto-ThinItalic.ttf")
.setFontAttrId(R.attr.fontPath)
+ .addCustomViewWithSetTypeface(CustomViewWithTypefaceSupport.class)
.addCustomStyle(TextField.class, R.attr.textFieldStyle)
.build()
);
diff --git a/CalligraphySample/src/main/java/uk/co/chrisjenx/calligraphy/sample/CustomViewWithTypefaceSupport.java b/CalligraphySample/src/main/java/uk/co/chrisjenx/calligraphy/sample/CustomViewWithTypefaceSupport.java
new file mode 100644
index 0000000..7b54681
--- /dev/null
+++ b/CalligraphySample/src/main/java/uk/co/chrisjenx/calligraphy/sample/CustomViewWithTypefaceSupport.java
@@ -0,0 +1,75 @@
+package uk.co.chrisjenx.calligraphy.sample;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * @author Dmitriy Tarasov
+ */
+public class CustomViewWithTypefaceSupport extends View {
+
+ private Paint paint;
+ private Rect textBounds;
+ private int width;
+ private int height;
+
+ public CustomViewWithTypefaceSupport(Context context) {
+ super(context);
+ init();
+ }
+
+ public CustomViewWithTypefaceSupport(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ public CustomViewWithTypefaceSupport(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init();
+ }
+
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ public CustomViewWithTypefaceSupport(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ init();
+ }
+
+ private void init() {
+ paint = new Paint();
+ paint.setTextSize(50);
+ textBounds = new Rect();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ String text = "This is a custom view with setTypeface support";
+ Paint.FontMetrics fm = paint.getFontMetrics();
+ paint.getTextBounds(text, 0, text.length(), textBounds);
+
+ width = textBounds.left + textBounds.right + getPaddingLeft() + getPaddingRight();
+ height = (int) (Math.abs(fm.top) + fm.bottom);
+
+ canvas.drawText(text, 0, -fm.top + getPaddingTop(), paint);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ setMeasuredDimension(width, height);
+ }
+
+ /**
+ * Used by Calligraphy to change view's typeface
+ */
+ @SuppressWarnings("unused")
+ public void setTypeface(Typeface tf) {
+ paint.setTypeface(tf);
+ invalidate();
+ }
+}
diff --git a/CalligraphySample/src/main/res/layout/fragment_main.xml b/CalligraphySample/src/main/res/layout/fragment_main.xml
index 4c7bc46..7dff524 100644
--- a/CalligraphySample/src/main/res/layout/fragment_main.xml
+++ b/CalligraphySample/src/main/res/layout/fragment_main.xml
@@ -72,6 +72,11 @@
android:layout_height="wrap_content"
android:text="@string/defined_custom_view"/>
+
+
, Integer> mClassStyleAttributeMap;
+ /**
+ * Collection of custom non-{@code TextView}'s registered for applying typeface during inflation
+ * @see uk.co.chrisjenx.calligraphy.CalligraphyConfig.Builder#addCustomViewWithSetTypeface(Class)
+ */
+ private final Set> hasTypefaceViews;
protected CalligraphyConfig(Builder builder) {
mIsFontSet = builder.isFontSet;
@@ -111,9 +123,11 @@ protected CalligraphyConfig(Builder builder) {
mAttrId = builder.attrId;
mReflection = builder.reflection;
mCustomViewCreation = builder.customViewCreation;
+ mCustomViewTypefaceSupport = builder.customViewTypefaceSupport;
final Map, Integer> tempMap = new HashMap<>(DEFAULT_STYLES);
tempMap.putAll(builder.mStyleClassMap);
mClassStyleAttributeMap = Collections.unmodifiableMap(tempMap);
+ hasTypefaceViews = Collections.unmodifiableSet(builder.mHasTypefaceClasses);
}
/**
@@ -138,6 +152,14 @@ public boolean isCustomViewCreation() {
return mCustomViewCreation;
}
+ public boolean isCustomViewTypefaceSupport() {
+ return mCustomViewTypefaceSupport;
+ }
+
+ public boolean isCustomViewHasTypeface(View view) {
+ return hasTypefaceViews.contains(view.getClass());
+ }
+
/* default */ Map, Integer> getClassStyles() {
return mClassStyleAttributeMap;
}
@@ -162,6 +184,10 @@ public static class Builder {
* Use Reflection to intercept CustomView inflation with the correct Context.
*/
private boolean customViewCreation = true;
+ /**
+ * Use Reflection during view creation to try change typeface via setTypeface method if it exists
+ */
+ private boolean customViewTypefaceSupport = false;
/**
* The fontAttrId to look up the font path from.
*/
@@ -179,6 +205,8 @@ public static class Builder {
*/
private Map, Integer> mStyleClassMap = new HashMap<>();
+ private Set> mHasTypefaceClasses = new HashSet<>();
+
/**
* This defaults to R.attr.fontPath. So only override if you want to use your own attrId.
*
@@ -275,6 +303,15 @@ public Builder addCustomStyle(final Class extends TextView> styleClass, final
return this;
}
+ /**
+ * Register custom non-{@code TextView}'s which implement {@code setTypeface} so they can have the Typeface applied during inflation.
+ */
+ public Builder addCustomViewWithSetTypeface(Class> clazz) {
+ customViewTypefaceSupport = true;
+ mHasTypefaceClasses.add(clazz);
+ return this;
+ }
+
public CalligraphyConfig build() {
this.isFontSet = !TextUtils.isEmpty(fontAssetPath);
return new CalligraphyConfig(this);
diff --git a/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/CalligraphyFactory.java b/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/CalligraphyFactory.java
index ca45b62..dbff457 100644
--- a/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/CalligraphyFactory.java
+++ b/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/CalligraphyFactory.java
@@ -3,6 +3,7 @@
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Context;
+import android.graphics.Typeface;
import android.os.Build;
import android.text.TextUtils;
import android.util.AttributeSet;
@@ -11,6 +12,8 @@
import android.view.ViewTreeObserver;
import android.widget.TextView;
+import java.lang.reflect.Method;
+
class CalligraphyFactory {
private static final String ACTION_BAR_TITLE = "action_bar_title";
@@ -124,18 +127,8 @@ void onViewCreatedInternal(View view, final Context context, AttributeSet attrs)
// Try to get typeface attribute value
// Since we're not using namespace it's a little bit tricky
- // Try view xml attributes
- String textViewFont = CalligraphyUtils.pullFontPathFromView(context, attrs, mAttributeId);
-
- // Try view style attributes
- if (TextUtils.isEmpty(textViewFont)) {
- textViewFont = CalligraphyUtils.pullFontPathFromStyle(context, attrs, mAttributeId);
- }
-
- // Try View TextAppearance
- if (TextUtils.isEmpty(textViewFont)) {
- textViewFont = CalligraphyUtils.pullFontPathFromTextAppearance(context, attrs, mAttributeId);
- }
+ // Check xml attrs, style attrs and text appearance for font path
+ String textViewFont = resolveFontPath(context, attrs);
// Try theme attributes
if (TextUtils.isEmpty(textViewFont)) {
@@ -178,7 +171,50 @@ public void onGlobalLayout() {
}
});
}
+
+ // Try to set typeface for custom views using interface method or via reflection if available
+ if (view instanceof HasTypeface) {
+ Typeface typeface = getDefaultTypeface(context, resolveFontPath(context, attrs));
+ if (typeface != null) {
+ ((HasTypeface) view).setTypeface(typeface);
+ }
+ } else if (CalligraphyConfig.get().isCustomViewTypefaceSupport() && CalligraphyConfig.get().isCustomViewHasTypeface(view)) {
+ final Method setTypeface = ReflectionUtils.getMethod(view.getClass(), "setTypeface");
+ String fontPath = resolveFontPath(context, attrs);
+ Typeface typeface = getDefaultTypeface(context, fontPath);
+ if (setTypeface != null && typeface != null) {
+ ReflectionUtils.invokeMethod(view, setTypeface, typeface);
+ }
+ }
}
+ private Typeface getDefaultTypeface(Context context, String fontPath) {
+ if (TextUtils.isEmpty(fontPath)) {
+ fontPath = CalligraphyConfig.get().getFontPath();
+ }
+ if (!TextUtils.isEmpty(fontPath)) {
+ return TypefaceUtils.load(context.getAssets(), fontPath);
+ }
+ return null;
+ }
+
+ /**
+ * Resolving font path from xml attrs, style attrs or text appearance
+ */
+ private String resolveFontPath(Context context, AttributeSet attrs) {
+ // Try view xml attributes
+ String textViewFont = CalligraphyUtils.pullFontPathFromView(context, attrs, mAttributeId);
+ // Try view style attributes
+ if (TextUtils.isEmpty(textViewFont)) {
+ textViewFont = CalligraphyUtils.pullFontPathFromStyle(context, attrs, mAttributeId);
+ }
+
+ // Try View TextAppearance
+ if (TextUtils.isEmpty(textViewFont)) {
+ textViewFont = CalligraphyUtils.pullFontPathFromTextAppearance(context, attrs, mAttributeId);
+ }
+
+ return textViewFont;
+ }
}
diff --git a/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/HasTypeface.java b/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/HasTypeface.java
new file mode 100644
index 0000000..48950f9
--- /dev/null
+++ b/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/HasTypeface.java
@@ -0,0 +1,22 @@
+package uk.co.chrisjenx.calligraphy;
+
+import android.graphics.Typeface;
+
+/**
+ * There are two ways to set typeface for custom views:
+ *
+ * - Implementing this interface. You should only implements {@link #setTypeface(Typeface)} method.
+ * - Or via reflection. If custom view already has setTypeface method you can
+ * register it during Calligraphy configuration
+ * {@link uk.co.chrisjenx.calligraphy.CalligraphyConfig.Builder#addCustomViewWithSetTypeface(Class)}
+ *
+ * First way is faster but encourage more effort from the developer to implements interface. Second one
+ * requires less effort but works slowly cause reflection calls.
+ *
+ * @author Dmitriy Tarasov
+ */
+public interface HasTypeface {
+
+ void setTypeface(Typeface typeface);
+
+}
diff --git a/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/ReflectionUtils.java b/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/ReflectionUtils.java
index 923a20d..99c0d86 100644
--- a/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/ReflectionUtils.java
+++ b/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/ReflectionUtils.java
@@ -1,5 +1,7 @@
package uk.co.chrisjenx.calligraphy;
+import android.util.Log;
+
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -10,6 +12,8 @@
*/
class ReflectionUtils {
+ private static final String TAG = ReflectionUtils.class.getSimpleName();
+
static Field getField(Class clazz, String fieldName) {
try {
final Field f = clazz.getDeclaredField(fieldName);
@@ -50,8 +54,8 @@ static void invokeMethod(Object object, Method method, Object... args) {
try {
if (method == null) return;
method.invoke(object, args);
- } catch (IllegalAccessException | InvocationTargetException ignored) {
- ignored.printStackTrace();
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ Log.d(TAG, "Can't invoke method using reflection", e);
}
}
}