一、自定义控件(一) --- 自定义属性TextView
1,定义属性,制作attrs.xml文件;
属性值:
string,color,attr,array,bool,declare-styleable,dimen,drawable,eat-comment,fraction,
integer,integer-array,item,plurals,string-array,style
属性取值范围:
string,color,demension,integer,enum,reference,float,boolean,fraction,flag;
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--CustomTitleView-->
<attr name="titleText" format="string" />
<attr name="titleTextColor" format="color" />
<attr name="titleTextSize" format="dimension" />
<declare-styleable name="CustomTitleView">
<attr name="titleText" />
<attr name="titleTextColor" />
<attr name="titleTextSize" />
</declare-styleable>
</resources>
<!--属性值具体意义详述:
reference:参考某一资源ID
color:颜色值
boolean:布尔值
dimension:尺寸值
float:浮点值
integer:整型值
string:字符串
fraction:百分数
enum:枚举值
flag:位或运算
多类型:
<declare-styleable name = "名称">
<attr name = "background" format = "reference|color" />
</declare-styleable>
-->
2,重写构造方法【1,2,3参数,重写三参数】
获取View属性值,实现基本的布局
/**
* 文本
*/
private String mTitleText;
/**
* 文本的颜色
*/
private int mTitleTextColor;
/**
* 文本的大小
*/
private int mTitleTextSize;
/**
* 绘制时控制文本绘制的范围
*/
private Rect mBound;
/**
* 画笔
*/
private Paint mPaint;
/**
* 构造方法
*
* @param context
* @param attrs
*/
public CustomTitleView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
/**
* 构造方法
*
* @param context
*/
public CustomTitleView(Context context) {
this(context, null);
}
/**
* 获得我自定义的样式属性
*
* @param context
* @param attrs
* @param defStyle
*/
public CustomTitleView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
/**
* 添加事件【<span style="color:#333399;">第五步时,把这一块代码加入</span>】
*/
this.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mTitleText = randomText();
postInvalidate();
}
});
/**
* 获得我们所定义的自定义样式属性
*/
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomTitleView, defStyle, 0);
int n = a.getIndexCount();
for (int i = 0; i < n; i++) {
int attr = a.getIndex(i);
switch (attr) {
case R.styleable.CustomTitleView_titleText:
mTitleText = a.getString(attr);
break;
case R.styleable.CustomTitleView_titleTextColor:
// 默认颜色设置为黑色
mTitleTextColor = a.getColor(attr, Color.BLACK);
break;
case R.styleable.CustomTitleView_titleTextSize:
// 默认设置为16sp,TypeValue也可以把sp转化为px
mTitleTextSize = a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
break;
}
}
a.recycle();
/**
* 获得绘制文本的宽和高
*/
mPaint = new Paint();
mPaint.setTextSize(mTitleTextSize);
mPaint.setColor(mTitleTextColor);
mBound = new Rect();
mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound);
}
3,重写onDraw()方法,绘制View
paint绘制view到canvas上【将view用笔绘制到画布上】
@Override
protected void onDraw(Canvas canvas) {
mPaint.setColor(Color.YELLOW);
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);
mPaint.setColor(mTitleTextColor);
canvas.drawText(mTitleText, getWidth() / 2 - mBound.width() / 2, getHeight() / 2 + mBound.height() / 2, mPaint);
}
4,重写onMeasure()方法
实现将所有View重新布局【摆放】
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
/**
* 1,不重写该方法,系统默认填充父窗体
*/
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
/**
* 2, 重写当前方法
*/
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width;
int height;
/**
* 宽度获取
*/
if (widthMode == MeasureSpec.EXACTLY) {
width = widthSize;
} else {
mPaint.setTextSize(mTitleTextSize);
mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound);
float textWidth = mBound.width();
int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight());
width = desired;
}
/**
* 高度获取
*/
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
} else {
mPaint.setTextSize(mTitleTextSize);
mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound);
float textHeight = mBound.height();
int desired = (int) (getPaddingTop() + textHeight + getPaddingBottom());
height = desired;
}
setMeasuredDimension(width, height);
}
5,添加控件的响应事件
添加事件触发器
实现事件的具体响应
/**
* 生成随机文字【也可以实现其他的事件,管理控件属性】
*
* @return
*/
private String randomText() {
Random random = new Random();
Set<Integer> set = new HashSet<Integer>();
while (set.size() < 4) {
int randomInt = random.nextInt(10);
set.add(randomInt);
}
StringBuffer sb = new StringBuffer();
for (Integer i : set) {
sb.append("" + i);
}
return sb.toString();
}
在使用命名空间时:
xmlns:test="
http://schemas.android.com/apk/res-auto" 在AS中使用
xmlns:test="
http://schemas.android.com/apk/res/[你的包名]"
http://www.imooc.com/video/8302 慕课网【推荐学习】
二、自定义控件(二) --- 属性TextView联合图片
自定义控件的基本步骤:
1、自定义View的属性
2、在View的构造方法中获得我们自定义的属性
[ 3、重写onMesure ] //可选项
4、重写onDraw
2、在View的构造方法中获得我们自定义的属性
[ 3、重写onMesure ] //可选项
4、重写onDraw
在绘制过程中,控制内容大小。
扩展:
为自定义控件添加触发事件;
为自定义控件添加整体响应【事件机制,返回,Home处理】
<!--CustomImageView-->
<attr name="image" format="reference" />
<attr name="imageScaleType">
<enum name="fillXY" value="0" />
<enum name="center" value="1" />
</attr>
<declare-styleable name="CustomImageView">
<attr name="titleText" />
<attr name="titleTextSize" />
<attr name="titleTextColor" />
<attr name="image" />
<attr name="imageScaleType" />
</declare-styleable>
/**
* 类说明:带图片说明的ImageView控件
* 作者:vision
* 时间:2016/7/15
*/
public class CustomImageView extends View {
/**
* 标识图片当前缩放模式
*/
private static final int IMAGE_SCALE_FITXY = 0;
/**
* 文本
*/
private String mTitle;
/**
* 文本的颜色
*/
private int mTextColor;
/**
* 文本的大小
*/
private int mTextSize;
/**
* 缩放参数
*/
private int mImageScale;
/**
* 绘制时控制文本绘制的范围
*/
private Rect mTextBound;
/**
* 绘制整体的范围
*/
private Rect rect;
/**
* 画笔
*/
private Paint mPaint;
/**
* 图像内容
*/
private Bitmap mImage;
/**
* 控件宽度
*/
private int mWidth;
/**
* 控件高度
*/
private int mHeight;
/**
* 响应事件监听器
*/
private CustomImageViewClickListener listener;
/**
* 构造方法
*
* @param context
* @param attrs
*/
public CustomImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
/**
* 构造方法
*
* @param context
*/
public CustomImageView(Context context) {
this(context, null);
}
/**
* 初始化所特有自定义类型
*
* @param context
* @param attrs
* @param defStyle
*/
public CustomImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
if (listener != null) {
listener.onCustomImageViewClickListener(view);
}
}
});
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomImageView, defStyle, 0);
int n = a.getIndexCount();
for (int i = 0; i < n; i++) {
int attr = a.getIndex(i);
switch (attr) {
case R.styleable.CustomImageView_image:
mImage = BitmapFactory.decodeResource(getResources(), a.getResourceId(attr, 0));
break;
case R.styleable.CustomImageView_imageScaleType:
mImageScale = a.getInt(attr, 0);
break;
case R.styleable.CustomImageView_titleText:
mTitle = a.getString(attr);
break;
case R.styleable.CustomImageView_titleTextColor:
mTextColor = a.getColor(attr, Color.BLACK);
break;
case R.styleable.CustomImageView_titleTextSize:
mTextSize = a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
16, getResources().getDisplayMetrics()));
break;
}
}
a.recycle();
rect = new Rect();
mPaint = new Paint();
mTextBound = new Rect();
mPaint.setTextSize(mTextSize);
// 计算了描绘字体需要的范围
mPaint.getTextBounds(mTitle, 0, mTitle.length(), mTextBound);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
/**
* 设置宽度
*/
int specMode = MeasureSpec.getMode(widthMeasureSpec);
int specSize = MeasureSpec.getSize(widthMeasureSpec);
if (specMode == MeasureSpec.EXACTLY) {// match_parent , accurate
Log.e("xxx", "EXACTLY");
mWidth = specSize;
} else {
// 由图片决定的宽
int desireByImg = getPaddingLeft() + getPaddingRight() + mImage.getWidth();
// 由字体决定的宽
int desireByTitle = getPaddingLeft() + getPaddingRight() + mTextBound.width();
if (specMode == MeasureSpec.AT_MOST) {// wrap_content
int desire = Math.max(desireByImg, desireByTitle);
mWidth = Math.min(desire, specSize);
Log.e("xxx", "AT_MOST");
}
}
/***
* 设置高度
*/
specMode = MeasureSpec.getMode(heightMeasureSpec);
specSize = MeasureSpec.getSize(heightMeasureSpec);
if (specMode == MeasureSpec.EXACTLY) {// match_parent , accurate
mHeight = specSize;
} else {
int desire = getPaddingTop() + getPaddingBottom() + mImage.getHeight() + mTextBound.height();
if (specMode == MeasureSpec.AT_MOST) {// wrap_content
mHeight = Math.min(desire, specSize);
}
}
setMeasuredDimension(mWidth, mHeight);
}
@Override
protected void onDraw(Canvas canvas) {
// super.onDraw(canvas);
/**
* 边框
*/
mPaint.setStrokeWidth(4);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.CYAN);
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);
rect.left = getPaddingLeft();
rect.right = mWidth - getPaddingRight();
rect.top = getPaddingTop();
rect.bottom = mHeight - getPaddingBottom();
mPaint.setColor(mTextColor);
mPaint.setStyle(Paint.Style.FILL);
/**
* 当前设置的宽度小于字体需要的宽度,将字体改为xxx...
*/
if (mTextBound.width() > mWidth) {
TextPaint paint = new TextPaint(mPaint);
String msg = TextUtils.ellipsize(mTitle, paint, (float) mWidth - getPaddingLeft() - getPaddingRight(),
TextUtils.TruncateAt.END).toString();
canvas.drawText(msg, getPaddingLeft(), mHeight - getPaddingBottom(), mPaint);
} else {
//正常情况,将字体居中
canvas.drawText(mTitle, mWidth / 2 - mTextBound.width() * 1.0f / 2, mHeight - getPaddingBottom(), mPaint);
}
//取消使用掉的快
rect.bottom -= mTextBound.height();
if (mImageScale == IMAGE_SCALE_FITXY) {
canvas.drawBitmap(mImage, null, rect, mPaint);
} else {
//计算居中的矩形范围
rect.left = mWidth / 2 - mImage.getWidth() / 2;
rect.right = mWidth / 2 + mImage.getWidth() / 2;
rect.top = (mHeight - mTextBound.height()) / 2 - mImage.getHeight() / 2;
rect.bottom = (mHeight - mTextBound.height()) / 2 + mImage.getHeight() / 2;
canvas.drawBitmap(mImage, null, rect, mPaint);
}
}
/**
* 设置监听器
*
* @param listener
*/
public void setListener(CustomImageViewClickListener listener) {
this.listener = listener;
}
public interface CustomImageViewClickListener {
void onCustomImageViewClickListener(View v);
}
}
三、使用自定义控件
<future.com.selfdefineviewfirst.view.CustomTitleView
android:id="@+id/text_self_define"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:titleText="自定义控件属性:标题文字"
app:titleTextColor="@color/green"
app:titleTextSize="18sp" />
<future.com.selfdefineviewfirst.view.CustomImageView
android:id="@+id/pengyuyan1"
android:layout_width="150dp"
android:layout_height="wrap_content"
selfde:image="@mipmap/a7"
selfde:imageScaleType="center"
selfde:titleText="彭于晏最棒最帅最好的潜力股"
selfde:titleTextColor="#0000ff"
selfde:titleTextSize="30sp" />
public class MainActivity extends AppCompatActivity implements CustomView.CustomListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((CustomView) this.findViewById(R.id.custom1)).setCustomListener(this);
}
@Override
public void onCuscomClick(View v, int custom_id) {
switch (custom_id) {
case 1:
Toast.makeText(this, "你点我干嘛?!", Toast.LENGTH_LONG).show();
break;
default:
break;
}
}
/**
* 类说明:CustomImageView 展示页面
* 增加:控件点击事件
* <p/>
* 作者:vision
* 时间:2016/7/15
*/
public class CustomImageViewActivity extends Activity implements CustomImageView.CustomImageViewClickListener {
/**
* 第一个控件
*/
private CustomImageView pengyuyan1;
/**
* 第二个控件
*/
private CustomImageView pengyuyan2;
/**
* 第三个控件
*/
private CustomImageView pengyuyan3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_custom_image_view);
pengyuyan1 = (CustomImageView) findViewById(R.id.pengyuyan1);
pengyuyan2 = (CustomImageView) findViewById(R.id.pengyuyan2);
pengyuyan3 = (CustomImageView) findViewById(R.id.pengyuyan3);
pengyuyan1.setListener(this);
pengyuyan2.setListener(this);
pengyuyan3.setListener(this);
}
@Override
public void onCustomImageViewClickListener(View v) {
switch (v.getId()) {
case R.id.pengyuyan1:
Toast.makeText(this, "帅哥彭于晏1", Toast.LENGTH_LONG).show();
break;
case R.id.pengyuyan2:
Toast.makeText(this, "德艺双馨彭于晏2", Toast.LENGTH_LONG).show();
break;
case R.id.pengyuyan3:
Toast.makeText(this, "超越自己彭于晏3", Toast.LENGTH_LONG).show();
break;
}
}
}
四、相关类似功能的扩展
实现加载过程进度显示
TextView的背景修改自定义控件
效果如图:
这是源码哦
有些时候,我们自以为我们不配得到的,其实是我们错过的;而配不上我们的,还放肆的将我们辜负了。