Skip to content

Commit

Permalink
Merge pull request #210 from TinkoffCreditSystems/master
Browse files Browse the repository at this point in the history
Added ability to set typefaces for custom views with setTypeface method
  • Loading branch information
chrisjenx committed Oct 12, 2015
2 parents 66c0d4a + 85fe6c0 commit 417120b
Show file tree
Hide file tree
Showing 8 changed files with 195 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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()
);
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}
}
5 changes: 5 additions & 0 deletions CalligraphySample/src/main/res/layout/fragment_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@
android:layout_height="wrap_content"
android:text="@string/defined_custom_view"/>

<uk.co.chrisjenx.calligraphy.sample.CustomViewWithTypefaceSupport
fontPath="fonts/Oswald-Stencbab.ttf"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

<ViewStub
android:id="@+id/stub"
android:layout_width="wrap_content"
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.3.0-beta3'
classpath 'com.android.tools.build:gradle:1.3.0'
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import android.os.Build;
import android.text.TextUtils;
import android.view.View;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.CheckBox;
Expand All @@ -13,7 +14,9 @@

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
* Created by chris on 20/12/2013
Expand Down Expand Up @@ -100,20 +103,31 @@ public static CalligraphyConfig get() {
* Use Reflection to intercept CustomView inflation with the correct Context.
*/
private final boolean mCustomViewCreation;
/**
* Use Reflection to try to set typeface for custom views if they has setTypeface method
*/
private final boolean mCustomViewTypefaceSupport;
/**
* Class Styles. Build from DEFAULT_STYLES and the builder.
*/
private final Map<Class<? extends TextView>, 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<Class<?>> hasTypefaceViews;

protected CalligraphyConfig(Builder builder) {
mIsFontSet = builder.isFontSet;
mFontPath = builder.fontAssetPath;
mAttrId = builder.attrId;
mReflection = builder.reflection;
mCustomViewCreation = builder.customViewCreation;
mCustomViewTypefaceSupport = builder.customViewTypefaceSupport;
final Map<Class<? extends TextView>, Integer> tempMap = new HashMap<>(DEFAULT_STYLES);
tempMap.putAll(builder.mStyleClassMap);
mClassStyleAttributeMap = Collections.unmodifiableMap(tempMap);
hasTypefaceViews = Collections.unmodifiableSet(builder.mHasTypefaceClasses);
}

/**
Expand All @@ -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<Class<? extends TextView>, Integer> getClassStyles() {
return mClassStyleAttributeMap;
}
Expand All @@ -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.
*/
Expand All @@ -179,6 +205,8 @@ public static class Builder {
*/
private Map<Class<? extends TextView>, Integer> mStyleClassMap = new HashMap<>();

private Set<Class<?>> mHasTypefaceClasses = new HashSet<>();

/**
* This defaults to R.attr.fontPath. So only override if you want to use your own attrId.
*
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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";
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package uk.co.chrisjenx.calligraphy;

import android.graphics.Typeface;

/**
* There are two ways to set typeface for custom views:
* <ul>
* <li>Implementing this interface. You should only implements {@link #setTypeface(Typeface)} method.</li>
* <li>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)}</li>
* </ul>
* 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);

}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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);
}
}
}

0 comments on commit 417120b

Please sign in to comment.