TextView设置字重(自定义自重)

1、目的

android提供的几种加粗方法不满足我司ui设计的字体字重

2、三种加粗方法

  1. 设置TextView的textStyle为Bold,这种方式的textView很粗
    xml:android:textStyle="bold"
    代码:paint.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
    通过textStyle方式



  2. 代码设置FakeBoldText,这种方式加粗过的比较接近medium效果

paint.setFakeBoldText(true)

在这里插入图片描述

  1. 如果以上都不满足ui小姐姐,我们还可以换种思路,TextView的加粗其实本质就是画笔paint的粗细,我们可以通过设置画笔的宽度来满足需求
paint.setStrokeWidth(8f);
paint.setStyle(Paint.Style.FILL_AND_STROKE);

在这里插入图片描述

(设置Style为FILL_AND_STROKE的目的是:如果字体过大,填充模式为STROKE的话就会出现字画之间的漏洞,设置FILL不会有效果,如图)

STOKE模式:
在这里插入图片描述


FIll模式:
在这里插入图片描述

3、第三种方式额外问题以及解决方案

问题:
  1、每个TextView都这样设置的话,重复代码太多
  2、字重之间没有一个衡量单位标准
  3、字号大小不同需要设置不同的StrokeWidth,如果不设置,小字号看起来很粗,大字号看起来没效果

解决方案:
  1、继承LayoutFactory2(每个view创建的地方),在xml里面自定义设置一个额外属性,如果有地方需要自定义自重,使用该属性设置
  2、与自家ui小姐姐商讨确定字重的等级集,比如我们公司就制定一共5个等级字重
  3、根据字号以及等级,设计一套计算规则来计算出StrokeWidth

3.1实现细节

  1. 在xml中自定义属性textBoldStyle,枚举,定义了5个等级
    <!-- 系统TextView自定义额外属性 -->
    <attr name="textBold" format="enum">
        <enum name="zero" value="0" />
        <enum name="one" value="1" />
        <enum name="two" value="2" />
        <enum name="three" value="3" />
        <enum name="four" value="4" />
    </attr>
  1. 在要用到的xml的文件TextVIew节点下设置该属性
    <TextView
        android:id="@+id/textView3"
        style="@style/TextVIewStyle"
        app:textBold="two" />

  style属性:

    <style name="TextVIewStyle">
        <item name="android:layout_width">match_parent</item>
        <item name="android:layout_height">100dp</item>
        <item name="android:gravity">center</item>
        <item name="android:text">Hello World!你好世界!</item>
        <item name="android:textSize">20sp</item>
    </style>
  1. 重写LayoutFactory2,关键思想就是重写onCreateView,自己创建TextView以及TextView的子类(怎么重写?一个字,抄,抄系统源码),然后读取textBold属性,获取加粗等级,然后设计一套计算规则(我只是简单的根据字体dp进行等比放大缩小,效果还可以),得到设置画笔宽度的值
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;

import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

public class NightThemeInflaterFactory implements LayoutInflater.Factory2 {

	private Map<TextView, Integer> map = new HashMap<>();

	private static final String NAMESPACE_RES_AUTO = "http://schemas.android.com/apk/res-auto";
	private static final String[] mClassPrefixList = {
			"android.widget.",
			"android.webkit.",
			"android.app.",
			"android.view."
	};

	//记录对应VIEW的构造函数
	private static final Class<?>[] mConstructorSignature = new Class[]{
			Context.class, AttributeSet.class};

	private static final HashMap<String, Constructor<? extends View>> mConstructorMap =
			new HashMap<String, Constructor<? extends View>>();

	@Override
	public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
		return onCreateView(name, context, attrs);
	}

	@Override
	public View onCreateView(String name, Context context, AttributeSet attrs) {
		//创建系统的控件
		View view = createSDKView(name, context, attrs);
		if (null == view) {
			//创建非系统控件
			view = createView(name, context, attrs);
		}

		//如果是TextView或者是继承了TextView的子控件
		if (view instanceof TextView) {
			TextView textView = (TextView) view;
			//查找textBold属性(xml中直接定义的)
			int value = attrs.getAttributeIntValue(NAMESPACE_RES_AUTO, "textBold", -1);
			//查找textBold属性(写在style里面的)
			if (value == -1) {
				TypedArray typedArray = context.obtainStyledAttributes(R.style.TextVIewStyle, new int[]{R.attr.textBold});
				value = typedArray.getInt(0, -1);
				typedArray.recycle();
			}
			float density = getDensity(context);
			float textSize = textView.getTextSize();
			//将文字大小换算成dp属性,根据dp进行放大缩小系数
			int dp = (int) (textSize / density);
			//我的项目是以dp的1/10为最大的等级,最低等级是0即正常字体(不加粗),
			//然后在1/10的基础上再划分为4个等级
			float fullNums = dp / 20f;
			value = Math.min(value, 4);
			float targetLevel = fullNums * value;
			if (value > -1 && textView.getTypeface().getStyle() != Typeface.BOLD) {
				textView.getPaint().setStrokeWidth(targetLevel);
				textView.getPaint().setStyle(Paint.Style.FILL_AND_STROKE);
				map.put(textView, value);
			}
		}
		return view;

	}

	public void generateTvBold(float percent, Context context) {
		//我的项目是以dp的1/10为最大的等级,最低等级是0即正常字体(不加粗),
		//然后在1/10的基础上再划分为4个等级
		float density = getDensity(context);
		TextView textView = null;
		for (Map.Entry<TextView, Integer> entry : map.entrySet()) {
			textView = entry.getKey();
			float textSize = textView.getTextSize();
			int dp = (int) (textSize / density);
			float fullNums = dp / percent;
			int value = entry.getValue();
			float targetLevel = fullNums * value;
			textView.getPaint().setStrokeWidth(targetLevel);
			textView.getPaint().setStyle(Paint.Style.FILL_AND_STROKE);
			textView.invalidate();
		}

	}

	private View createSDKView(String name, Context context, AttributeSet
			attrs) {
		//如果包含 . 则不是SDK中的view 可能是自定义view包括support库中的View
		if (-1 != name.indexOf('.')) {
			return null;
		}
		//不包含就要在解析的 节点 name前,拼上: android.widget. 等尝试去反射
		for (int i = 0; i < mClassPrefixList.length; i++) {
			View view = createView(mClassPrefixList[i] + name, context, attrs);
			if (view != null) {
				return view;
			}
		}
		return null;
	}

	/**
	 * @param name
	 * @param context
	 * @param attrs   反射创建view
	 * @return
	 */
	private View createView(String name, Context context, AttributeSet
			attrs) {
		Constructor<? extends View> constructor = findConstructor(context, name);
		try {
			return constructor.newInstance(context, attrs);
		} catch (Exception e) {
		}
		return null;
	}

	private Constructor<? extends View> findConstructor(Context context, String name) {
		Constructor<? extends View> constructor = mConstructorMap.get(name);
		if (constructor == null) {
			try {
				Class<? extends View> clazz = context.getClassLoader().loadClass
						(name).asSubclass(View.class);
				constructor = clazz.getConstructor(mConstructorSignature);
				mConstructorMap.put(name, constructor);
			} catch (Exception e) {
			}
		}
		return constructor;
	}

	private float getDensity(Context context) {
		return context.getResources().getDisplayMetrics().density;
	}

}
  1. 在activity的onCreate函数的setContentView之前设置使用我们自定义的layoutFactory2
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		LayoutInflaterCompat.setFactory2(getLayoutInflater(), CustomFactory());
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
	}
  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值