View
,有不少的名稱。不管是你熟知的佈局,仍是控件,他們所有都繼承自View
。 php
文內部分圖片轉載自Carson_Ho大佬的文章java
其實經過layout
中的第二張圖咱們已經知道了控件大小的計算了。android
height
= bottom
- top
width
= right
- left
對於ViewGroup而言,就是對容器內子控件的遍歷和計算了。canvas
由於直接繼承自View
的控件使用wrap_cotent
和match_parent
是顯示出來的效果是相同的。須要咱們使用MeasureSpec
中的getMode()
方法來對當前的模式進行區分和比較。性能優化
模式 | 狀態 |
---|---|
UNSPECIFIED | 未指定模式,View想多大就多大,父容器不作限制,通常用於系統內部的測量 |
AT_MOST | 最大模式,對應wrap_content,View的大小不大於SpecSize的值 |
EXACTLY | 精確模式,對應match_parent,View的大小爲SpecSize的值 |
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//用於獲取設定的模式
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
// 用於獲取設定的長度
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
// 相似這樣的判斷,後面不過多複述
// 用於判斷是否是wrap_content
// 若是不進行處理,效果會是match_parent
if(widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST){
setMeasuredDimension(20, 20);
}
}
複製代碼
在肯定位置時,咱們有一個很是須要主要的地方—— 座標系。Android系統的座標系和平時畫的座標系並不相同。 app
因此相對應的,咱們的位置計算方法天然和咱們原來的正好是相反的。 ide
4個頂點的位置分別由4個值決定:佈局
top
:子View上邊界到所在容器上邊界的距離。left
:子View左邊界到所在容器左邊界的距離。bottom
:子View下邊界到所在容器上邊界的距離。right
:子View右邊界到所在容器左邊界的距離。全部的計算都是相對於所在容器纔可以開始的。post
一共有6個步驟:性能
關於開發者須要重寫的方法通常是第三步繪製View的內容對應的
onDraw()
。
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getWidth();
int height = getHeight();
// 在畫布上進行相似這樣的操做
canvas.drawLine(0, height/2, width,height/2, paint);
}
複製代碼
在平常項目的佈局文件中咱們常常會使用到xmlns:app="http://schemas.android.com/apk/res-auto"
這樣標籤,其實他就是用來引入咱們自定義的標籤使用的。
res/values
目錄下建立attrs
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="DefaultView">
<attr name="color" format="color"/>
</declare-styleable>
</resources>
複製代碼
DefaultView(Context context, @Nullable AttributeSet attrs)
中獲取。如下是整個完整代碼。/** * author: ClericYi * time: 2020-01-30 */
public class DefaultView extends View {
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private int mColor = Color.RED;
public DefaultView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initAttrs(context, attrs);
initDraw();
}
private void initAttrs(Context context, @Nullable AttributeSet attrs) {
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.DefaultView);
// 從styleable中獲取的名字是系統會生成的,通常是 類名_name 的形式
mColor = array.getColor(R.styleable.DefaultView_color, Color.GREEN);
// 獲取完資源後即便回收
array.recycle();
}
private void initDraw() {
paint.setColor(mColor);
paint.setStrokeWidth(3f);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getWidth();
int height = getHeight();
canvas.drawLine(0, height/2, width,height/2, paint);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
if(widthMode == MeasureSpec.AT_MOST){
setMeasuredDimension(20, 20);
}
}
}
複製代碼
首先的話咱們先了解如何去知道一個View
是否被過分繪製了?
其實在咱們手機中的開發模式已經存在這個選項了。
開啓前 | 開啓後 |
---|---|
下方給出繪製的次數對應圖
在這個問題以前,須要瞭解什麼是過分繪製,你能夠理解爲同一位置的控件不斷的疊加而產生的無用數據,那咱們就來講說集中解決方案吧。
方案1: 減小嵌套層數。
使用線性佈局 | 使用約束佈局 |
---|---|
由於只是一個案例,想說的意思,若是多個LinearLayout
嵌套實現的效果,若是能被一個ConstraintLayout
直接實現,那麼就用後者替代,由於不會這樣在同一個區域重複出現
方案2: 去除默認的背景
這個解決方案其實針對的背景會被自動繪製的問題,若是咱們把這個層次消去,從繪製角度老說也是一種提高了。正如圖示通常直接減小了一層的繪製。
在代碼中的具體表現,經過對style.xml
中的Theme
進行修改:
<item name="android:windowBackground">@null</item>
複製代碼
以上就是個人學習成果,若是有什麼我沒有思考到的地方或是文章內存在錯誤,歡迎與我分享。
相關文章推薦: