注意点
- 绘制View的显示内容,需要重写
onDraw()
方法 - 该View若要使用
wrap_content
属性,必须重写onMeasure()
方法 - 通过自定义
attrs
属性,还可以设置新的属性配置值
View中重要的回调方法
- ++onFinishInflate()++: 从XML加载组件后回调
- ++onSizeChanged()++:组件大小改变时
- onMeasure():进行测量
- onLayout(): 确定显示的位置
- onTouchEvent(): 监听触摸事件时回调
自定义控件三大方法
一、现有控件进行扩展
比如扩展TextView
思路如下:
@Override
protected void onDraw(Canvas canvas) {
//回调父类方法前,实现自己的逻辑,对TextView来说在“绘制文本”内容之前
super.onDraw(canvas);
//回调父类方法后,实现自己的逻辑,对TextView来说在“绘制文本”内容之后
}
1.利用画笔绘制矩形框
public class MyTextView extends TextView {
public MyTextView(Context context) {
super(context);
}
public MyTextView(Context context,AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
//回调父类方法前,实现自己的逻辑,对TextView来说在“绘制文本”内容之前
Paint mPaint1 = new Paint();
mPaint1.setColor(ContextCompat.getColor(getContext(), R.color.colorAccent));
mPaint1.setStyle(Paint.Style.FILL);
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint1);
canvas.save();
//绘制文字前平移10像素(px)
canvas.translate(10, 0);
super.onDraw(canvas);
canvas.restore();
}
}
2.LinearGradient Shader
和Matrix
实现动态的文字闪动效果
需要onDraw()
和onSizeChanged()
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(mMatrix != null){
mTranslate += mViewWidth/5;
if(mTranslate > 2*mViewWidth){
mTranslate = -mViewWidth;
}
mMatrix.setTranslate(mTranslate, 0);
mLinearGradient.setLocalMatrix(mMatrix);
postInvalidateDelayed(100);
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if(mViewWidth == 0){
mViewWidth = getMeasuredWidth();
if(mViewWidth > 0){
mPaint = getPaint();
mLinearGradient = new LinearGradient(
0,
0,
mViewWidth,
0,
new int[]{
Color.BLUE, 0xffffffff,
Color.BLUE},
null,
Shader.TileMode.CLAMP);
mPaint.setShader(mLinearGradient);
mMatrix = new Matrix();
}
}
}
二、复合控件
简单的组合控件:
链接:http://blog.csdn.net/yelangjueqi/article/details/8879340
1.定义属性
为一个View提供自定义的属性,只需要在res
的values
目录下创建一个attrs.xml
的属性定义文件,代码参考下面:
使用UI模板需要引用第三方控件的名字空间
* 下列引用android的系统属性
xmlns:android="http://schemas.android.com/apk/res/android"
- 第三方的控件都使用如下代码来引用名字空间
xmlns:app="http://schemas.android.com/apk/res-auto"
android studio需要使用xmlns:app
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="TopBar">
<attr name="Title" format="string"/>
<attr name="TitleTextSize" format="dimension"/>
<attr name="TitleTextColor" format="color"/>
<attr name="RightText" format="string"/>
<attr name="RightTextSize" format="dimension"/>
<attr name="RightTextColor" format="color"/>
<attr name="LeftImageSize" format="integer"/>
</declare-styleable>
</resources>
2.自定义控件类
package com.example.lenovo.qqdemos.CustomView;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.example.lenovo.qqdemos.R;
/**
* Created by feather on 2016/8/19.
*/
public class TopBar extends LinearLayout{
private ImageView mHeadImage;
private TextView mTitleText;
private TextView mRightText;
//标题
String mTitle;
int mTitleTextColor;
float mTitleTextSize;
//左侧头像
float mHeadImageSize;
//右侧文本
String mRightTextStr;
int mRightTextColor;
float mRightTextSize;
//接口
topbarClickListener mListener;
public TopBar(Context context, AttributeSet attrs) {
super(context, attrs);
//注意参数: root=this, 及 attachToRo
LayoutInflater.from(context).inflate(R.layout.feather_topbar, this, true);
mHeadImage = (ImageView) findViewById(R.id.topbar_head_image);
mTitleText = (TextView) findViewById(R.id.topbar_title);
mRightText = (TextView) findViewById(R.id.topbar_right_text);
//将在 atts.xml中定义的 declare-styleable的所有属性的值存储到 TypedArray中
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TopBar);
/*-----------------------------------------------------------
* 标题-根据属性初始化
* ----------------------------------------------------------*/
String mTitle = typedArray.getString(R.styleable.TopBar_Title);
int mTitleTextColor = typedArray.getColor(R.styleable.TopBar_TitleTextColor, Color.WHITE);
float mTitleTextSize = typedArray.getDimension(R.styleable.TopBar_TitleTextSize, 20);
if(mTitle != null) mTitleText.setText(mTitle);
mTitleText.setTextColor(mTitleTextColor);
mTitleText.setTextSize(mTitleTextSize);
/*-----------------------------------------------------------
* 左侧头像-根据属性初始化
* ----------------------------------------------------------*/
int mHeadImageSize = typedArray.getInt(R.styleable.TopBar_LeftImageSize, 20);
mHeadImage.setMaxWidth(mHeadImageSize);
mHeadImage.setMaxHeight(mHeadImageSize);
//监听器
mHeadImage.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mListener.leftImageClick();
}
});
/*-----------------------------------------------------------
* 右侧文本-根据属性初始化
* ----------------------------------------------------------*/
String mRightTextStr = typedArray.getString(R.styleable.TopBar_RightText);
int mRightTextColor = typedArray.getColor(R.styleable.TopBar_RightTextColor, Color.WHITE);
float mRightTextSize = typedArray.getDimension(R.styleable.TopBar_RightTextSize, 20);
if(mRightTextStr != null) mRightText.setText(mRightTextStr);
mRightText.setTextColor(mRightTextColor);
mRightText.setTextSize(mRightTextSize);
//监听器
mRightText.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mListener.rightTextClick();
}
});
}
public interface topbarClickListener{
void leftImageClick();
void rightTextClick();
}
}
1.其中使用属性的办法
//将在 atts.xml中定义的 declare-styleable的所有属性的值存储到 TypedArray中
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TopBar);
/*-----------------------------------------------------------
* 根据属性初始化
* ----------------------------------------------------------*/
String mTitle = typedArray.getString(R.styleable.TopBar_Title);
int mTitleTextColor = typedArray.getColor(R.styleable.TopBar_TitleTextColor, Color.WHITE);
float mTitleTextSize = typedArray.getDimension(R.styleable.TopBar_TitleTextSize, 20);
int mHeadImageSize = typedArray.getInt(R.styleable.TopBar_LeftImageSize, 20);
String mRightTextStr = typedArray.getString(R.styleable.TopBar_RightText);
int mRightTextColor = typedArray.getColor(R.styleable.TopBar_RightTextColor, Color.WHITE);
float mRightTextSize = typedArray.getDimension(R.styleable.TopBar_RightTextSize, 20);
//避免重新创建时的错误
typedArray.recycle();
2.该控件的布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorBlue"
android:orientation="vertical" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#00000000">
<com.pkmmte.view.CircularImageView
android:id="@+id/topbar_head_image"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/default_user_head"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:visibility="invisible"
>
</com.pkmmte.view.CircularImageView>
<TextView
android:id="@+id/topbar_title"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="标题"
android:textSize="20dp"
android:gravity="center"
android:textColor="@color/white"
android:layout_centerInParent="true"
android:visibility="invisible"/>
<TextView
android:id="@+id/topbar_right_text"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:text="编辑"
android:textSize="20dp"
android:layout_alignParentRight="true"
android:layout_marginRight="4dp"
android:textColor="@color/white"
android:gravity="center"
android:visibility="invisible"/>
</RelativeLayout>
</LinearLayout>
3.使用该控件
- 可以通过
xmlns:app="http://schemas.android.com/apk/res-auto"
使用自定义属性,如下面app:Title
部分
<com.example.lenovo.qqdemos.CustomView.TopBar
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/chat_menu_topbar"
android:layout_width="match_parent"
android:layout_height="55dp"
app:Title="hello">
</com.example.lenovo.qqdemos.CustomView.TopBar>
错误提示
1.android.view.InflateException: Binary XML file line #9: Error inflating class com.feather.past.MyTextView
2.java.lang.NoSuchMethodException: [class android.content.Context, interface android.util.AttributeSet]
1.报错原因:
在自定义view时,没有重写含有(Context context,AttributeSet attrs)的构造器
解决办法:
重新对于构造器,例如:
public ControlKeyboardLinearLayout(Context context,AttributeSet attrs){
super(context, attrs);
}
2.报错原因:
自定义view为内部类时,没有将内部类设置为static,例如:
public class a{
public class b extents TextVIew{
...
}
}
解决办法:
为内部类加上static关键字,例如:
public class a{
public static class b extents TextVIew{
...
}
}