自定义View的属性
在上一章中讲了那么多,这一章开始就进行实战了。首先来一发自定义View属性的demo。
自定义View属性的步骤分为以下3步。
(1) 新建一个attrs.xml文件,在这个资源文件中定义我们需要的属性。
(2)新建一个自定义的View,对其中的的方法进行重写。
(3)在layout中引用这个新的控件。
先进行第一步。在values文件下新建一个attrs.xml文件。
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="testText" format="string"/>
<attr name="testColor" format="color"/>
<attr name="testSize" format="dimension"/>
<attr name="testBackGroudColor" format="color"/>
<declare-styleable name="CustomTitleView">
<attr name="testText" />
<attr name="testColor" />
<attr name="testSize" />
<attr name="testBackGroudColor"/>
</declare-styleable>
</resources>
这里我们定义了一个属性组,包含了文字,文字颜色,文字大小以及控件背景色,且规定了它们的类型分别为string ,color,dimension。
然后在自定义的View中引用这些属性。
public class TestView extends View{
private String testText;//文本
private int testColor;//文本颜色
private int testSize;//文字大小
private int testBackGroudColor;//背景色
private Rect mBound;
private Paint mPaint;
public TestView(Context context) {
this(context, null);
}
public TestView(Context context,AttributeSet attrs) {
this(context, attrs, 0);
}
public TestView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// TODO Auto-generated constructor stub
//构造函数中引用属性
TypedArray ta = context.getTheme().obtainStyledAttributes(attrs,R.styleable.CustomTitleView, defStyleAttr, 0);
int n = ta.getIndexCount();
for (int i = 0; i < n; i++)
{
int attr = ta.getIndex(i);
switch (attr)
{
case R.styleable.CustomTitleView_testText:
testText = ta.getString(attr);
break;
case R.styleable.CustomTitleView_testColor:
testColor = ta.getColor(attr, Color.GRAY);
break;
case R.styleable.CustomTitleView_testSize:
testSize = ta.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
break;
case R.styleable.CustomTitleView_testBackGroudColor:
testBackGroudColor = ta.getColor(attr, Color.BLACK);
}
}
ta.recycle();
mPaint = new Paint();
mPaint.setTextSize(testSize);
mBound = new Rect();
mPaint.getTextBounds(testText, 0, testText.length(), mBound);
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
protected void onDraw(Canvas canvas)
{
mPaint.setColor(testBackGroudColor);
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);
mPaint.setColor(testColor);
canvas.drawText(testText, getWidth() / 2 - mBound.width() / 2, getHeight() / 2 + mBound.height() / 2, mPaint);
}
接下来进行最后一步,在布局中引用自定义的View
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res/com.example.myview"
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"
tools:context="com.example.myview.MainActivity" >
<com.example.myview.TestView
android:layout_width="200dp"
android:layout_height="100dp"
custom:testText="12345"
custom:testColor="#ff0000"
custom:testSize="40sp"
custom:testBackGroudColor="#00ff00"/>
</RelativeLayout>
注意命名控件的引用xmlns:custom=”http://schemas.android.com/apk/res/com.example.myview”。
具体代码将传到github上,效果可自行下载后查看。
自定可以随意拖动的滚动View
这个Demo中包含了View的滚动和点击事件。涉及到了View自定义中的onTouchEvent onMeasure等。
先初始化一个文本
protected void init() {
mPaint = new Paint();
mPaint.setColor(Color.GREEN);
mPaint.setTextSize(80);
Rect rect = new Rect();
mPaint.getTextBounds(text[0], 0, text[0].length(), rect);
textWidth = rect.width();
textHeight = rect.height();
x = getLeft() + getPaddingLeft();
startY = getTop() + textHeight + getPaddingTop() - 7;// 文字基线 需要调整
Log.d("hk", "textHeight startY = "+textHeight+" "+startY);
endY = startY + textHeight + getPaddingBottom();
Log.d("hk", "endY getPaddingBottom() "+endY+" "+getPaddingBottom());
nextStartY = getTop() - 7;
firstY = startY;
secondY = nextStartY;
}
在对这个文本的大小进行测量
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = measureWidth(widthMeasureSpec);
int height = measureHeight(heightMeasureSpec);
setMeasuredDimension(width, height);
}
private int measureHeight(int heightMeasureSpec) {
int result = 0;
int size = MeasureSpec.getSize(heightMeasureSpec);
int mode = MeasureSpec.getMode(heightMeasureSpec);
if (mode == MeasureSpec.EXACTLY) {
result = size;
} else {
result = (int) (getPaddingTop() + getPaddingBottom() + textHeight);
if (mode == MeasureSpec.AT_MOST) {
result = Math.min(result, size);
}
}
return result;
}
private int measureWidth(int widthMeasureSpec) {
int result = 0;
int size = MeasureSpec.getSize(widthMeasureSpec);
int mode = MeasureSpec.getMode(widthMeasureSpec);
if (mode == MeasureSpec.EXACTLY) {
result = size;
} else {
result = (int) (getPaddingLeft() + getPaddingRight() + textWidth);
if (mode == MeasureSpec.AT_MOST) {
result = Math.min(result, size);
}
}
return result;
}
最后在onDraw中让文字进行滚动。
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Log.d(“HK”, “onDraw”);
//启动滚动
if (!isScrooll) {
mHandler.sendEmptyMessageDelayed(CHANGE_SPEECH, 2000);
isScrooll = true;
}
canvas.drawText(text[0], x, startY, mPaint);
canvas.drawText(text[1], x, nextStartY, mPaint);
startY += speech;
nextStartY += speech;
//超出View的控件时
if (startY > endY || nextStartY > endY) {
if (startY > endY) {
//第一次滚动过后交换值
startY = secondY;
nextStartY = firstY;
} else if (nextStartY > endY) {
//第二次滚动过后交换值
startY = firstY;
nextStartY = secondY;
}
speech = 0;
isScrooll = false;
}
invalidate();//刷新View,View的刷星又会调用到draw ,因此可以循环下去
}
为了使该控件能够接受拖动事件,需要重写onTouchEvent。
public boolean onTouchEvent(MotionEvent event) {
float x;
float y;
x = event.getRawX();
y = event.getRawY();
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.d("HK", "ACTION_DOWN"+x+" "+y);
break;
case MotionEvent.ACTION_MOVE:
int lefttopx = (int) (x-textWidth/2);
int lefttopy = (int) (y-textHeight/2-180);
int rightbottomx = (int) (lefttopx+textWidth);
int rightbottomy = (int) (lefttopy+textHeight);
//if(Math.abs(rightbottomx-lefttopx)>50&&Math.abs(rightbottomy-lefttopy)>20)
layout(lefttopx, lefttopy, rightbottomx, rightbottomy);//重新布局
break;
case MotionEvent.ACTION_UP:
Log.d("HK", "ACTION_UP");
break;
}
return true;
}
这样就可以实现一个可拖动的,切能够自己滚动显示的View。具体代码见github。
GITHUB:
https://github.com/everyhappy/myViews