1 dp,sp,px
- 名詞解釋
- px:像素碟刺,就是屏幕上的點(diǎn)旱易,如圖片的像素大小為32*32偏瓤,這就是指的像素
- dpi:每英寸點(diǎn)數(shù)份乒,即每英寸包括的像素個(gè)數(shù)恕汇,用對(duì)角線上像素點(diǎn)數(shù)/對(duì)角線長(zhǎng)度。
- dp:設(shè)備獨(dú)立像素或辖,與像素密度密切相關(guān)瘾英。在dpi=160的設(shè)備上,1dp=1px.
- sp:相當(dāng)于dp颂暇,常用于文字修飾
- dip:=dp
- 使用
- 常用尺寸大小dp
- 文字尺寸用sp
- 在屏幕上畫(huà)一個(gè)分割線可以用px缺谴,比如1px
- m,h,xh,xxh,xxxh.1.5倍的等比
2 LayoutInflater
將xml文件解析為視圖
- 獲得LayoutInflater實(shí)例的三種方法
LayoutInflater m1 = getLayoutInflater()
LayoutInflater layoutInflater = getSystemService(LATOU_INFLATER_SERVICE)
LayoutInflater layoutInflater = LayoutInflater.from(Context)
- 從源碼來(lái)看,三種方法其實(shí)都是一種耳鸯,就是第二種
/**
* Obtains the LayoutInflater from the given context.
*/
public static LayoutInflater from(Context context) {
LayoutInflater LayoutInflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (LayoutInflater == null) {
throw new AssertionError("LayoutInflater not found.");
}
return LayoutInflater;
}
從上面from()的源碼來(lái)看湿蛔,方法里就調(diào)用了getSystemService()膀曾,所以殊途同歸。
tips:getLayoutInflater()還沒(méi)找到
3 提取布局屬性:theme&style
當(dāng)控件的很多屬性有重合的時(shí)候阳啥,可以把它們相同的屬性提取出來(lái)添谊,這樣可以減少代碼的冗余度。
也可用parent繼承已有的style察迟,在代碼中是這樣引用的
4 如何自定義屬性
深入理解android的自定義屬性斩狱,寫(xiě)的更加易懂,以下參考了這篇博客卷拘。
- 創(chuàng)建屬性
- value下新建資源文件喊废,命名attrs_+類名
-
<declare-styleable><declare-styleable/>
:聲明屬性。這個(gè)標(biāo)簽是必須的嗎栗弟?答案否」す耄可以把這個(gè)去掉乍赫,直接聲明屬性。但是如果沒(méi)有這個(gè)標(biāo)簽陆蟆,有些工作需要我們?nèi)プ隼壮В热纾帉?xiě)常量(屬性的下標(biāo)叠殷,int[]數(shù)組)如改鲫,在R.java中生成的代碼就要我們?nèi)ゾ帉?xiě)
public static final class attr{
public static final int backgroundColor=0x7f0100b3;
}
public static final class styleable {
public static final int[] RedTextButton = {
0x7f0100b3, 0x7f0100b4
};
public static final int RedTextButton_backgroundColor = 0;
public static final int RedTextButton_textSize = 1;
}
所以有了這個(gè)標(biāo)簽,我們就可以只去關(guān)注這些屬性的編寫(xiě)了林束,更加高效像棘。
-
<attr/>
name:名稱;format:設(shè)置類型
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="RedTextButton">
<attr name="backgroundColor" format="color"/>
<attr name="textSize" format="integer"/>
<attr name="text" format="string"/>
</declare-styleable>
</resources>
- 然后在CustomView代碼中獲取這些屬性
public void init(Context context, AttributeSet attrs){
this.setOnClickListener(this);
TypedArray typedArray = context.obtainStyledAttributes(
attrs,R.styleable.RedTextButton);
mBackGroundColor = typedArray.getColor(
R.styleable.RedTextButton_backgroundColor, Color.BLUE);
mText = typedArray.getString(R.styleable.RedTextButton_text);
typedArray.recycle();
//
mTextSize = typedArray.getInteger(R.styleable.RedTextButton_textSize, 18);
}
AttributeSet:參數(shù)的集合壶冒,其實(shí)可以通過(guò)getAttributeName()和getAttributeValue()獲得所有屬性的key和value缕题。但是為什么還要使用TypeArray呢?因?yàn)槿绻鹶alue是引用類型如"@string/app_name",那么它獲得的value將是@+一串?dāng)?shù)字胖腾,如果要把這些數(shù)字解析出來(lái)烟零,則還需要寫(xiě)代碼完成這項(xiàng)工作,而TypeArray是簡(jiǎn)化了我們的工作咸作。參考TextView獲取屬性的代碼锨阿,可見(jiàn)系統(tǒng)也是這樣做的
/*
* Look the appearance up without checking first if it exists because
* almost every TextView has one and it greatly simplifies the logic
* to be able to parse the appearance first and then let specific tags
* for this View override it.
*/
TypedArray a = theme.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.TextViewAppearance, defStyleAttr, defStyleRes);
TypedArray appearance = null;
int ap = a.getResourceId(
com.android.internal.R.styleable.TextViewAppearance_textAppearance, -1);
a.recycle();
....
- 如果直接使用系統(tǒng)的屬性,則直接在attrs.xml中聲明
<resources>
<declare-styleable name="RedTextButton">
<attr name="backgroundColor" format="color"/>
<attr name="textSize" format="integer"/>
<attr name="android:text"/>
</declare-styleable>
</resources>
5 自定義控件的步驟
步驟如下:
- 自定義屬性
- 在customView中獲取這些屬性
- 重寫(xiě)onMesure记罚。老師沒(méi)有說(shuō)墅诡,但是不寫(xiě)好像有一丟丟的問(wèn)題
- 重寫(xiě)onDraw。繪制
既然步驟知道了毫胜,那就開(kāi)始书斜。自定義屬性和獲取屬性上面已經(jīng)介紹一遍了诬辈,接下來(lái)就是重寫(xiě)一系列方法
- 重寫(xiě)draw方法
/**
*
* @param canvas 繪圖工具
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPanit.setTextSize(mTextSize);
//獲取文字四周的矩形,文字荐吉,開(kāi)始的位置焙糟,結(jié)束的位置,把文字的四周邊距計(jì)算出來(lái)放在mRect中
mPanit.getTextBounds(mText, 0, mText.length(), mRect);
float textWidth = mRect.width();
float textHeight = mRect.height();
mPanit.setColor(mBackGroundColor);
canvas.drawRect(0f, 0f, getMeasuredWidth(),getMeasuredHeight() ,mPanit);
//中間一個(gè)白色的數(shù)字
mPanit.setColor(Color.WHITE);
canvas.drawText(mText, getWidth() / 2 - textWidth / 2, getHeight() / 2 + textHeight / 2, mPanit);
mPanit.setColor(Color.YELLOW);
canvas.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2, mPanit);
}
布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<com.example.myactionbardemo.RedTextButton
android:id="@+id/my"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_marginLeft="2dp"
android:layout_marginTop="2dp"
app:text="10"
app:backgroundColor="@color/myColor"
app:textSize="70sp"/>
</LinearLayout>
顯示如下
如果把寬高改成wrapcontent,則會(huì)變成(數(shù)字我設(shè)置了點(diǎn)擊事件所以變了)样屠。不符合
所以需要重寫(xiě)onMesure方法
重寫(xiě)之前了解 MeasureSpec 的 specMode穿撮,一共分為三種類型:
EXACTLY:一般表示設(shè)置了 明確值,或者 match_parent 痪欲;
AT_MOST:表示子控件限制在一個(gè)最大值內(nèi)悦穿,一般為 wrap_content;
UNSPECIFIED:表示子控件像多大就多大业踢,很少使用
@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 width;
int height;
if (widthMode == MeasureSpec.EXACTLY){
width = widthSize;
}else {
mPanit.setTextSize(mTextSize);
//獲取文字四周的矩形栗柒,文字,開(kāi)始的位置知举,結(jié)束的位置瞬沦,把文字的四周邊距計(jì)算出來(lái)放在mRecr中
mPanit.getTextBounds(mText, 0, mText.length(), mRect);
int desired = getPaddingLeft() + getPaddingRight() + mRect.width();
width = desired;
}
if (heightMode == MeasureSpec.EXACTLY)
{
height = heightSize;
} else
{
mPanit.setTextSize(mTextSize);
//獲取文字四周的矩形,文字雇锡,開(kāi)始的位置逛钻,結(jié)束的位置,把文字的四周邊距計(jì)算出來(lái)放在mRecr中
mPanit.getTextBounds(mText, 0, mText.length(), mRect);
int desired = (getPaddingTop() + getPaddingBottom() + mRect.height());
height = desired;
}
setMeasuredDimension(width, height);
}
然后wrapcontent就會(huì)變成
但是還有一個(gè)問(wèn)題就是文字看上去不是那么居中锰提,這篇博客關(guān)于文字居中寫(xiě)得特別的詳細(xì)怎么繪制居中文字曙痘,碼著學(xué)習(xí)。
最后給view添加一個(gè)點(diǎn)擊事件立肘,點(diǎn)擊一下數(shù)字會(huì)減少
@Override
public void onClick(View view) {
int mNumber = Integer.parseInt(mText);
if (mNumber > 0){
mNumber--;
}else {
mNumber = 10;
}
mText=String.valueOf(mNumber);
invalidate();
}
tips:
自定義控件水太深
我現(xiàn)在有點(diǎn)餓