下面是关于 "android自定义View之复合控件" 的完整攻略。
什么是复合控件?
复合控件是指由多个基本控件组成的控件,它通常会具有一定的业务逻辑和自定义配置属性等特征。复合控件开发的一般过程是将多个基本控件组合在一起,并对组合后的控件进行一些额外的封装,以便于在项目中重复使用。
实现复合控件的步骤
开发自定义的复合控件通常需要以下步骤:
- 继承 ViewGroup 或已有的组合控件(如 ConstraintLayout 等),定义自己的组合控件。
- 在构造方法中初始化控件,并加载布局文件(可选)。
- 实现 onMeasure() 方法测量自定义控件的尺寸。
- 实现 onLayout() 方法对内部控件进行布局。
- 实现 onDraw() 方法绘制控件以及相应效果。
- 对外提供属性和方法,允许用户配置自定义控件的样式和行为。
示例演练
示例一:封装SwitchButton
下面是一个简单的示例代码,我们将会用代码实现自定义开关按钮:
<com.example.myview.SwitchButton
android:id="@+id/switch_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
app:switch_color="@color/colorAccent"
app:switch_radius="15dp" />
在布局文件中,我们定义了一个自定义的 SwitchButton 控件,在属性中设置了开关状态、按钮颜色和半径等参数。
class SwitchButton(context: Context, attrs: AttributeSet?) : RelativeLayout(context, attrs) {
private lateinit var switchView: View
private var isChecked = false
private var switchRadius = 0
private var switchColor = 0
init {
initView(context, attrs)
}
private fun initView(context: Context, attrs: AttributeSet?) {
LayoutInflater.from(context).inflate(R.layout.layout_switch_button, this, true)
switchView = findViewById(R.id.switch_button)
attrs?.let {
val typedArray =
context.obtainStyledAttributes(it, R.styleable.SwitchButton)
isChecked = typedArray.getBoolean(R.styleable.SwitchButton_checked, false)
switchColor =
typedArray.getColor(R.styleable.SwitchButton_switch_color, context.getColor(R.color.colorAccent))
switchRadius = typedArray.getDimensionPixelSize(
R.styleable.SwitchButton_switch_radius,
0
)
typedArray.recycle()
}
setSwitchView()
setOnClickListener {
isChecked = !isChecked
setSwitchView()
}
}
private fun setSwitchView() {
val layoutParams = switchView.layoutParams as LayoutParams
if (isChecked) {
switchView.setBackgroundResource(R.drawable.bg_switch_on)
layoutParams.leftMargin = 0
} else {
switchView.setBackgroundResource(R.drawable.bg_switch_off)
layoutParams.leftMargin = switchRadius*3
}
switchView.layoutParams = layoutParams
}
}
在代码中我们继承自 RelativeLayout,并对控件进行了初始化操作。我们通过上面的代码可以看到,我们在initView()方法中获取用户对控件传递过来的属性参数,并设置对应的属性,如checked、switch_color、switch_radius等。我们通过代码设置了开关状态,并提供了自定义开关颜色和宽度的属性,用户可以自由选择设置。最后通过onClickListener()监听器对开关状态进行控制,并刷新自定义开关。
示例二:封装微信底部导航栏效果
下面是另外一个示例代码,我们将会用代码实现自定义微信底部导航栏效果:
<com.example.myview.WechatBottomBar
android:id="@+id/bottom_bar"
android:layout_width="match_parent"
android:layout_height="56dp"
android:background="@color/colorPrimary"
app:text_color_normal="@color/colorPrimaryDark"
app:text_color_selected="@android:color/white"
app:item_text_color="#ffffff"
app:item_text_size="12sp"
app:item_margin="0dp"
app:bar_padding="12dp"
app:bar_margin_left="0dp"
app:bar_margin_right="0dp"
app:bar_margin_top="0dp"
app:bar_margin_bottom="0dp"
app:bottom_item_count="4" />
在布局文件中,我们定义了一个自定义的 WechatBottomBar 控件,在属性中设置了背景颜色、大小、字体颜色和间距等参数。
class WechatBottomBar @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr) {
private val radioButtons: MutableList<RadioButton> = mutableListOf()
var onCheckedChangeListener: OnCheckedChangeListener? = null
init {
initView(context, attrs)
}
override fun onFinishInflate() {
super.onFinishInflate()
radioButtons.forEach {
it.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
onCheckedChangeListener?.onCheckedChange(it)
radioButtons.filter { r -> r != it }.forEach { r -> r.isChecked = false }
}
}
}
}
private fun initView(
context: Context,
attrs: AttributeSet?
) {
attrs?.let {
val ta = context.obtainStyledAttributes(it, R.styleable.WechatBottomBar)
val bottomItemCount = ta.getInt(R.styleable.WechatBottomBar_bottom_item_count, 5)
val barPadding = ta.getDimension(R.styleable.WechatBottomBar_bar_padding, 0f)
val barMarginLeft = ta.getDimension(R.styleable.WechatBottomBar_bar_margin_left, 0f)
val barMarginRight = ta.getDimension(R.styleable.WechatBottomBar_bar_margin_right, barMarginLeft)
val barMarginTop = ta.getDimension(R.styleable.WechatBottomBar_bar_margin_top, 0f)
val barMarginBottom = ta.getDimension(R.styleable.WechatBottomBar_bar_margin_bottom, 0f)
val itemTextSize = ta.getDimension(R.styleable.WechatBottomBar_item_text_size, 0f)
val itemTextColor = ta.getColor(
R.styleable.WechatBottomBar_item_text_color,
resources.getColor(android.R.color.black)
)
val itemMargin = ta.getDimension(R.styleable.WechatBottomBar_item_margin, 0f)
val normalTextColor =
ta.getColor(R.styleable.WechatBottomBar_text_color_normal, itemTextColor)
val p = ta.getColor(R.styleable.WechatBottomBar_text_color_selected, normalTextColor)
val count = bottomItemCount.min(5).max(2)
val itemWidth = (width - paddingLeft - paddingRight) / bottomItemCount
LayoutInflater.from(context).inflate(R.layout.custom_wechat_bottom_bar, this, true)
gravity = Gravity.CENTER_HORIZONTAL
orientation = HORIZONTAL
for (i in 0 until count) {
val rb = RadioButton(context).apply {
layoutParams = LayoutParams(itemWidth, LayoutParams.MATCH_PARENT).apply {
leftMargin = itemMargin.toInt()
rightMargin = itemMargin.toInt()
topMargin = barPadding.toInt()
bottomMargin = barPadding.toInt()
}
setTextSize(TypedValue.COMPLEX_UNIT_PX, itemTextSize)
setTextColor(ColorStateList(
arrayOf(
intArrayOf(android.R.attr.state_checked),
intArrayOf(-android.R.attr.state_checked)
),
intArrayOf(p, normalTextColor)
))
this.buttonDrawable = null
isChecked = i == 0
text = "Item ${i + 1}"
id = i + 1
}
radioButtons.add(rb)
addView(rb)
}
ta.recycle()
setMargins(barMarginLeft.toInt(), barMarginTop.toInt(), barMarginRight.toInt(), barMarginBottom.toInt())
}
}
override fun generateLayoutParams(attrs: AttributeSet?): LayoutParams =
LayoutParams(0, LayoutParams.WRAP_CONTENT, 1f)
interface OnCheckedChangeListener {
fun onCheckedChange(button: RadioButton)
}
}
在代码中我们依然是继承自 LinearLayout,将 RadioButton 作为子控件添加到我们自定义的 WechatBottomBar 控件中。在 initView() 方法中获取了用户传递的属性值,包括底部控件的个数、内边距等属性。在代码中需要实现 OnCheckedChangeListener 接口,主要是实现选中状态的切换。在 onFinishInflate() 方法中,我们遍历获取到的 radioButtons 集合,监听 CheckedChangeListener 并为其指定具体实现。最终实现了微信底部导航栏的样式,并将传递过来的属性值指派到 RadioButton 控件。
结语
上面的两个示例都展示了如何实现一个复合控件。自定义 View 的过程需要掌握各个方法的含义,更需要面向对象的设计思想。希望通过本文的讲解,大家能够对自定义控件有一定的认识,也能够在实际项目中灵活应用。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:android自定义View之复合控件 - Python技术站