編輯:關於Android編程
接下來我們只分析updateViewLayout()方法。
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.updateViewLayout(view, params);
}
applyDefaultToken(params);方法和Window的層級有關系,這裡和我們探討的view的跟新沒有關系,因此跳過
mGlobal.updateViewLayout(view, params); 發現windowManager的更新其實是交給了mGlobal來操作了,那麼mGlobal是什麼呢?
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();發現mGlobal其實是WindowManaerImpl一個成員變量,而且還是單例。其實WindowManagerImpl的跟新委托給了WindowManagerGlobal
那麼WindowManagerGlobal的updateViewLayout()方法裡面完成了什麼功能呢?
public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
view.setLayoutParams(wparams);
synchronized (mLock) {
int index = findViewLocked(view, true);
ViewRootImpl root = mRoots.get(index);
mParams.remove(index);
mParams.add(index, wparams);
root.setLayoutParams(wparams, false);
}
}
前半部分是異常判斷,跳過下面是給view設置布局參數,新的布局參數。
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params; view.setLayoutParams(wparams);
下面是找到viewRootImpl,給root重新設置布局參數。
int index = findViewLocked(view, true);
ViewRootImpl root = mRoots.get(index);
mParams.remove(index);
mParams.add(index, wparams);
root.setLayoutParams(wparams, false);
那麼ViewRootImpl是什麼呢?其實是android系統中view和WindowManager通訊的橋梁。比如測量 布局 繪制 時間分發 都是在這裡傳遞給view的
接下來我們分析 root.setLayoutParams(wparams, false);這段代碼。
if (newView) {
mSoftInputMode = attrs.softInputMode;
requestLayout();
}
代碼比較長,這裡截取部分代碼 requestLayout();
那麼requestLayout中做了什麼操作呢?
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
終於到了重點 checkThread(),在這個方法中做了一個判斷,就是當前更新ui的線程是否和ViewRootImpl創建的線程是否是同一個,不是則拋出異常下面是checkThread代碼
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
那麼mThread是什麼時候創建的呢?下面我們看下ViewRootImpl的構造方法
* 那麼viewRootImpl對象什麼時候創建的呢?其實在WindowManagerImpl的addview中調用了WindowManagerGlobal的addview。在WindowManagerGlobal的addView的時候創建了ViewRootImpl對象
<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPjxpbWcgYWx0PQ=="" src="/uploadfile/Collfiles/20160824/20160824092324623.png" title="\" />


下面是核心代碼,我們將會一步一步對其進行分析
new Thread() {
@Override
public void run() {
Looper.prepare();
wm = (WindowManager) MyApplication.ctx.getSystemService(WINDOW_SERVICE);
view = View.inflate(MainActivity.this, R.layout.item, null);
tv = (TextView) view.findViewById(R.id.tv);
params = new WindowManager.LayoutParams();
params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;// 設置最大的層級 以便顯示在其他應用的上面
// 設置不攔截焦點
params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
params.width = (int) (60 * getResources().getDisplayMetrics().density);
params.height = (int) (60 * getResources().getDisplayMetrics().density);
params.gravity = Gravity.LEFT | Gravity.TOP;// 且設置坐標系 左上角
params.format = PixelFormat.TRANSPARENT;
width = wm.getDefaultDisplay().getWidth();
height = wm.getDefaultDisplay().getHeight();
params.y = height / 2 - params.height / 2;
wm.addView(view, params);
view.setOnTouchListener(new View.OnTouchListener() {
private int y;
private int x;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
x = (int) event.getRawX();
y = (int) event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
int minX = (int) (event.getRawX() - x);
int minY = (int) (event.getRawY() - y);
params.x = Math.min(width - params.width, Math.max(0, minX + params.x));
params.y = Math.min(height - params.height, Math.max(0, minY + params.y));
wm.updateViewLayout(view, params);
x = (int) event.getRawX();
y = (int) event.getRawY();
break;
case MotionEvent.ACTION_UP:
if (params.x > 0 && params.x < width - params.width) {
int x = params.x;
if (x > (width - params.width) / 2) {
params.x = width - params.width;
} else {
params.x = 0;
}
wm.updateViewLayout(view, params);
} else if (params.x == 0 || params.x == (width - params.width)) {
Toast.makeText(MainActivity.this, "被電擊了", Toast.LENGTH_SHORT).show();
tv.setText("abcd");
}
break;
}
return true;
}
});
Looper.loop();
}
}.start();
首先准備Looper,之後loop。因為更新view的時候會在當前的子線程中使用handler。而使用handler必須要looper。 接下來拿到windowManager wm = (WindowManager) MyApplication.ctx.getSystemService(WINDOW_SERVICE); 填充view WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; 設置type,將window的級別設置較大,能夠顯示在其他的window之上 params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;這裡是設置window透傳,也就是當前view所在的window不阻礙底層的window獲得觸摸事件。 接下來設置window的寬度和高度 params.format = PixelFormat.TRANSPARENT;設置透明 否則的話 圓形view後面顯示一層黑色,默認效果是黑色。需要設置,才能體現出圓形。 接下來就是設置Gravity了,這裡比較簡單,因為想實現懸浮窗口的拖拽效果,因此需要修改WindowManager的LayoutParams的x,y值。因此需要和gravity配合使用 接下來就是將view添加到WindowManager中了 剩下的就是觸摸事件了 在松手的時候判斷了,更新了view中顯示的ui 下面是更新效果圖

Android小米推送簡單使用方法
公司項目需要做推送,我們選擇用小米推送,經過一段時間的摸索,終於可以簡單的使用小米推送了。1.創建賬號登入後 登入後選擇消息推送:2.進入後創建項目,按照步驟創建完後如下
android 自定義開關(SwitchButton)
最近心血來潮,寫了一個自定義仿iPhone的開關。有需要的同學可以來下載啦。支持點擊自動滾動,速率可以自己根據需要修改。觸摸滾動,大小自定義,支持修改樣式。就不錄制動畫,
java/android 設計模式學習筆記(23)---解釋器模式
這篇博客我們來介紹一下解釋器模式(Interpreter Pattern),也是行為型設計模式之一,是一種用的比較少的設計模式,其提供了一種解釋語言的語法或表達式的方式,
Android View系統解析(下)
轉載請注明出處:http://blog.csdn.net/singwhatiwanna/article/details/38426471(來自singwhatiwanna