近日在读《Android群英传》中自定义控件详解,此文作为总结与回顾。
View中有些重要的回调方法:
- onFinishiInflate()从XML加载组件后回调
- onSizeChanged()组件大小修改时回调
- onMeasure()回调该方法对组件测量
- onLayout()回调该方法显示位置
- onTouchEvent()监听触摸事件
通常情况有三种方式创建自定义View控件:
- 对现有控件扩展
- 通过组合实现新控件
- 重写View实现新控件
1、对现有控件扩展
此方法是在原有控件基础上扩展,增加功能或者修改UI。
举一个例子,如下图,该控件继承自TextView,重写onDraw()方法,实现下图效果。
NewText代码:
public class NewText extends TextView {
private int mViewWidth;
private Paint mPaint;
private LinearGradient mLinearGradient;
private Matrix mGradientMatrix;
private int mTranslate;
private Paint mPaint1;
private Paint mPaint2;
public NewText(Context context) {
super(context);
}
public NewText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void onDraw(Canvas canvas) {
mPaint1 = new Paint();
mPaint1.setColor(Color.BLUE);
mPaint1.setStyle(Paint.Style.STROKE);
mPaint2 = new Paint();
mPaint2.setColor(Color.YELLOW);
mPaint2.setStyle(Paint.Style.FILL);
canvas.drawRect(0,0,getMeasuredWidth(),getMeasuredHeight(),mPaint1); //绘制蓝色矩形
canvas.drawRect(10,10,getMeasuredWidth() + 10,getMeasuredHeight() + 10,mPaint2); //绘制黄色矩形
canvas.save();
super.onDraw(canvas); //调用TextView的onDraw方法绘制文字
canvas.restore();
}
}
布局文件:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="com.example.administrator.ui.MainActivity"
tools:showIn="@layout/activity_main">
<com.example.administrator.ui.NewText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="hello world"
android:textSize="30sp"/>
</RelativeLayout>
遇到问题:
关于构造函数
public NewText(Context context) 通过Java代码动态创建该控件时调用
public NewText(Context context,AttributeSet attrs) xml布局文件中创建时调用
public NewText(Context context,AttributeSet attrs,int defStyle) xml布局文件中创建,同时设置Style时调用
2、通过组合实现新控件
通常继承一个GroupView,然后添加合适的控件,进而组合成新的复合控件。
下面以复合控件Topbar为例:
2.1定义属性
创建att.xml,定义相应属性
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="Topbar">
<attr name="title1" format="string" />
<attr name="titleTextSize" format="dimension" />
<attr name="titleTextColor1" format="color|reference" />
<attr name="leftTextColor" format="color|reference" />
<attr name="leftBackground" format="reference|color" />
<attr name="rightTextColor" format="reference|color" />
<attr name="rightBackground" format="reference|color" />
<attr name="rightText" format="string" />
<attr name="leftText" format="string" />
</declare-styleable>
</resources>
系统提供了TypeArray用来获取自定义属性集,通过TypeArray的getString(),getColor()等方法可以获得相应属性值。
TypeArray ta = context.obtainStyledAttributed(AttributeSet, int[]);
2.2组合控件
package com.example.administrator.ui;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.RelativeLayout;
import android.widget.TextView;
/**
* Created by Administrator on 2016/1/30.
*/
public class Topbar extends RelativeLayout {
private LayoutParams mTitleParams;
private LayoutParams mRightParams;
private LayoutParams mLeftParams;
private TextView mTitleView;
private Button mRightButton;
private Button mLeftButton;
private int mTitleTextColor;
private String mTitle;
private float mTitleTextSize;
private Drawable mRightBackground;
private String mRightText;
private int mRightTextColor;
private String mLeftText;
private Drawable mLeftBackground;
private int mLeftTextColor;
TypedArray ta;
public Topbar(Context context, AttributeSet attrs) {
super(context, attrs);
//将属性值存储到TypeArray对象中
ta = context.obtainStyledAttributes(attrs, R.styleable.Topbar);
//获取相应属性值
mLeftTextColor = ta.getColor(R.styleable.Topbar_leftTextColor, 0);
mLeftBackground = ta.getDrawable(R.styleable.Topbar_leftBackground);
mLeftText = ta.getString(R.styleable.Topbar_leftText);
mRightTextColor = ta.getColor(R.styleable.Topbar_rightTextColor, 0);
mRightBackground = ta.getDrawable(R.styleable.Topbar_rightBackground);
mRightText = ta.getString(R.styleable.Topbar_rightText);
mTitleTextSize = ta.getDimension(R.styleable.Topbar_titleTextSize, 30);
mTitleTextColor = ta.getColor(R.styleable.Topbar_titleTextColor1, 0);
mTitle = ta.getString(R.styleable.Topbar_title1);
//获取完TypeArray之后,调用recyle避免重新创建时出错
ta.recycle();
//创建组件并赋值
mLeftButton = new Button(context);
mRightButton = new Button(context);
mTitleView = new TextView(context);
mLeftButton.setBackground(mLeftBackground);
mLeftButton.setText(mLeftText);
mLeftButton.setTextColor(mLeftTextColor);
mRightButton.setBackground(mRightBackground);
mRightButton.setText(mRightText);
mRightButton.setTextColor(mRightTextColor);
mTitleView.setText(mTitle);
mTitleView.setTextColor(mTitleTextColor);
mTitleView.setTextSize(mTitleTextSize);
mTitleView.setGravity(TEXT_ALIGNMENT_CENTER);
//设置布局元素
mLeftParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
mLeftParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT, TRUE);
addView(mLeftButton, mLeftParams);
mRightParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
mRightParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT,TRUE);
addView(mRightButton, mRightParams);
mTitleParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
mTitleParams.addRule(RelativeLayout.CENTER_IN_PARENT,TRUE);
addView(mTitleView, mTitleParams);
//添加mRightButton与mLeftButton响应事件
mRightButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mListener.RightClick();
}
});
mLeftButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mListener.LeftClick();
}
});
}
public Topbar(Context context) {
super(context);
}
public Topbar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
ta = context.obtainStyledAttributes(attrs, R.styleable.Topbar);
}
private TopbarClickListener mListener;
public void setonTopbarClickListener(TopbarClickListener listener){
this.mListener = listener;
}
//为调用者暴漏接口
public interface TopbarClickListener{
void LeftClick();
void RightClick();
}
}
Topbar bar = (Topbar) findViewById(R.id.topbar);
bar.setonTopbarClickListener(new Topbar.TopbarClickListener() {
@Override
public void LeftClick() {
Toast.makeText(MainActivity.this,"left",Toast.LENGTH_LONG).show();
}
@Override
public void RightClick() {
Toast.makeText(MainActivity.this,"right",Toast.LENGTH_LONG).show();
}
});
第三方控件使用使用下面代码引入名字空间
xmlns:app="http://schemas.android.com/apk/res-auto"
名字空间取为app,自定义属性调用如下
<com.example.administrator.ui.Topbar
android:id="@+id/topbar"
android:layout_width="match_parent"
android:layout_height="60dp"
app:leftBackground="@color/colorAccent"
app:leftText="left"
app:leftTextColor="@color/colorPrimary"
app:rightBackground="@color/colorAccent"
app:rightText="right"
app:rightTextColor="@color/colorPrimary"
app:title1="title"
app:titleTextColor1="@color/colorPrimary"
app:titleTextSize="30sp"/>
3、重写View实现新控件
继承View,并重写它的onDraw(),onMeasure()实现绘制,onTouchEvent()等触控事件实现交互。