編輯:關於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="\" />
現在我們終於理清楚了,不能在子線程中更新ui的原因。下面是核心代碼,我們將會一步一步對其進行分析
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 下面是更新效果圖 初始文本為Click
公司項目需要做推送,我們選擇用小米推送,經過一段時間的摸索,終於可以簡單的使用小米推送了。1.創建賬號登入後 登入後選擇消息推送:2.進入後創建項目,按照步驟創建完後如下
最近心血來潮,寫了一個自定義仿iPhone的開關。有需要的同學可以來下載啦。支持點擊自動滾動,速率可以自己根據需要修改。觸摸滾動,大小自定義,支持修改樣式。就不錄制動畫,
這篇博客我們來介紹一下解釋器模式(Interpreter Pattern),也是行為型設計模式之一,是一種用的比較少的設計模式,其提供了一種解釋語言的語法或表達式的方式,
轉載請注明出處:http://blog.csdn.net/singwhatiwanna/article/details/38426471(來自singwhatiwanna