Android自定義View
參考:
從此再有不愁自定義View——Android自定義view詳解
android View繪制源碼分析(上)
android View繪制源碼分析(下)
1.介紹
自定義一個View有以下幾個步驟:Measure飞蹂,Layout屯远,Draw券盅。
一個說明這個view的大小昔脯,一個定制位置珊肃,一個繪制這個view
2.Measure
measure的方法如下:
void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
有兩個參數(shù),一個是寬度參數(shù),一個是高度參數(shù)。
一個int是4個字節(jié)珍特,在measureapec中,使用前2位表示mode魔吐,后面30位表示size扎筒。
mode有三種:EXACTLY
, AT_MOST
,UNSPECIFIED
酬姆。當父布局是EXACTLY
時嗜桌,子控件確定大小或者match_parent
,mode都是EXACTLY
辞色,子控件是wrap_content
時骨宠,mode為AT_MOST
;當父布局是AT_MOST
時淫僻,子控件確定大小诱篷,mode為EXACTLY
,子控件wrap_content
或者match_parent
時雳灵,mode為AT_MOST
。
可以使用如下方法來看采用的模式和size大小
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int size = 1111;
int measureWidth = widthMode==MeasureSpec.EXACTLY ? widthSize : size;
int measureHeight = heightMode==MeasureSpec.EXACTLY ? heightSize : size;
setMeasuredDimension(measureWidth,measureHeight);
}
3.自定義屬性
(1)先在res/values/下建立xml文件闸盔,比如:attrs.xml悯辙,在里面自定義屬性,比如這里background_color和size
<resources>
<declare-styleable name="MyView">
<attr name="background_color" format="color"/>
<attr name="size" format="dimension"/>
</declare-styleable>
</resources>
(2)在布局文件xml中將這個自定義屬性用上:
<com.microsoft.hond.fortest.MyView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginRight="16dp"
android:layout_marginTop="16dp"
app:background_color="@color/colorPrimary"
app:size="24dp"
/>
現(xiàn)在已經(jīng)用上了,但是還不知道這個屬性是干嘛用的躲撰,因此需要在代碼中把這兩個屬性的值拿出來针贬,然后把其值用到真正的屬性中去。
(3)在自定義的View中拢蛋,比如這里的MyView.java桦他,有三種構(gòu)造方法,采用第三種構(gòu)造谆棱,即有三個輸入?yún)?shù)的構(gòu)造方法快压。
public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// TODO Auto-generated constructor stub
TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.MyView,defStyleAttr,R.style.AppTheme);
customer_size = a.getDimensionPixelSize(R.styleable.MyView_size,size);
customer_background = a.getColor(R.styleable.MyView_background_color, Color.RED);
a.recycle();
}
先用obtainStyledAttributes
取得xml中屬性的名字,然后使用get方法獲得各自的屬性的值垃瞧,最后調(diào)用recycle
方法
4. Paint
在onDraw中最主要的就是使用paint畫圖了蔫劣。首先要知道的是measure和layout為draw提供了一個入?yún)anvas,就是畫布个从,告訴開發(fā)者在這個地方這塊布上進行畫畫脉幢。而這時候需要掏出一支筆,是紅筆藍筆嗦锐,還是粗的筆細的筆嫌松,就需要使用Paint類去定義了。
比如下面這個Paint定義
private void initPaint() {
bgPaint = new Paint();
bgPaint.setColor(bgPaintColor);
bgPaint.setAntiAlias(true);
bgPaint.setStrokeWidth(paintBold);
//使得畫筆更加圓滑
bgPaint.setStrokeJoin(Paint.Join.ROUND);
bgPaint.setStrokeCap(Paint.Cap.ROUND);
bfPaint = new Paint();
bfPaint.setColor(beforePaintColor);
bfPaint.setAntiAlias(true);
bfPaint.setStrokeWidth(paintBold);
bfPaint.setStrokeJoin(Paint.Join.ROUND);
bfPaint.setStrokeCap(Paint.Cap.ROUND);
textPaint = new Paint();
textPaint.setColor(textColor);
textPaint.setAntiAlias(true);
textPaint.setTextAlign(Paint.Align.CENTER);
textPaint.setTextSize(40);
}
上面就掏出了三支筆bgPaint奕污,bfPain和textPaint
5.onDraw
onDraw就是當你有了畫布萎羔,有了畫筆,系統(tǒng)為Canvas畫布提供了一系列的畫畫方式菊值,比如你想畫什么外驱,畫圓就用drawCircle,畫線就用drawLine腻窒,里面一般帶上需要的參數(shù)昵宇,比如畫圓就帶上圓心的x和y,還有半徑儿子。另外每一個方法都要帶上畫筆paint瓦哎。
6.插入屬性動畫ValueAnimator
和ObjectAnimator
先定義一個ValueAnimator
,然后對其屬性進行配置
public void startAnimation() {
mAnimator = ValueAnimator.ofFloat(1, 2);
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
scale = (float) animation.getAnimatedValue();
postInvalidate();
}
});
// 重復次數(shù) -1表示無限循環(huán)
mAnimator.setRepeatCount(-1);
//重復周期
mAnimator.setDuration(1000);
// 重復模式, RESTART: 重新開始 REVERSE:恢復初始狀態(tài)再開始
mAnimator.setRepeatMode(ValueAnimator.REVERSE);
mAnimator.start();
}
通過調(diào)整scale的大小來調(diào)整園的大小柔逼,因此可以看到每一秒圓的半徑就會放大一倍然后變成原先狀態(tài)蒋譬。
ObjectAnimator
是對ValueAnimator
的繼承,可以對某一個view對象而不是一個value進行操作愉适,比如可以直接對一個textView操作犯助,不需要通過值來改變。
ObjectAnimator
對于大部分的來說已經(jīng)夠用了(如果不是自己繪制的view)维咸,畢竟大部分的view可以輸入進來直接記性變換剂买,而只有一些很強的定制的view需要通過值來改變惠爽。
使用:
private void setAnimal() {
ObjectAnimator animator = ObjectAnimator.ofFloat(mTextView, "alpha", 1f, 0f, 1f);
animator.setDuration(5000);
animator.setRepeatCount(-1);
// 重復模式, RESTART: 重新開始 REVERSE:恢復初始狀態(tài)再開始
animator.setRepeatMode(ValueAnimator.REVERSE);
animator.start();
}
比如上面代碼,就通過改變一個textView的alpha屬性瞬哼,使其透明度不斷變化婚肆。達到了動畫的要求。
總結(jié):簡單的有對象的(如textView等等)直接使用ObjectAnimator
改變屬性值坐慰,自己定制的较性,使用ValueAnimator
,通過改變值來改變繪制结胀。
7.View的點擊事件
通過覆寫onTouch
達到目的
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: //按下
setDefault();
handleDown(x, y);
return true;
case MotionEvent.ACTION_UP: //彈起
type = 1;//彈起刷新
invalidate();//刷新界面
//一次按下結(jié)束,返回點擊的數(shù)字
if (onNumberClickListener != null) {
if (number != null) {
if (number.equals("delete")) {
onNumberClickListener.onNumberDelete();
} else {
onNumberClickListener.onNumberReturn(number);
}
}
}
//恢復默認
setDefault();
return true;
case MotionEvent.ACTION_CANCEL: //取消
//恢復默認值
setDefault();
return true;
}
return false;
上面是一個計算器view的點擊事件赞咙,先獲取點擊的位置x和y,然后判斷點擊的屬性把跨,是按下人弓,還是松開,還是移動着逐,分別對應(yīng)不同的動作崔赌。