自定义控件的步骤:
首先写一个类,就是给控件起个名字
要在res/values目录下建立attrs.xml文件,写下需要定义的属性
在自定义类,包含AttributeSet参数的构造方法中,关联自定义属性
将自定义的控件类放在布局文件中
在视图类中使用
正文内容如下:
1、继承View使用canvas绘制实例,自定义一个TextView
public class CustomTextView extends View{
private Paint mPaint;//画笔
private int backColor;//背景色
private int textColor;//文字颜色
private float textSize;//文字大小
private String textContent;//文字内容
public CustomTextView(Context context) {
super(context);
}
//关联自定义属性
public CustomTextView(Context context,AttributeSet attr) {
super(context);
TypedArray array = context.obtainStyledAttributes(attr, R.styleable.CustomTextView);
textColor = array.getColor(R.styleable.CustomTextView_textColor, 0X000000);
backColor = array.getColor(R.styleable.CustomTextView_backColor, 0XFFFFFF);
textSize = array.getDimension(R.styleable.CustomTextView_textSize, 32);
textContent = array.getString(R.styleable.CustomTextView_textContent);
array.recycle();
}
//开始绘画
@SuppressLint("DrawAllocation")
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint = new Paint();
mPaint.setStyle(Style.FILL);//填充方式
mPaint.setTextSize(textSize);
mPaint.setColor(backColor);
canvas.drawRect(new Rect(10,10,200,100), mPaint);
mPaint.setColor(textColor);
canvas.drawText(textContent, 20, 60, mPaint);
}
}attrs.xml文件内容如下:
<declare-styleable name="CustomTextView"> <attr name="backColor" format="color" /> <attr name="textColor" format="color" /> <attr name="textSize" format="dimension" /> <attr name="textContent" format="string" /> </declare-styleable>
2、继承ImageView实例,自定义一个圆形图片,适合做头像用
类,属性,构造方法如下:
public class RoundImageView extends ImageView
private int mBorderThickness = 0;//边框厚度
private int mBorderOutsideColor = 0;//外边框颜色
private int mBorderInsideColor = 0;//内边框颜色
private int defaultColor = 0xFFFFFF;//默认使用颜色
private int defaultWidth = 0;//图片宽度
private int defaultHeight = 0;//图片高度
public RoundImageView(Context context) {
super(context);
}
public RoundImageView(Context context, AttributeSet attrs) {
super(context, attrs);
setCustomAttributes(context, attrs);
}
public RoundImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setCustomAttributes(context, attrs);
}
private void setCustomAttributes(Context context, AttributeSet attrs) {
TypedArray array = context.obtainStyledAttributes(attrs,
R.styleable.roundedimageview);
mBorderThickness = array.getDimensionPixelSize(
R.styleable.roundedimageview_border_thickness, 0);
mBorderOutsideColor = array
.getColor(R.styleable.roundedimageview_border_outside_color,
defaultColor);
mBorderInsideColor = array.getColor(
R.styleable.roundedimageview_border_inside_color, defaultColor);
array.recycle();
}边缘画圆方法:
private void drawCircleBorder(Canvas canvas, int radius, int color) {
Paint paint = new Paint();
/* 去锯齿 */
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
paint.setDither(true);
paint.setColor(color);
/* 设置paint的 style 为STROKE:空心 */
paint.setStyle(Paint.Style.STROKE);
/* 设置paint的外框宽度 */
paint.setStrokeWidth(mBorderThickness);
canvas.drawCircle(defaultWidth / 2, defaultHeight / 2, radius, paint);
}主要的绘制方法,获取裁剪圆形图片方法
@Override
protected void onDraw(Canvas canvas) {
Drawable drawable = getDrawable();
if (drawable == null) {
return;
}
if (getWidth() == 0 || getHeight() == 0) {
return;
}
this.measure(0, 0);
if (drawable.getClass() == NinePatchDrawable.class)
return;
Bitmap b = ((BitmapDrawable) drawable).getBitmap();
Bitmap bitmap = b.copy(Bitmap.Config.ARGB_8888, true);
if (defaultWidth == 0) {
defaultWidth = getWidth();
}
if (defaultHeight == 0) {
defaultHeight = getHeight();
}
int radius = 0;
if (mBorderInsideColor != defaultColor
&& mBorderOutsideColor != defaultColor) {
// 定义画两个边框,分别为外圆边框和内圆边框
radius = (defaultWidth < defaultHeight ? defaultWidth
: defaultHeight) / 2 - 2 * mBorderThickness;
// 画内圆
drawCircleBorder(canvas, radius + mBorderThickness / 2,
mBorderInsideColor);
// 画外圆
drawCircleBorder(canvas, radius + mBorderThickness
+ mBorderThickness / 2, mBorderOutsideColor);
} else if (mBorderInsideColor != defaultColor
&& mBorderOutsideColor == defaultColor) {
// 定义画一个边框
radius = (defaultWidth < defaultHeight ? defaultWidth
: defaultHeight) / 2 - mBorderThickness;
drawCircleBorder(canvas, radius + mBorderThickness / 2,
mBorderInsideColor);
} else if (mBorderInsideColor == defaultColor
&& mBorderOutsideColor != defaultColor) {
// 定义画一个边框
radius = (defaultWidth < defaultHeight ? defaultWidth
: defaultHeight) / 2 - mBorderThickness;
drawCircleBorder(canvas, radius + mBorderThickness / 2,
mBorderOutsideColor);
} else {
// 没有边框
radius = (defaultWidth < defaultHeight ? defaultWidth
: defaultHeight) / 2;
}
Bitmap roundBitmap = getCroppedRoundBitmap(bitmap, radius);
canvas.drawBitmap(roundBitmap, defaultWidth / 2 - radius, defaultHeight
/ 2 - radius, null);
}
/**
* 获取裁剪后的圆形图片
*/
public Bitmap getCroppedRoundBitmap(Bitmap bmp, int radius) {
Bitmap scaledSrcBmp;
int diameter = radius * 2;
// 为了防止宽高不相等,造成圆形图片变形,因此截取长方形中处于中间位置最大的正方形图片
int bmpWidth = bmp.getWidth();
int bmpHeight = bmp.getHeight();
int squareWidth = 0, squareHeight = 0;
int x = 0, y = 0;
Bitmap squareBitmap;
if (bmpHeight > bmpWidth) {// 高大于宽
squareWidth = squareHeight = bmpWidth;
x = 0;
y = (bmpHeight - bmpWidth) / 2;
// 截取正方形图片
squareBitmap = Bitmap.createBitmap(bmp, x, y, squareWidth,
squareHeight);
} else if (bmpHeight < bmpWidth) {// 宽大于高
squareWidth = squareHeight = bmpHeight;
x = (bmpWidth - bmpHeight) / 2;
y = 0;
squareBitmap = Bitmap.createBitmap(bmp, x, y, squareWidth,
squareHeight);
} else {
squareBitmap = bmp;
}
if (squareBitmap.getWidth() != diameter
|| squareBitmap.getHeight() != diameter) {
scaledSrcBmp = Bitmap.createScaledBitmap(squareBitmap, diameter,
diameter, true);
} else {
scaledSrcBmp = squareBitmap;
}
Bitmap output = Bitmap.createBitmap(scaledSrcBmp.getWidth(),
scaledSrcBmp.getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(output);
Paint paint = new Paint();
Rect rect = new Rect(0, 0, scaledSrcBmp.getWidth(),
scaledSrcBmp.getHeight());
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
paint.setDither(true);
canvas.drawARGB(0, 0, 0, 0);
canvas.drawCircle(scaledSrcBmp.getWidth() / 2,
scaledSrcBmp.getHeight() / 2, scaledSrcBmp.getWidth() / 2,
paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(scaledSrcBmp, rect, rect, paint);
bmp = null;
squareBitmap = null;
scaledSrcBmp = null;
return output;
}attrs.xml文件内容如下:
<declare-styleable name="roundedimageview"> <attr name="border_thickness" format="dimension" /> <attr name="border_inside_color" format="color" /> <attr name="border_outside_color" format="color"></attr> </declare-styleable>
3、继承ViewGroup,实现简单滑动侧边栏菜单
public class SlideMenuView extends ViewGroup {
private Scroller scroller;//滑动器
private final int MENU = 0;//显示菜单标识
private final int MAIN = 1;//显示主页标识
private int startx;//起始X位置
private int currentScreen = MENU;//当前Screen
public SlideMenuView(Context context, AttributeSet attrs) {
super(context, attrs);
scroller = new Scroller(context);
}
// 测量子view
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
View menu = getChildAt(0);
menu.measure(menu.getLayoutParams().width, heightMeasureSpec);
View main = getChildAt(1);
main.measure(widthMeasureSpec, heightMeasureSpec);
}
// 将子view进行布局
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
View menu = getChildAt(0);
menu.layout(-menu.getLayoutParams().width, t, 0, b);
View main = getChildAt(1);
main.layout(l, t, r, b);
}
// 触摸事件
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startx = (int) event.getX();// 记录手指按下时,点到屏幕左边的距离
break;
case MotionEvent.ACTION_MOVE:
int movex = (int) event.getX();// 移动后,手指点到屏幕左边的距离
int diffx = startx - movex;// 屏幕左边的偏移量
int newscrollx = getScrollX() + diffx;// 偏移后
if (newscrollx > 0) {
scrollTo(0, 0);// 如果屏幕左边超过了主界面左边,那么让屏幕左边与主界面重合
} else if (newscrollx < -getChildAt(0).getWidth()) {
scrollTo(-getChildAt(0).getWidth(), 0);// 如果屏幕左边超过了侧边栏左边,那么让屏幕左边与侧边栏左边重合
}
scrollBy(diffx, 0);// 持续偏移
startx = movex;
break;
case MotionEvent.ACTION_UP:
int scrollx = getScrollX();// 屏幕左边距离主界面左边的距离,屏幕左边在主界面左边的左边,为负值
if (scrollx > -getChildAt(0).getWidth() / 2) {
currentScreen = MAIN;// 拖动屏幕不到侧边栏的一半时,放手,显示主界面
switchScreen();
} else if (scrollx < -getChildAt(0).getWidth() / 2) {
currentScreen = MENU;// 拖动屏幕超过了侧边栏的一般,放手,显示侧边栏
switchScreen();
}
break;
default:
break;
}
return true;
}
// 切换显示侧边栏和主界面
private void switchScreen() {
int dx = 0;
// 获得屏幕左边距离主界面左边的距离
int startX = getScrollX();
if (currentScreen == MAIN) {
// 目标是将屏幕左边与主界面左边重合
dx = 0 - getScrollX();
} else if (currentScreen == MENU) {
// 目标是将屏幕左边与侧边栏的左边重合
dx = -getChildAt(0).getWidth() - getScrollX();
}
scroller.startScroll(startX, 0, dx, 0, Math.abs(dx) * 5);
invalidate();
}
// invalidate()的最终的调用方法就是computeScroll() 因此需要重写该方法
@Override
public void computeScroll() {
if (scroller.computeScrollOffset()) {
scrollTo(scroller.getCurrX(), 0);
invalidate();
}
}
// 判断当前显示的是不是侧边栏
public boolean isMenuShow() {
return currentScreen == MENU;
}
// 隐藏侧边栏
public void hideMenu() {
currentScreen = MAIN;
switchScreen();
}
// 显示侧边栏
public void showMenu() {
currentScreen = MENU;
switchScreen();
}
}4、自定义控件的使用
<com.android.myself.view.SlideMenuView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:myself="http://schemas.android.com/apk/res/com.android.myself" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <LinearLayout android:layout_width="120dp" android:layout_height="match_parent" android:background="#ffd5d1" android:orientation="vertical" > </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <com.android.myself.view.RoundImageView android:layout_width="120dp" android:layout_height="120dp" android:scaleType="centerCrop" android:src="@drawable/jason" myself:border_inside_color="#fff7f2" myself:border_outside_color="#ffd5d1" myself:border_thickness="2dp" /> <com.android.myself.view.CustomTextView android:layout_width="wrap_content" android:layout_height="wrap_content" myself:backColor="#cccccc" myself:textColor="#FFFF00" myself:textContent="自定义" myself:textSize="16sp" /> </LinearLayout> </com.android.myself.view.SlideMenuView>
说明:xmlns:myself="http://schemas.android.com/apk/res/com.android.myself
com.android.myself是工程文件的包名
xmlns:myself是自定义的属性标签,可以随意写如xmlns:XXX,用的时候就是XXX:xxx
截图一张如下:

源码地址如下:http://files.cnblogs.com/files/pear-lemon/MySelf.zip
作者:掌缘生灭