導航:首頁 > 源碼編譯 > viewui源碼編譯

viewui源碼編譯

發布時間:2022-09-07 03:13:30

android UI繪制之View繪制的工作原理

這是AndroidUI繪制流程分析的第二篇文章,主要分析界面中View是如何繪制到界面上的具體過程。

ViewRoot 對應於 ViewRootImpl 類,它是連接 WindowManager 和 DecorView 的紐帶,View的三大流程均是通過 ViewRoot 來完成的。在 ActivityThread 中,當 Activity 對象被創建完畢後,會將 DecorView 添加到 Window 中,同時會創建 ViewRootImpl 對象,並將 ViewRootImpl 對象和 DecorView 建立關聯。

measure 過程決定了 View 的寬/高, Measure 完成以後,可以通過 getMeasuredWidth 和 getMeasuredHeight 方法來獲取 View 測量後的寬/高,在幾乎所有的情況下,它等同於View的最終的寬/高,但是特殊情況除外。 Layout 過程決定了 View 的四個頂點的坐標和實際的寬/高,完成以後,可以通過 getTop、getBottom、getLeft 和 getRight 來拿到View的四個頂點的位置,可以通過 getWidth 和 getHeight 方法拿到View的最終寬/高。 Draw 過程決定了 View 的顯示,只有 draw 方法完成後 View 的內容才能呈現在屏幕上。

DecorView 作為頂級 View ,一般情況下,它內部會包含一個豎直方向的 LinearLayout ,在這個 LinearLayout 裡面有上下兩個部分,上面是標題欄,下面是內容欄。在Activity中,我們通過 setContentView 所設置的布局文件其實就是被加到內容欄中的,而內容欄id為 content 。可以通過下面方法得到 content:ViewGroup content = findViewById(R.android.id.content) 。通過 content.getChildAt(0) 可以得到設置的 view 。 DecorView 其實是一個 FrameLayout , View 層的事件都先經過 DecorView ,然後才傳遞給我們的 View 。

MeasureSpec 代表一個32位的int值,高2位代表 SpecMode ,低30位代表 SpecSize , SpecMode 是指測量模式,而 SpecSize 是指在某種測量模式下的規格大小。
SpecMode 有三類,如下所示:

UNSPECIFIED

EXACTLY

AT_MOST

LayoutParams需要和父容器一起才能決定View的MeasureSpec,從而進一步決定View的寬/高。

對於頂級View,即DecorView和普通View來說,MeasureSpec的轉換過程略有不同。對於DecorView,其MeasureSpec由窗口的尺寸和其自身的LayoutParams共同確定;

對於普通View,其MeasureSpec由父容器的MeasureSpec和自身的Layoutparams共同決定;

MeasureSpec一旦確定,onMeasure就可以確定View的測量寬/高。

小結一下

當子 View 的寬高採用 wrap_content 時,不管父容器的模式是精確模式還是最大模式,子 View 的模式總是最大模式+父容器的剩餘空間。

View 的工作流程主要是指 measure 、 layout 、 draw 三大流程,即測量、布局、繪制。其中 measure 確定 View 的測量寬/高, layout 確定 view 的最終寬/高和四個頂點的位置,而 draw 則將 View 繪制在屏幕上。

measure 過程要分情況,如果只是一個原始的 view ,則通過 measure 方法就完成了其測量過程,如果是一個 ViewGroup ,除了完成自己的測量過程外,還會遍歷調用所有子元素的 measure 方法,各個子元素再遞歸去執行這個流程。

如果是一個原始的 View,那麼通過 measure 方法就完成了測量過程,在 measure 方法中會去調用 View 的 onMeasure 方法,View 類裡面定義了 onMeasure 方法的默認實現:

先看一下 getSuggestedMinimumWidth 和 getSuggestedMinimumHeight 方法的源碼

可以看到, getMinimumWidth 方法獲取的是 Drawable 的原始寬度。如果存在原始寬度(即滿足 intrinsicWidth > 0),那麼直接返回原始寬度即可;如果不存在原始寬度(即不滿足 intrinsicWidth > 0),那麼就返回 0。

接著看最重要的 getDefaultSize 方法:

如果 specMode 為 MeasureSpec.UNSPECIFIED 即未指定模式,那麼返回由方法參數傳遞過來的尺寸作為 View 的測量寬度和高度;
如果 specMode 不是 MeasureSpec.UNSPECIFIED 即是最大模式或者精確模式,那麼返回從 measureSpec 中取出的 specSize 作為 View 測量後的寬度和高度。

看一下剛才的表格:

當 specMode 為 EXACTLY 或者 AT_MOST 時,View 的布局參數為 wrap_content 或者 match_parent 時,給 View 的 specSize 都是 parentSize 。這會比建議的最小寬高要大。這是不符合我們的預期的。因為我們給 View 設置 wrap_content 是希望View的大小剛好可以包裹它的內容。

因此:

如果是一個 ViewGroup,除了完成自己的 measure 過程以外,還會遍歷去調用所有子元素的 measure 方法,各個子元素再遞歸去執行 measure 過程。

ViewGroup 並沒有重寫 View 的 onMeasure 方法,但是它提供了 measureChildren、measureChild、measureChildWithMargins 這幾個方法專門用於測量子元素。

如果是 View 的話,那麼在它的 layout 方法中就確定了自身的位置(具體來說是通過 setFrame 方法來設定 View 的四個頂點的位置,即初始化 mLeft , mRight , mTop , mBottom 這四個值), layout 過程就結束了。

如果是 ViewGroup 的話,那麼在它的 layout 方法中只是確定了 ViewGroup 自身的位置,要確定子元素的位置,就需要重寫 onLayout 方法;在 onLayout 方法中,會調用子元素的 layout 方法,子元素在它的 layout 方法中確定自己的位置,這樣一層一層地傳遞下去完成整個 View 樹的 layout 過程。

layout 方法的作用是確定 View 本身的位置,即設定 View 的四個頂點的位置,這樣就確定了 View 在父容器中的位置;
onLayout 方法的作用是父容器確定子元素的位置,這個方法在 View 中是空實現,因為 View 沒有子元素了,在 ViewGroup 中則進行抽象化,它的子類必須實現這個方法。

1.繪制背景( background.draw(canvas); );
2.繪制自己( onDraw );
3.繪制 children( dispatchDraw(canvas) );
4.繪制裝飾( onDrawScrollBars )。

dispatchDraw 方法的調用是在 onDraw 方法之後,也就是說,總是先繪制自己再繪制子 View 。

對於 View 類來說, dispatchDraw 方法是空實現的,對於 ViewGroup 類來說, dispatchDraw 方法是有具體實現的。

通過 dispatchDraw 來傳遞的。 dispatchDraw 會遍歷調用子元素的 draw 方法,如此 draw 事件就一層一層傳遞了下去。dispatchDraw 在 View 類中是空實現的,在 ViewGroup 類中是真正實現的。

如果一個 View 不需要繪制任何內容,那麼就設置這個標記為 true,系統會進行進一步的優化。

當創建的自定義控制項繼承於 ViewGroup 並且不具備繪制功能時,就可以開啟這個標記,便於系統進行後續的優化;當明確知道一個 ViewGroup 需要通過 onDraw 繪制內容時,需要關閉這個標記。

參考:《Android開發藝術探索》

⑵ uilayoutcontainerview 怎麼用代碼實現

寫協議,用代理
或者在childcontroller里寫Notification
關鍵是container里的viewcontroller不是用代碼生成的,而是在storyboard里通過segue連接起來的,因此設置委託的時候對象也不是同一個。
如果childcontroller在父類里有實例,用KVO也可以監測值的變化
var newController = self.storyboard?.("RegisterController") as! RegisterController
let oldController = childViewControllers.last as! UIViewController

oldController.(nil)
addChildViewController(newController)
newController.view.frame = oldController.view.frame

//isAnimating = true
transitionFromViewController(oldController, toViewController: newController, ration: 0.1, options: UIViewAnimationOptions.TransitionNone, animations: nil, completion: { (finished) -> Void in
oldController.()
newController.didMoveToParentViewController(self)
//self.isAnimating = false
})

⑶ 如何純代碼給UICollectionView添加HeaderView和FooterView

collectionView和table基本用法一樣但是header和footer,就找不到方法了
自己找了好久網上也沒個人寫的就一個寫的是用storyBoard寫的 對於純代碼的可能不怎麼太理解。
自己摸索的做出來了。下面純代碼的步驟:<語文水平渣 沒什麼文采,但是技術點肯定會說明白的>
如果要給你的collectionView添加header和footer,他的數據源和代理是沒有直接提供創建方法的,但是提供了一個兩用的方法
[objc] view plain
<span style="font-size:14px;">- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView :(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
</span>
如果要給你的collectionView添加header和footer步驟<本文只以header為例>
1.設置流水布局 ,需要在流水布局裡設置header和footer的size
[objc] view plain
- (id)init
{
// UICollectionViewLayout;
UICollectionViewFlowLayout *flow = [[UICollectionViewFlowLayout alloc] init]; // 流水布局
[objc] view plain
[objc] view plain
<span style="white-space:pre"> </span>// 設置header的Size
<span style="color:#ff6666;"> flow.headerReferenceSize = CGSizeMake(320, 44);</span>
[objc] view plain
// 設置格子的寬高
flow.itemSize = CGSizeMake(75, 61);
// 設置列距
flow.minimumInteritemSpacing = 5;
// 設置行距離
// flow.minimumLineSpacing = 0;
// 設置整體內容和四周的邊距
// top left bottom right
flow.sectionInset = UIEdgeInsetsMake(20, 2, 0, 2);
return [super initWithCollectionViewLayout:flow];
}
2. 創建UICollectionReusableView的子類UICollectionHeaderView,並創建其xib 設置xib的identifier為header
[objc] view plain
<span style="font-size:14px;">#import <UIKit/UIKit.h>
@interface UICollectionHeaderView : UICollectionReusableView
@end
</span>
3.在xib中添加你需要顯示的控制項,不要忘了class繼承UICollectionHeaderView。
4.在collectionViewController 的viewDidLoad注冊xib,方法和注冊cell差不多 只不過方法名不一樣
[objc] view plain
<span style="font-size:14px;">UINib *nib = [UINib nibWithNibName:@"WdViewCell" bundle:nil] ;
[self.collectionView registerNib:nib forCellWithReuseIdentifier:@"cell"];
[self.collectionView setBackgroundColor:[UIColor colorWithRed:240 green:240 blue:240 alpha:0.8]];
</span>
[objc] view plain
<span style="font-size:14px;">// 注冊header的
UINib *header = [UINib nibWithNibName:@"UICollectionHeaderView" bundle:nil];
[self.collectionView registerNib:header forSupplementaryViewOfKind:<span style="color:#ff6666;"></span> withReuseIdentifier:@"header"];</span>
5.顯示header
這個方法:kind標識你是header還是footer<可能還有其他的>header:,用個判斷或者switch就可以選擇你要顯示什麼類容了,創建header view的方法
[objc] view plain
<span style="font-size:14px;">// 設置每組的標題
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView :(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
if (kind == ) {
UICollectionHeaderView *headerView = [collectionView :kind withReuseIdentifier:@"header" forIndexPath:indexPath];
</span>
[objc] view plain
<span style="font-size:14px;">// 在這就可以設置header中子控制項的數據了</span>
[objc] view plain
<span style="font-size:14px;"> return headerView;
}
else
{
return nil;
}
}</span>
這樣就可以顯示你的header了

⑷ 如何純代碼給UICollectionView添加HeaderView

UICollectionView主要用於瀑布流,由於一直接觸較少,每次需要使用的時候都從網上翻閱資料,此次自己總結整理,以備不時之需。

collectionView和tableView最大的不同之處就是需要自定義cell,所以第一步自定義collectionViewCell

.h文件

#import <UIKit/UIKit.h>

@interface MyCollectionViewCell : UICollectionViewCell

@property (strong, nonatomic) UIImageView *topImage;

@property (strong, nonatomic) UILabel *botlabel;

@end

.m文件

#import "MyCollectionViewCell.h"

@implementation MyCollectionViewCell

- (id)initWithFrame:(CGRect)frame

{

self = [super initWithFrame:frame];

if (self)

{

_topImage = [[UIImageView alloc] initWithFrame:CGRectMake(10, 0, 70, 70)];

_topImage.backgroundColor = [UIColor redColor];

[self.contentView addSubview:_topImage];

_botlabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 80, 70, 30)];

_botlabel.textAlignment = NSTextAlignmentCenter;

_botlabel.textColor = [UIColor blueColor];

_botlabel.font = [UIFont systemFontOfSize:15];

_botlabel.backgroundColor = [UIColor purpleColor];

[self.contentView addSubview:_botlabel];

}

return self;

}

@end

在viewController中實現collectionView的三個協議

UICollectionViewDataSource,UICollectionViewDelegate,

具體實例化步驟均在代碼中有注釋,如下

#import "CollectionViewController.h"

#import "MyCollectionViewCell.h"

@interface CollectionViewController ()<UICollectionViewDataSource,UICollectionViewDelegate,>

{

UICollectionView *mainCollectionView;

}

@end

@implementation CollectionViewController

- (void)viewDidLoad {

[super viewDidLoad];

// Do any additional setup after loading the view.

self.view.backgroundColor = [UIColor whiteColor];

//1.初始化layout

UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];

//設置collectionView滾動方向

// [layout setScrollDirection:];

//設置headerView的尺寸大小

layout.headerReferenceSize = CGSizeMake(self.view.frame.size.width, 100);

//該方法也可以設置itemSize

layout.itemSize =CGSizeMake(110, 150);

//2.初始化collectionView

mainCollectionView = [[UICollectionView alloc成都軟體開發公司http://www.yingtaow.com?initWithFrame:self.view.bounds collectionViewLayout:layout];

[self.view addSubview:mainCollectionView];

mainCollectionView.backgroundColor = [UIColor clearColor];

//3.注冊collectionViewCell

//注意,此處的ReuseIdentifier 必須和 cellForItemAtIndexPath 方法中 一致 均為 cellId

[mainCollectionView registerClass:[MyCollectionViewCell class] forCellWithReuseIdentifier:@"cellId"];

//注冊headerView 此處的ReuseIdentifier 必須和 cellForItemAtIndexPath 方法中 一致 均為reusableView

[mainCollectionView registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind: withReuseIdentifier:@"reusableView"];

//4.設置代理

mainCollectionView.delegate = self;

mainCollectionView.dataSource = self;

}

#pragma mark collectionView代理方法

//返回section個數

- (NSInteger):(UICollectionView *)collectionView

{

return 3;

}

//每個section的item個數

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section

{

return 9;

}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath

{

MyCollectionViewCell *cell = (MyCollectionViewCell *)[collectionView :@"cellId" forIndexPath:indexPath];

cell.botlabel.text = [NSString stringWithFormat:@"{%ld,%ld}",(long)indexPath.section,(long)indexPath.row];

cell.backgroundColor = [UIColor yellowColor];

return cell;

}

//設置每個item的尺寸

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath

{

return CGSizeMake(90, 130);

}

//footer的size

//- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout :(NSInteger)section

//{

// return CGSizeMake(10, 10);

//}

//header的size

//- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout :(NSInteger)section

//{

// return CGSizeMake(10, 10);

//}

//設置每個item的UIEdgeInsets

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section

{

return UIEdgeInsetsMake(10, 10, 10, 10);

}

//設置每個item水平間距

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout :(NSInteger)section

{

return 10;

}

//設置每個item垂直間距

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout :(NSInteger)section

{

return 15;

}

//通過設置SupplementaryViewOfKind 來設置頭部或者底部的view,其中 ReuseIdentifier 的值必須和 注冊是填寫的一致,本例都為 「reusableView」

- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView :(NSString *)kind atIndexPath:(NSIndexPath *)indexPath

{

UICollectionReusableView *headerView = [collectionView : withReuseIdentifier:@"reusableView" forIndexPath:indexPath];

headerView.backgroundColor =[UIColor grayColor];

UILabel *label = [[UILabel alloc] initWithFrame:headerView.bounds];

label.text = @"這是collectionView的頭部";

label.font = [UIFont systemFontOfSize:20];

[headerView addSubview:label];

return headerView;

}

//點擊item方法

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath

{

MyCollectionViewCell *cell = (MyCollectionViewCell *)[collectionView cellForItemAtIndexPath:indexPath];

NSString *msg = cell.botlabel.text;

NSLog(@"%@",msg);

}

- (void)didReceiveMemoryWarning {

[super didReceiveMemoryWarning];

// Dispose of any resources that can be recreated.

}

/*

#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {

// Get the new view controller using [segue destinationViewController].

// Pass the selected object to the new view controller.

}

*/

@end

⑸ 為什麼現在很火的uView UI框架免費開源呢

先理解什麼是開源,一句話來說,開源指的是那些源代碼或源設計可以被大眾使用、修改發行的軟體或設計體。一般來說,沒有開源的基礎技術是沒有生命力的。所以作為一個開發者的來說,我非常欣賞、佩服uView UI作者易瑞文的思維和貢獻的。

⑹ Android UI | View 的繪制流程詳解

上一篇文章講解了,從setContentView方法到了解View是如何繪制的: 傳送門
在這篇博客講述了, 在ViewRootImpl類中performTraversals方法中具體的繪制過程,其中裡面就有 performMeasure()、performLayout()、performDraw() 三個方法的調用, 那麼要了解View 的測量、布局、繪制,就分別跟這三個方法有關系。

先來看看performMeasure()方法的調用過程

先看performMeasure方法,這個方法有兩個參數,都是通過getRootMeasureSpec()方法計算得到

這里有一個關鍵類MeasureSpec,在這里需要了解下這個類的原理。
這里要感謝 這位博主 ,他講述的很清晰,我自己動手測算了,很容易理解。大概就是用一個數字通過高位記錄Mode,地位記錄size的方式,記錄兩個數據,都是通過一個掩碼做位移運算得來。就是說這個變數(measureSpec)的值可以通過掩碼分別得到測量mode 和 測量size。
繼續查看performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);方法:

這里mView是DecorView對象,那麼他調用的實際上是View的measure方法,查詢DecorView和FrameLayout都沒有measure方法,所以他調用的是View的measure方法

DecorView.onMeasure()方法如下:

FrameLayout.onMeasure方法如下:(這個方法裡面都很重要)

我們先看 , measureChildWithMargins 方法,

getChildMeasureSpec 方法內容如下:

那麼假如我們寫的布局根節點是LinearLayout,那麼就會在執行到View.measure方法裡面的onMeasure方法時,就會調用到LinearLayout.onMeasure方法,具體內容如下:

通過源碼可以看到,還是會循環調用子View , 就這樣循環遞歸的測量完最裡面的一個view,這個過程中onMeasure方法可能會被多次執行。

還是從ViewRootImpl.performTraversals開始

這里跟measure 調用流程其實一樣,DecorView和FrameLayout沒有重寫layout方法,所以調用的是View.layout方法,

由於當前對象是decorView,所以調用的是DecorView.onLayout方法:

FrameLayout.onLayout 方法如下:

繼續查看 View.draw方法

在這個方法裡面,所有重要的方法都在裡面,onDraw、dispatchDraw 等等都在裡面,我看了下這個方法裡面都挺重要就沒刪減,也都能看得懂。

到此View的整個繪制流程就搞清楚了。

關於子view測量
1、不管父View是何模式,若子View有確切數值,則子View大小就是其本身大小,且mode是EXACTLY
2、若子View是match_parent,則模式與父View相同,且大小同父View(若父View是UNSPECIFIED,則子View大小為0)
3、若子View是wrap_content,則模式是AT_MOST,大小同父View,表示不可超過父View大小(若父View是UNSPECIFIED,則子View大小為0)

關於繪制流程
我們自定義的view,基本上只需要重寫 onMeasure、onLayout、onDraw即可

⑺ 大牛們是怎麼閱讀 Android 系統源碼的

如果只是想看看一些常用類的實現, 在Android包管理器里把源碼下載下來, 隨便一個IDE配好Source Code的path看就行.
但如果想深入的了解Android系統, 那麼可以看下我的一些簡單的總結.

知識
java
Java是AOSP的主要語言之一. 沒得說, 必需熟練掌握.
熟練的Android App開發
Linux
Android基於Linux的, 並且AOSP的推薦編譯環境是Ubuntu 12.04. 所以熟練的使用並了解Linux這個系統是必不可少的. 如果你想了解偏底層的代碼, 那麼必需了解基本的Linux環境下的程序開發. 如果再深入到驅動層, 那麼Kernel相關的知識也要具備.
Make
AOSP使用Make系統進行編譯. 了解基本的Makefile編寫會讓你更清晰了解AOSP這個龐大的項目是如何構建起來的.
Git
AOSP使用git+repo進行源碼管理. 這應該是程序員必備技能吧.
C++
Android系統的一些性能敏感模塊及第三方庫是用C++實現的, 比如: Input系統, Chromium項目(WebView的底層實現).

硬體
流暢的國際網路
AOSP代碼下載需要你擁有一個流暢的國際網路. 如果在下載代碼這一步就失去耐心的話, 那你肯定沒有耐心去看那亂糟糟的AOSP代碼. 另外, 好程序員應該都會需要一個流暢的Google.
一台運行Ubuntu 12.04的PC.
如果只是閱讀源碼而不做太多修改的話, 其實不需要太高的配置.
一台Nexus設備
AOSP項目默認只支持Nexus系列設備. 沒有也沒關系, 你依然可以讀代碼. 但如果你想在大牛之路走的更遠, 還是改改代碼, 然後刷機調試看看吧.
高品質USB線
要刷機時線壞了, 沒有更窩心的事兒了.
軟體
Ubuntu 12.04
官方推薦, 沒得選.
Oracle Java 1.6
注意不要用OpenJDK. 這是個坑, 官方文檔雖然有寫, 但還是單獨提一下.
安裝:
sudo apt-get install python-software-properties
sudo add-apt-repository ppa:webupd8team/java
sudo apt-get update
sudo apt-get install oracle-java6-installer
sudo apt-get install oracle-java6-set-default

Eclipse
估計會有不少人吐槽, 為什麼要用這個老古董. 其實原因很簡單, 合適. 剛開始搞AOSP時, 為了找到效率最優的工具, 我嘗試過Eclipse, IntelliJ IDEA, Vim+Ctags, Sublime Text+Ctags. 最終結果還是Eclipse. 主要優點有:
有語法分析 (快速准確的類, 方法跳轉).
支持C++ (IntelliJ的C++支持做的太慢了).
嵌入了DDMS, View Hierarchy等調試工具.
為了提高效率, 花5分鍾背下常用快捷鍵非常非常值得.
調整好你的classpath, 不要導入無用的代碼. 因為AOSP項目代碼實在是太多了. 當你還不需要看C++代碼時, 不要為項目添加C++支持, 建索引過程會讓你崩潰.
Intellij IDEA
開發App必備. 當你要調試系統的某個功能是, 常常需要迅速寫出一個調試用App, 這個時候老舊的Eclipse就不好用了. Itellij IDEA的xml自動補全非常給力.
巨人的肩膀

這個一定要先讀. 項目介紹, 代碼下載, 環境搭建, 刷機方法, Eclipse配置都在這里. 這是一切的基礎.

這個其實是給App開發者看的. 但是裡面也有不少關於系統機制的介紹, 值得細讀.

此老羅非彼老羅. 羅升陽老師的博客非常有營養, 基本可以作為指引你開始閱讀AOSP源碼的教程. 你可以按照博客的時間順序一篇篇挑需要的看.但這個系列的博客有些問題:
早期的博客是基於舊版本的Android;
大量的代碼流程追蹤. 讀文章時你一定要清楚你在看的東西在整個系統處於什麼樣的位置.

鄧凡平老師也是為Android大牛, 博客同樣很有營養. 但是不像羅升陽老師的那麼系統. 更多的是一些技術點的深入探討.

Android官方Issue列表. 我在開發過程中發現過一些奇怪的bug, 最後發現這里基本都有記錄. 當然你可以提一些新的, 有沒有人改就是另外一回事了.

一定要能流暢的使用這個工具. 大量的相關知識是沒有人系統的總結的, 你需要自己搞定.
其它
代碼組織
AOSP的編譯單元不是和git項目一一對應的, 而是和Android.mk文件一一對應的. 善用mmm命令進行模塊編譯將節省你大量的時間.
Binder
這是Android最基礎的進程間通訊. 在Application和System services之間大量使用. 你不僅要知道AIDL如何使用, 也要知道如何手寫Binder介面. 這對你理解Android的Application和System services如何交互有非常重要的作用. Binder如何實現的倒不必著急看.
HAL
除非你對硬體特別感興趣或者想去方案公司上班, 否則別花太多時間在這一層.
CyanogenMod
這是一個基於AOSP的第三方Rom. 從這個項目的wiki里你能學到很多AOSP官方沒有告訴你的東西. 比如如何支持Nexus以外的設備.
DIA
這是一個Linux下畫UML的工具, 能夠幫你梳理看過的代碼.
XDA

⑻ Android 重學系列 View的繪制流程(六) 硬體渲染(上)

本文開始聊聊Android中的硬體渲染。如果跟著我的文章順序,從SF進程到App進程的繪制流程一直閱讀,我們到這里已經有了一定的基礎,可以試著進行橫向比對如Chrome瀏覽器渲染流程,看看軟體渲染,硬體渲染,SF合成都做了什麼程度的優化。

先讓我們回顧一下負責硬體渲染的主體對象ThreadedRenderer在整個繪制流程中做了哪幾個步驟。

在硬體渲染的過程中,有一個很核心的對象RenderNode,作為每一個View繪制的節點對象。

當每一次進行准備進行繪制的時候,都會雷打不動執行如下三個步驟:

如果遇到什麼問題歡迎來到 https://www.jianshu.com/p/c84bfa909810 下進行討論

實際上整個硬體渲染的設計還是比較龐大。因此本文先聊聊ThreadedRender整個體系中主要對象的構造以及相關的原理。

首先來認識下面幾個重要的對象有一個大體的印象。

在Java層中面向Framework中,只有這么多,下面是一一映射的簡圖。

能看到實際上RenderNode也會跟著View 樹的構建同時一起構建整個顯示層級。也是因此ThreadedRender也能以RenderNode為線索構建出一套和軟體渲染一樣的渲染流程。

僅僅這樣?如果只是這么簡單,知道我習慣的都知道,我喜歡把相關總結寫在最後。如果把總攬寫在正文開頭是因為設計比較繁多。因為我們如果以流水線的形式進行剖析容易造成迷失細節的困境。

讓我繼續介紹一下,在硬體渲染中native層的核心對象。

如下是一個思維導圖:

有這么一個大體印象後,就不容易迷失在源碼中。我們先來把這些對象的實例化以及上面列舉的ThreadedRenderer在ViewRootImpl中執行行為的順序和大家來聊聊其原理,先來看看ThreadedRenderer的實例化。

當發現mSurfaceHolder為空的時候會調用如下函數:

而這個方法則調用如下的方法對ThreadedRenderer進行創建:

文件:/ frameworks / base / core / java / android / view / ThreadedRenderer.java

能不能創建的了ThreadedRenderer則決定於全局配置。如果ro.kernel.qemu的配置為0,說明支持OpenGL 則可以直接返回true。如果qemu.gles為-1說明不支持OpenGL es返回false,只能使用軟體渲染。如果設置了qemu.gles並大於0,才能打開硬體渲染。

我們能看到ThreadedRenderer在初始化,做了三件事情:

關鍵是看1-3點中ThreadRenderer都做了什麼。

文件:/ frameworks / base / core / jni / android_view_ThreadedRenderer.cpp

能看到這里是直接實例化一個RootRenderNode對象,並把指針的地址直接返回。

能看到RootRenderNode繼承了RenderNode對象,並且保存一個JavaVM也就是我們所說的Java虛擬機對象,一個java進程全局只有一個。同時通過getForThread方法,獲取ThreadLocal中的Looper對象。這里實際上拿的就是UI線程的Looper。

在這個構造函數有一個mDisplayList十分重要,記住之後會頻繁出現。接著來看看RenderNode的頭文件:
文件:/ frameworks / base / libs / hwui / RenderNode.h

實際上我把幾個重要的對象留下來:

文件:/ frameworks / base / core / java / android / view / RenderNode.java

能看到很簡單,就是包裹一個native層的RenderNode返回一個Java層對應的對象開放Java層的操作API。

能看到這個過程生成了兩個對象:

這個對象實際上讓RenderProxy持有一個創建動畫上下文的工廠。RenderProxy可以通過ContextFactoryImpl為每一個RenderNode創建一個動畫執行對象的上下文AnimationContextBridge。

文件:/ frameworks / base / libs / hwui / renderthread / RenderProxy.cpp

在這里有幾個十分重要的對象被實例化,當然這幾個對象在聊TextureView有聊過( SurfaceView和TextureView 源碼淺析 ):

我們依次看看他們初始化都做了什麼。

文件:/ frameworks / base / libs / hwui / renderthread / RenderThread.cpp

能看到其實就是簡單的調用RenderThread的構造函數進行實例化,並且返回對象的指針。

RenderThread是一個線程對象。先來看看其頭文件繼承的對象:
文件:/ frameworks / base / libs / hwui / renderthread / RenderThread.h

其中RenderThread的中進行排隊處理的任務隊列實際上是來自ThreadBase的WorkQueue對象。

文件:/ frameworks / base / libs / hwui / thread / ThreadBase.h

ThreadBase則是繼承於Thread對象。當調用start方法時候其實就是調用Thread的run方法啟動線程。

另一個更加關鍵的對象,就是實例化一個Looper對象到WorkQueue中。而直接實例化Looper實際上就是新建一個Looper。但是這個Looper並沒有獲取當先線程的Looper,這個Looper做什麼的呢?下文就會揭曉。

WorkQueue把一個Looper的方法指針設置到其中,其作用可能是完成了某一件任務後喚醒Looper繼續工作。

而start方法會啟動Thread的run方法。而run方法最終會走到threadLoop方法中,至於是怎麼走進來的,之後有機會會解剖虛擬機的源碼線程篇章進行講解。

在threadloop中關鍵的步驟有如下四個:

在這個過程中創建了幾個核心對象:

另一個核心的方法就是,這個方法為WorkQueue的Looper注冊了監聽:

能看到在這個Looper中注冊了對DisplayEventReceiver的監聽,也就是Vsync信號的監聽,回調方法為displayEventReceiverCallback。

我們暫時先對RenderThread的方法探索到這里,我們稍後繼續看看回調後的邏輯。

文件:/ frameworks / base / libs / hwui / thread / ThreadBase.h

能看到這里的邏輯很簡單實際上就是調用Looper的pollOnce方法,阻塞Looper中的循環,直到Vsync的信號到來才會繼續往下執行。詳細的可以閱讀我寫的 Handler與相關系統調用的剖析 系列文章。

文件:/ frameworks / base / libs / hwui / thread / ThreadBase.h

實際上調用的是WorkQueue的process方法。

文件:/ frameworks / base / libs / hwui / thread / WorkQueue.h

能看到這個過程中很簡單,幾乎和Message的loop的邏輯一致。如果Looper的阻塞打開了,則首先找到預計執行時間比當前時刻都大的WorkItem。並且從mWorkQueue移除,最後添加到toProcess中,並且執行每一個WorkItem的work方法。而每一個WorkItem其實就是通過從某一個壓入方法添加到mWorkQueue中。

到這里,我們就明白了RenderThread中是如何消費渲染任務的。那麼這些渲染任務又是哪裡誕生呢?

上文聊到了在RenderThread中的Looper會監聽Vsync信號,當信號回調後將會執行下面的回調。

能看到這個方法的核心實際上就是調用drainDisplayEventQueue方法,對ui渲染任務隊列進行處理。

能到在這里mVsyncRequested設置為false,且mFrameCallbackTaskPending將會設置為true,並且調用queue的postAt的方法執行ui渲染方法。

還記得queue實際是是指WorkQueue,而WorkQueue的postAt方法實際實現如下:
/ frameworks / base / libs / hwui / thread / WorkQueue.h

情景帶入,當一個Vsync信號達到Looper的監聽者,此時就會通過WorkQueue的drainDisplayEventQueue 壓入一個任務到隊列中。

每一個默認的任務都是執行dispatchFrameCallback方法。這里的判斷mWorkQueue中是否存在比當前時間更遲的時刻,並返回這個WorkItem。如果這個對象在頭部needsWakeup為true,說明可以進行喚醒了。而mWakeFunc這個方法指針就是上面傳下來:

把阻塞的Looper喚醒。當喚醒後就繼續執行WorkQueue的process方法。也就是執行dispatchFrameCallbacks方法。

在這里執行了兩個事情:

先添加到集合中,在上面提到過的threadLoop中,會執行如下邏輯:

如果大小不為0,則的把中的IFrameCallback全部遷移到mFrameCallbacks中。

而這個方法什麼時候調用呢?稍後就會介紹。其實這部分的邏輯在TextureView的解析中提到過。

接下來將會初始化一個重要對象:

這個對象名字叫做畫布的上下文,具體是什麼上下文呢?我們現在就來看看其實例化方法。
文件:/ frameworks / base / libs / hwui / renderthread / CanvasContext.cpp

文件:/ device / generic / goldfish / init.ranchu.rc

在init.rc中默認是opengl,那麼我們就來看看下面的邏輯:

首先實例化一個OpenGLPipeline管道,接著OpenGLPipeline作為參數實例化CanvasContext。

文件:/ frameworks / base / libs / hwui / renderthread / OpenGLPipeline.cpp

能看到在OpenGLPipeline中,實際上就是存儲了RenderThread對象,以及RenderThread中的mEglManager。透過OpenGLPipeline來控制mEglManager進而進一步操作OpenGL。

做了如下操作:

文件:/ frameworks / base / libs / hwui / renderstate / RenderState.cpp

文件:/ frameworks / base / libs / hwui / renderthread / DrawFrameTask.cpp

實際上就是保存這三對象RenderThread;CanvasContext;RenderNode。

文件:/ frameworks / base / core / jni / android_view_ThreadedRenderer.cpp

能看到實際上就是調用RenderProxy的setName方法給當前硬體渲染對象設置名字。

文件:/ frameworks / base / libs / hwui / renderthread / RenderProxy.cpp

能看到在setName方法中,實際上就是調用RenderThread的WorkQueue,把一個任務隊列設置進去,並且調用runSync執行。

能看到這個方法實際上也是調用post執行排隊執行任務,不同的是,這里使用了線程的Future方式,阻塞了執行,等待CanvasContext的setName工作完畢。

⑼ 如何做好IOS View的布局

iOS 越來越人性化了,用戶可以在設置-通用-輔助功能中動態調整字體大小了。你會發現所有 iOS 自帶的APP的字體大小都變了,可惜我們開發的第三方APP依然是以前的字體。在 iOS 7 之後我們可以用 UIFont 的preferredFontForTextStyle: 類方法來指定一個樣式,並讓字體大小符合用戶設定的字體大小。目前可供選擇的有六種樣式: UIFontTextStyleHeadline UIFontTextStyleBody UIFontTextStyleSubheadline UIFontTextStyleFootnote UIFontTextStyleCaption1 UIFontTextStyleCaption2 iOS會根據樣式的用途來合理調整字體。 問題來了,諸如字體大小這種「動態類型」,我們需要對其進行動態的UI調整,否則總是覺得我們的界面怪怪的: 我們想要讓Cell 的高度隨著字體大小而作出調整: 總之,還會有其他動態因素導致我們需要修改布局。 解決方案 UITableView 有三種策略可以調節Cell(或者是Header和Footer)的高度: a.調節Height屬性 b.通過委託方法tableView: heightForRowAtIndexPath: c.Cell的「自排列」(self-sizing) 前兩種策略都是我們所熟悉的,後面將介紹第三種策略。UITableViewCell 和 UICollectionViewCell 都支持 self-sizing。 在 iOS 7 中,UITableViewDelegate新增了三個方法來滿足用戶設定Cell、Header和Footer預計高度的方法: - tableView:: - tableView:: - tableView:: 當然對應這三個方法 UITableView 也 estimatedRowHeight、estimatedSectionHeaderHeight 和 estimatedSectionFooterHeight 三個屬性,局限性在於只能統一定義所有行和節的高度。 以 Cell 為例,iOS 會根據給出的預計高度來創建一個Cell,但等到真正要顯示它的時候,iOS 8會在 self-sizing 計算得出新的 Size 並調整 table 的 contentSize 後,將 Cell 繪制顯示出來。關鍵在於如何得出 Cell 新的 Size,iOS提供了兩種方法: 自動布局 這個兩年前推出的神器雖然在一開始表現不佳,但隨著 Xcode 的越來越給力,在iOS7中自動布局儼然成了默認勾選的選項,通過設定一系列約束來使得我們的UI能夠適應各種尺寸的屏幕。如果你有使用約束的經驗,想必已經有了解決思路:向 Cell 的 contentView 添加約束。iOS 會先調用 UIView 的 systemLayoutSizeFittingSize: 方法來根據約束計算新的Size,如果你沒實現約束,systemLayoutSizeFittingSize: 會接著調用sizeThatFits:方法。 人工代碼 我們可以重寫sizeThatFits:方法來自己定義新的Size,這樣我們就不必學習約束相關的知識了。 下面我給出了一個用 Swift 語言寫的 Demo-HardChoice ,使用自動布局來調整UITableViewCell的高度。我通過實現一個UITableViewCell的子類DynamicCell來實現自動布局,你可以再GitHub上下載源碼: import UIKit class DynamicCell: UITableViewCell { required init(coder: NSCoder) { super.init(coder: coder) if textLabel != nil { textLabel.font = UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline) textLabel.numberOfLines = 0 } if detailTextLabel != nil { detailTextLabel.font = UIFont.preferredFontForTextStyle(UIFontTextStyleBody) detailTextLabel.numberOfLines = 0 } } override func constraints() -> [AnyObject] { var constraints = [AnyObject]() if textLabel != nil { constraints.extend(constraintsForView(textLabel)) } if detailTextLabel != nil { constraints.extend(constraintsForView(detailTextLabel)) } constraints.append(NSLayoutConstraint(item: contentView, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.GreaterThanOrEqual, toItem: contentView, attribute: NSLayoutAttribute.Height, multiplier: 0, constant: 44)) contentView.addConstraints(constraints) return constraints } func constraintsForView(view:UIView) -> [AnyObject]{ var constraints = [NSLayoutConstraint]() constraints.append(NSLayoutConstraint(item: view, attribute: NSLayoutAttribute.FirstBaseline, relatedBy: NSLayoutRelation.Equal, toItem: contentView, attribute: NSLayoutAttribute.Top, multiplier: 1.8, constant: 30.0)) constraints.append(NSLayoutConstraint(item: contentView, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.GreaterThanOrEqual, toItem: view, attribute: NSLayoutAttribute.Baseline, multiplier: 1.3, constant: 8)) return constraints } } 上面的代碼需要注意的是,Objective-C中的類在Swift中都可以被當做AnyObject,這在類型兼容問題上很管用。 別忘了在相應的 UITableViewController 中的 viewDidLoad 方法中加上: self.tableView.estimatedRowHeight = 44 自適應效果如下: UICollectionView UITableView 和 UICollectionView 都是 data-source 和 delegate 驅動的。UICollectionView 在此之上進行了進一步抽象。它將其子視圖的位置,大小和外觀的控制權委託給一個單獨的布局對象。通過提供一個自定義布局對象,你幾乎可以實現任何你能想像到的布局。布局繼承自 UICollectionViewLayout 抽象基類。iOS 6 中以 UICollectionViewFlowLayout 類的形式提出了一個具體的布局實現。在 UICollectionViewFlowLayout 中,self-sizing 同樣適用: 採用self-sizing後: UICollectionView 實現 self-sizing 不僅可以通過在 Cell 的 contentView 上加約束和重寫 sizeThatFits: 方法,也能在 Cell 層面(以前都是在 contentSize 上進行 self-sizing)上做文章:重寫 UICollectionReusableView 的: 方法來在 self-sizing 計算出 Size 之後再修改,這樣就達到了對Cell布局屬性()的全面控制。 PS:: 方法默認調整Size屬性來適應 self-sizing Cell,所以重寫的時候需要先調用父類方法,再在返回的 對象上做你想要做的修改。 由此我們從最經典的 UICollectionViewLayout 強制計算屬性(還記得 的一系列工廠方法么?)到使用 self-sizing 來根據我們需求調整屬性中的Size,再到重寫UICollectionReusableView(UICollectionViewCell也是繼承於它)的 : 方法來從Cell層面對所有屬性進行修改: 下面來說說如何在 UICollectionViewFlowLayout 實現 self-sizing: 首先,UICollectionViewFlowLayout 增加了estimatedItemSize 屬性,這與 UITableView 中的 」estimated...Height「 很像(注意我用省略號囊括那三種屬性),但畢竟 UICollectionView 中的 Item 都需要約束 Height 和 Width的,所以它是個 CGSIze,除了這點它與 UITableView 中的」estimated...Height「用法沒區別。 其...沒有其次,在 UICollectionView 中實現 self-sizing,只需給 estimatedItemSize 屬性賦值(不能是 CGSizeZero ),一行代碼足矣。 InvalidationContext 假如設備屏幕旋轉,或者需要展示一些其妙的效果(比如 CoverFlow ),我們需要將當前的布局失效,並重新計算布局。當然每次計算都有一定的開銷,所以我們應該謹慎的僅在我們需要的時候調用 invalidateLayout 方法來讓布局失效。 在 iOS 6 時代,有的人會「聰明地」這樣做: - (BOOL):(CGRect)newBounds { CGRect oldBounds = self.collectionView.bounds; if (CGRectGetWidth(newBounds) != CGRectGetWidth(oldBounds)) { return YES; } return NO; } 而 iOS 7 新加入的 類聲明了在布局失效時布局的哪些部分需要被更新。當數據源變更時,invalidateEverything 和 invalidateDataSourceCounts 這兩個只讀 Bool 屬性標記了UICollectionView 數據源「全部過期失效」和「Section和Item數量失效」,UICollectionView會將它們自動設定並提供給你。 你可以調用invalidateLayoutWithContext:方法並傳入一個對象,這能優化布局的更新效率。 當你自定義一個 UICollectionViewLayout 子類時,你可以調用 invalidationContextClass 方法來返回一個你定義的 的子類,這樣你的 Layout 子類在失效時會使用你自定義的InvalidationContext 子類來優化更新布局。 你還可以重寫 : 方法,在實現自定義 Layout 時通過重寫這個方法返回一個 InvalidationContext 對象。 綜上所述都是 iOS 7 中新加入的內容,並且還可以應用在 UICollectionViewFlowLayout 中。在 iOS 8 中, 也被用在self-sizing cell上。 iOS8 中 新加入了三個方法使得我們可以更加細致精密地使某一行某一節Item(Cell)、Supplementary View 或 Decoration View 失效: invalidateItemsAtIndexPaths: :atIndexPaths: :atIndexPaths: 對應著添加了三個只讀數組屬性來標記上面那三種組件: invalidatedItemIndexPaths iOS自帶的照片應用會將每一節照片的信息(時間、地點)停留顯示在最頂部,實現這種將 Header 粘在頂端的功能其實就是將那個 Index 的 Supplementary View 失效,就這么簡單。 新加入的 contentOffsetAdjustment 和 contentSizeAdjustment 屬性可以讓我們更新 CollectionView 的 content 的位移和尺寸。 此外 UICollectionViewLayout 還加入了一對兒方法來幫助我們使用self-sizing: :withOriginalAttributes: :withOriginalAttributes: 當一個self-sizing Cell發生屬性發生變化時,第一個方法會被調用,它詢問是否應該更新布局(即原布局失效),默認為NO;而第二個方法更細化的指明了哪些屬性應該更新,需要調用父類的方法獲得一個InvalidationContext 對象,然後對其做一些你想要的修改,最後返回。 試想,如果在你自定義的布局中,一個Cell的Size因為某種原因發生了變化(比如由於字體大小變化),其他的Cell會由於 self-sizing 而位置發生變化,你需要實現上面兩個方法來讓指定的Cell更新布局中的部分屬性;別忘了整個 CollectionView 的 contentSize 和 contentOffset 因此也會發生變化,你需要給 contentOffsetAdjustment 和 contentSizeAdjustment 屬性賦值。

⑽ 如何viewcontroller代碼中設置自定義UIView的寬度或高度

1.設置UIView寬與高要設置frame.
2.要看自定義.要用靜.
3.或者viewController 增加IBOutlet 鏈接自定義view獲取frame屬性知道高寬setFrame設置高寬

閱讀全文

與viewui源碼編譯相關的資料

熱點內容
卡爾曼濾波演算法書籍 瀏覽:768
安卓手機怎麼用愛思助手傳文件進蘋果手機上 瀏覽:843
安卓怎麼下載60秒生存 瀏覽:802
外向式文件夾 瀏覽:235
dospdf 瀏覽:430
怎麼修改騰訊雲伺服器ip 瀏覽:387
pdftoeps 瀏覽:492
為什麼鴻蒙那麼像安卓 瀏覽:735
安卓手機怎麼拍自媒體視頻 瀏覽:185
單片機各個中斷的初始化 瀏覽:723
python怎麼集合元素 瀏覽:480
python逐條解讀 瀏覽:832
基於單片機的濕度控制 瀏覽:498
ios如何使用安卓的帳號 瀏覽:882
程序員公園采訪 瀏覽:811
程序員實戰教程要多長時間 瀏覽:974
企業數據加密技巧 瀏覽:134
租雲伺服器開發 瀏覽:813
程序員告白媽媽不同意 瀏覽:335
攻城掠地怎麼查看伺服器 瀏覽:600