考虑到圆角背景在应用中多处使用,各种颜色以及radius小调整导致图片泛滥,所以统一使用Factory2配合自定义属性进行替换
attrs.xml内相关自定义属性
<declare-styleable name="rect_style">
<attr format="dimension" name="rect_radius_lt" />
<attr format="dimension" name="rect_radius_rt" />
<attr format="dimension" name="rect_radius_lb" />
<attr format="dimension" name="rect_radius_rb" />
<attr format="dimension" name="rect_radius" />
<attr format="color" name="rect_color" />
</declare-styleable>
考虑到可能还有其余factory使用,比如说一键替换背景,所以我们提供一个out属性,用于兼容
open class BaseActivity: FragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
val rectBgFactory2= RectBgInflateFactory2()
// val skinFactory2=SkinInflateFactory2()
// rectBgFactory2.outFactory=skinFactory2
layoutInflater.factory2=RectBgInflateFactory2()
super.onCreate(savedInstanceState)
}
}
接下来是rect factory的实现
主要逻辑在于遍历判断xml解析得到的tagname,通过反射构造对应view,然后创建shape背景设置给view
package com.example.test
import android.content.Context
import android.graphics.drawable.ShapeDrawable
import android.graphics.drawable.shapes.RoundRectShape
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import androidx.collection.SimpleArrayMap
import java.lang.reflect.Constructor
class RectBgInflateFactory2 : LayoutInflater.Factory2 {
var outFactory:LayoutInflater.Factory2?=null
override fun onCreateView(
parent: View?,
name: String,
context: Context,
attrs: AttributeSet
): View? {
val view=createView(parent, name, context, attrs)
view?.let {
setRectBg(it,context,attrs)
}
return view
}
private fun setRectBg(view:View,context: Context,attrs: AttributeSet){
val rectTa = context.obtainStyledAttributes(attrs,R.styleable.rect_style)
val rectColor=rectTa.getColor(R.styleable.rect_style_rect_color,-1)
var rectRadiusLt=0f
var rectRadiusRt=0f
var rectRadiusLb=0f
var rectRadiusRb=0f
val rectRadius=rectTa.getDimension(R.styleable.rect_style_rect_radius,-1f)
if (rectRadius!=-1f){
rectRadiusLt=rectRadius
rectRadiusRt=rectRadius
rectRadiusLb=rectRadius
rectRadiusRb=rectRadius
}else{
rectRadiusLt=rectTa.getDimension(R.styleable.rect_style_rect_radius_lt,0f)
rectRadiusRt=rectTa.getDimension(R.styleable.rect_style_rect_radius_rt,0f)
rectRadiusLb=rectTa.getDimension(R.styleable.rect_style_rect_radius_lb,0f)
rectRadiusRb=rectTa.getDimension(R.styleable.rect_style_rect_radius_rb,0f)
}
if (rectColor!=-1){
val rectShape=ShapeDrawable(RoundRectShape(arrayOf(rectRadiusLt,rectRadiusLt,rectRadiusRt,rectRadiusRt,rectRadiusRb,rectRadiusRb,rectRadiusLb,rectRadiusLb).toFloatArray(),null,null))
rectShape.paint.color=rectColor
view.background=rectShape
}
}
private fun createView(parent: View?,name: String, context: Context, attrs: AttributeSet): View? {
//外部factory先处理view
var view:View?= outFactory?.onCreateView(parent, name, context, attrs)
if (view==null){
var vClassName = name
if (vClassName == "view") {
vClassName = attrs.getAttributeValue(null, "class")
}
constructorArgs[0] = context
constructorArgs[1] = attrs
if (-1 == name.indexOf('.')) {
for (i in vClassPrefixList.indices) {
view = createViewByPrefix(context, vClassName, vClassPrefixList[i])
if (view != null) {
break
}
}
} else {
//全路径
view=createViewByPrefix(context, vClassName, null)
}
constructorArgs[0] = null
constructorArgs[1] = null
}
return view
}
private val constructorCacheMap = SimpleArrayMap<String, Constructor<out View?>>()//class 缓存
private val constructorArgs = arrayOfNulls<Any>(2)//构造参数
private val vClassPrefixList = arrayOf(
"android.widget.",
"android.view.",
"android.webkit.",
"androidx.appcompat.widget"
)
private fun createViewByPrefix(context: Context, name: String, prefix: String?): View? {
var constructor = constructorCacheMap[name]
return try {
if (constructor == null) {
val clazz = Class.forName(
if (prefix != null) prefix + name else name,
false,
context.classLoader
).asSubclass(View::class.java)
constructor = clazz.getConstructor(Context::class.java, AttributeSet::class.java)
constructorCacheMap.put(name, constructor)
}
constructor!!.isAccessible = true
constructor.newInstance(*constructorArgs)
} catch (e: Exception) {
null
}
}
override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? {
return null
}
}