1.androidå¼å设置å±è½å½å¶
2.炫酷!这些都是Android中不规则形状View的布局实现!
3.Android UIç»å¶ä¹Viewç»å¶çå·¥ä½åç
4.èªå®ä¹View(1)
androidå¼å设置å±è½å½å¶
项ç®å¼åä¸ï¼ä¸ºäºç¨æ·ä¿¡æ¯çå®å ¨ï¼ä¼æç¦æ¢é¡µé¢è¢«æªå±ãå½å±çéæ±ãè¿ç±»èµæï¼å¨ç½ä¸æå¾å¤ï¼ä¸è¬é½æ¯éè¿è®¾ç½®ActivityçFlag解å³ï¼å¦ï¼
//ç¦æ¢é¡µé¢è¢«æªå±ãå½å±getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
è¿ç§è®¾ç½®å¯è§£å³ä¸è¬çé²æªå±ãå½å±çéæ±ã
å¦æ页é¢ä¸æå¼¹åºPopupwindowï¼å¨å½å±è§é¢ä¸çæææ¯ï¼
éPopupwindowåºå为é»è²
ä½Popupwindowåºåä»ç¶æ¯å¯ä»¥çå°ç
å¦ä¸é¢ä¸¤å¼ Gifå¾æ示ï¼
æªè®¾ç½®FLAG_SECUREï¼å½å±çææï¼å¦ä¸å¾ï¼gitå¾çä¸é´çæ°´å°å¿½ç¥ï¼ï¼
设置äºFLAG_SECUREä¹åï¼å½å±çææï¼å¦ä¸å¾ï¼gitå¾çä¸é´çæ°´å°å¿½ç¥ï¼ï¼
åå åæ
çå°äºä¸é¢çææï¼æ们å¯è½ä¼æçé®PopupWindowä¸åDialogæèªå·±çwindow对象ï¼èæ¯ä½¿ç¨WindowManager.addViewæ¹æ³å°Viewæ¾ç¤ºå¨Activityçªä½ä¸çãé£ä¹ï¼Activityå·²ç»è®¾ç½®äºFLAG_SECUREï¼ä¸ºä»ä¹å½å±æ¶è¿è½çå°PopupWindowï¼
æ们å éè¿getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);æ¥åæä¸æºç ï¼
1ãWindow.java
//windowå¸å±åæ°private final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();//æ·»å æ è¯public void addFlags(int flags) {
setFlags(flags, flags);
}//éè¿mWindowAttributes设置æ è¯public void setFlags(int flags, int mask) { final WindowManager.LayoutParams attrs = getAttributes();
attrs.flags = (attrs.flags&~mask) | (flags&mask);
mForcedWindowFlags |= mask;
dispatchWindowAttributesChanged(attrs);
}//è·å¾å¸å±åæ°å¯¹è±¡ï¼å³mWindowAttributespublic final WindowManager.LayoutParams getAttributes() { return mWindowAttributes;
}
éè¿æºç å¯ä»¥çå°ï¼è®¾ç½®windowå±æ§çæºç é常ç®åï¼å³ï¼éè¿windowéçå¸å±åæ°å¯¹è±¡mWindowAttributes设置æ è¯å³å¯ã
2ãPopupWindow.java
//æ¾ç¤ºPopupWindowpublic void showAtLocation(View parent, int gravity, int x, int y) {
mParentRootView = new WeakReference<>(parent.getRootView());
showAtLocation(parent.getWindowToken(), gravity, x, y);
}//æ¾ç¤ºPopupWindowpublic void showAtLocation(IBinder token, int gravity, int x, int y) { if (isShowing() || mContentView == null) { return;
}
TransitionManager.endTransitions(mDecorView);
detachFromAnchor();
mIsShowing = true;
mIsDropdown = false;
mGravity = gravity;
//å建Windowå¸å±åæ°å¯¹è±¡
final WindowManager.LayoutParams p =createPopupLayoutParams(token);
preparePopup(p);
p.x = x;
p.y = y;
invokePopup(p);
}//å建Windowå¸å±åæ°å¯¹è±¡protected final WindowManager.LayoutParams createPopupLayoutParams(IBinder token) { final WindowManager.LayoutParams p = new WindowManager.LayoutParams();
p.gravity = computeGravity();
p.flags = computeFlags(p.flags);
p.type = mWindowLayoutType;
p.token = token;
p.softInputMode = mSoftInputMode;
p.windowAnimations = computeAnimationResource(); if (mBackground != null) {
p.format = mBackground.getOpacity();
} else {
p.format = PixelFormat.TRANSLUCENT;
} if (mHeightMode < 0) {
p.height = mLastHeight = mHeightMode;
} else {
p.height = mLastHeight = mHeight;
} if (mWidthMode < 0) {
p.width = mLastWidth = mWidthMode;
} else {
p.width = mLastWidth = mWidth;
}
p.privateFlags = PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH
| PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
p.setTitle("PopupWindow:" + Integer.toHexString(hashCode())); return p;
}//å°PopupWindowæ·»å å°Windowä¸private void invokePopup(WindowManager.LayoutParams p) { if (mContext != null) {
p.packageName = mContext.getPackageName();
} final PopupDecorView decorView = mDecorView;
decorView.setFitsSystemWindows(mLayoutInsetDecor);
setLayoutDirectionFromAnchor();
mWindowManager.addView(decorView, p); if (mEnterTransition != null) {
decorView.requestEnterTransition(mEnterTransition);
}
}
éè¿PopupWindowçæºç åæï¼æ们ä¸é¾çåºï¼å¨è°ç¨showAtLocationæ¶ï¼ä¼åç¬å建ä¸ä¸ªWindowManager.LayoutParamså¸å±åæ°å¯¹è±¡ï¼ç¨äºæ¾ç¤ºPopupWindowï¼è该å¸å±åæ°å¯¹è±¡ä¸å¹¶æªè®¾ç½®ä»»ä½é²æ¢æªå±Flagã
å¦ä½è§£å³
åå æ¢ç¶æ¾å°äºï¼é£ä¹å¦ä½å¤çå¢ï¼
åå头åæä¸Windowçå ³é®ä»£ç ï¼
//éè¿mWindowAttributes设置æ è¯public void setFlags(int flags, int mask) { final WindowManager.LayoutParams attrs = getAttributes();
attrs.flags = (attrs.flags&~mask) | (flags&mask);
mForcedWindowFlags |= mask;
dispatchWindowAttributesChanged(attrs);
}
å ¶å®åªéè¦è·å¾WindowManager.LayoutParams对象ï¼å设置ä¸flagå³å¯ã
ä½æ¯PopupWindow并没æåActivityä¸æ ·æç´æ¥è·å¾windowçæ¹æ³ï¼æ´å«è¯´è®¾ç½®Flagäºãæ们ååæä¸PopupWindowçæºç ï¼
//å°PopupWindowæ·»å å°Windowä¸private void invokePopup(WindowManager.LayoutParams p) { if (mContext != null) {
p.packageName = mContext.getPackageName();
}
final PopupDecorView decorView = mDecorView;
decorView.setFitsSystemWindows(mLayoutInsetDecor);
setLayoutDirectionFromAnchor(); //æ·»å View
mWindowManager.addView(decorView, p); if (mEnterTransition != null) {
decorView.requestEnterTransition(mEnterTransition);
}
}
æ们è°ç¨showAtLocationï¼æç»é½ä¼æ§è¡mWindowManager.addView(decorView, p);
é£ä¹æ¯å¦å¯ä»¥å¨addViewä¹åè·åå°WindowManager.LayoutParamså¢ï¼
çæ¡å¾ææ¾ï¼é»è®¤æ¯ä¸å¯ä»¥çãå 为PopupWindow并没æå ¬å¼è·åWindowManager.LayoutParamsçæ¹æ³ï¼èä¸mWindowManagerä¹æ¯ç§æçã
å¦ä½æè½è§£å³å¢ï¼
æ们å¯ä»¥éè¿hookçæ¹å¼è§£å³è¿ä¸ªé®é¢ãæ们å 使ç¨å¨æ代çæ¦æªPopupWindowç±»çaddViewæ¹æ³ï¼æ¿å°WindowManager.LayoutParams对象ï¼è®¾ç½®å¯¹åºFlagï¼ååå°è·å¾mWindowManager对象å»æ§è¡addViewæ¹æ³ã
é£é©åæï¼
ä¸è¿ï¼éè¿hookçæ¹å¼ä¹æä¸å®çé£é©ï¼å 为mWindowManageræ¯ç§æ对象ï¼ä¸åPublicçAPIï¼è°·æåç»å级Androidçæ¬ä¸ä¼èèå ¶å ¼å®¹æ§ï¼æ以æå¯è½åç»Androidçæ¬ä¸æ¹äºå ¶å称ï¼é£ä¹æ们éè¿åå°è·å¾mWindowManager对象ä¸å°±æé®é¢äºãä¸è¿ä»å代çæ¬çAndroidæºç å»çï¼mWindowManager被æ¹çå çä¸å¤§ï¼æ以hookä¹æ¯å¯ä»¥ç¨çï¼æ们尽éå代ç æ¶èèä¸è¿ç§é£é©ï¼é¿å 以ååºé®é¢ã
public class PopupWindow {
...... private WindowManager mWindowManager;
......
}
èaddViewæ¹æ³æ¯ViewMangeræ¥å£çå ¬å ±æ¹æ³ï¼æ们å¯ä»¥æ¾å¿ä½¿ç¨ã
public interface ViewManager{ public void addView(View view, ViewGroup.LayoutParams params); public void updateViewLayout(View view, ViewGroup.LayoutParams params); public void removeView(View view);
}
åè½å®ç°
èèå°hookçå¯ç»´æ¤æ§åæ©å±æ§ï¼æ们å°ç¸å ³ä»£ç å°è£ æä¸ä¸ªç¬ç«çå·¥å ·ç±»å§ã
package com.ccc.ddd.testpopupwindow.utils;
import android.os.Handler;
import android.view.WindowManager;
import android.widget.PopupWindow;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class PopNoRecordProxy implements InvocationHandler { private Object mWindowManager;//PopupWindowç±»çmWindowManager对象
public static PopNoRecordProxy instance() { return new PopNoRecordProxy();
} public void noScreenRecord(PopupWindow popupWindow) { if (popupWindow == null) { return;
} try { //éè¿åå°è·å¾PopupWindowç±»çç§æ对象ï¼mWindowManager
Field windowManagerField = PopupWindow.class.getDeclaredField("mWindowManager");
windowManagerField.setAccessible(true);
mWindowManager = windowManagerField.get(popupWindow); if(mWindowManager == null){ return;
} //å建WindowManagerçå¨æ代ç对象proxy
Object proxy = Proxy.newProxyInstance(Handler.class.getClassLoader(), new Class[]{ WindowManager.class}, this); //æ³¨å ¥å¨æ代ç对象proxyï¼å³ï¼mWindowManager对象ç±proxy对象æ¥ä»£çï¼
windowManagerField.set(popupWindow, proxy);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { //æ¦æªæ¹æ³mWindowManager.addView(View view, ViewGroup.LayoutParams params);
if (method != null && method.getName() != null && method.getName().equals("addView")
&& args != null && args.length == 2) { //è·åWindowManager.LayoutParamsï¼å³ï¼ViewGroup.LayoutParams
WindowManager.LayoutParams params = (WindowManager.LayoutParams) args[1]; //ç¦æ¢å½å±
setNoScreenRecord(params);
}
} catch (Exception ex) {
ex.printStackTrace();
} return method.invoke(mWindowManager, args);
} /
*** ç¦æ¢å½å±
*/
private void setNoScreenRecord(WindowManager.LayoutParams params) {
setFlags(params, WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
} /
*** å 许å½å±
*/
private void setAllowScreenRecord(WindowManager.LayoutParams params) {
setFlags(params, 0, WindowManager.LayoutParams.FLAG_SECURE);
} /
*** 设置WindowManager.LayoutParams flagå±æ§ï¼åèç³»ç»ç±»Window.setFlags(int flags, int mask)ï¼
*
* @param params WindowManager.LayoutParams
* @param flags The new window flags (see WindowManager.LayoutParams).
* @param mask Which of the window flag bits to modify.
*/
private void setFlags(WindowManager.LayoutParams params, int flags, int mask) { try { if (params == null) { return;
} params.flags = (params.flags & ~mask) | (flags & mask);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
Popwindowç¦æ¢å½å±å·¥å ·ç±»ç使ç¨ï¼ä»£ç 示ä¾ï¼
//å建PopupWindow
//æ£å¸¸é¡¹ç®ä¸ï¼è¯¥æ¹æ³å¯æ¹æå·¥åç±»
//æ£å¸¸é¡¹ç®ä¸ï¼ä¹å¯èªå®ä¹PopupWindowï¼å¨å ¶ç±»ä¸è®¾ç½®ç¦æ¢å½å±
private PopupWindow createPopupWindow(View view, int width, int height) {
PopupWindow popupWindow = new PopupWindow(view, width, height); //PopupWindowç¦æ¢å½å±
PopNoRecordProxy.instance().noScreenRecord(popupWindow); return popupWindow;
} //æ¾ç¤ºPopupwindow
private void showPm() {
View view = LayoutInflater.from(this).inflate(R.layout.pm1, null);
PopupWindow pw = createPopupWindow(view,ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
pw1.setFocusable(false);
pw1.showAtLocation(this.getWindow().getDecorView(), Gravity.BOTTOM | Gravity.RIGHT, PopConst.PopOffsetX, PopConst.PopOffsetY);
}
å½å±ææå¾ï¼
炫酷!这些都是Android中不规则形状View的布局实现!
在Android开发中,我们经常面临一个问题:如何创建非矩形形状的源库源码View?在官方提供的工具中,我们找不到直接创建非矩形View的解决方案。通常,开发者需要自己定义一个View,但这涉及到大量的代码重写,并且可能会导致一些特性的丢失,或产生bug。
自定义View虽然可以实现非矩形形状,但其触摸区域仍然是矩形的。如果想要让特定区域的触摸无效,需要在`onTouchEvent`中进行复杂的计算。对于ViewGroup而言,115论坛源码这将更加复杂,因为需要重写`dispatchTouchEvent`。
这时候,`ClipPathLayout`这个强大的布局出现了,它能够对子View的Path进行裁剪,完美解决了非矩形View的布局问题。
`ClipPathLayout`,顾名思义,spark join 源码就是一种可以裁剪子View绘制范围和触摸范围的布局,从而实现非矩形的View。
那么,`ClipPathLayout`有什么实际用途呢?答案是:它可以让你的View以非矩形形状绘制,并且可以控制触摸范围,实现更自由的交互设计。
让我们通过实例来看看`ClipPathLayout`的效果。例如,网页源码购买你可以将一个方形裁剪成圆形,并且让圆形View的四个角成为触摸无效的区域。或者,你甚至可以使用它来制作不规则形状的按钮,如阴阳鱼的图案。
那么,如何使用`ClipPathLayout`呢?首先,确保你已经添加了依赖库到你的欢迎网站源码项目中。接着,在你的ViewGroup中添加一个子View,并使用`PathInfo`来配置Path,从而实现非矩形的布局。
使用`ClipPathLayout`的过程相对简单,只需要几步就可以让你的View以非矩形形状展示,并且可以控制触摸区域。它的实现原理基于对Canvas的裁剪,通过Path信息控制Canvas的绘制范围。
如果你想深入了解`ClipPathLayout`的工作原理,那么,它的工作机制实际上涉及到了对Path的生成、绘制以及触摸事件的处理。`ClipPathLayout`使用了Path来定义非矩形区域,并在Canvas绘制之前对Canvas进行裁剪,这样可以控制View的显示范围。对于触摸事件的处理,它通过在`dispatchTouchEvent`方法中判断触摸点是否在裁剪后的区域内来实现。
如果你对`ClipPathLayout`的实现原理感兴趣,可以查阅相关源码进行深入学习。此外,`ClipPathLayout`还提供了自定义Path生成器的功能,允许你根据需求实现不同的非矩形布局。
最后,`ClipPathLayout`不仅仅局限于简单的圆形或不规则形状的布局,通过自定义Path生成器,你可以实现更多复杂且独特的界面设计。对于那些追求界面创新的开发者来说,`ClipPathLayout`无疑是一个强大的工具。
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 代表ä¸ä¸ªä½çintå¼ï¼é«2ä½ä»£è¡¨ SpecMode ,ä½ä½ä»£è¡¨ 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å¼åèºæ¯æ¢ç´¢ã
èªå®ä¹View(1)
ç»åè¿åé¢ä¸ç¯å°å°å¦å¦çåºç¡ç¯ä¹åï¼ç»äºå°äºè¿é¶ç¯ï¼æ£å¼è¿å ¥è§£æèªå®ä¹Viewçé¶æ®µãè³äºæ¬ç« è为ä»ä¹è¦å«è¿é¶ç¯(è½ç¶è®²çæ¯åºç¡çå 容)ï¼å 为ä»æ¬ç¯å¼å§ï¼å°ä¼éæ¸æå¼èªå®ä¹Viewçç¥ç§é¢çº±ï¼æ¯ä¸ç¯é½å°æ¯ä¸ä¸ç¯å 容æ´å æ·±å ¥ï¼å©ç¨æå¦çç¥è¯è½å¤å¶ä½æ´å ç«é ·èªå®ä¹Viewï¼å°±åå¨å°é¶ä¸ä¸æ ·ï¼æ¯ä¸ç¯é½æ´ä¸ä¸å±ï¼ 帮å©å¤§å®¶ä¸æ¥æ¥èµ°å人çå· å³°ï¼åºä»»CEOï¼èµ¢åç½å¯ç¾ã 误ï¼æ¯å¸®å©å¤§å®¶æ´å äºè§£é£äºç«é ·çèªå®ä¹Viewæ¯å¦ä½å¶ä½çï¼è¾¾å°ä¸¾ä¸åä¸çææã
ä½ä¸ºä¸ä¸ª<b>æ(hui)追(zhuang)æ±(B)</b>çç¨åºåï¼è¯å®æ³åä¸äºè®©äººç¼åä¸äº®çç¨åºææï¼ä½æ¯ç³»ç»æä¾çé£äºä¸è¬å¾é¾æ»¡è¶³ï¼ä¸ºäº<b>梦(zhuang)æ³(B)</b>å°±å¿ é¡»è¦å¦ä¹ ä¸äºèªå®ä¹Viewãä¸é¢æ们就äºè§£ä¸äºèªå®ä¹Viewç¸å ³çä¸è¥¿ã
èªå®ä¹ViewGroupä¸è¬æ¯å©ç¨ç°æçç»ä»¶æ ¹æ®ç¹å®çå¸å±æ¹å¼æ¥ç»ææ°çç»ä»¶ï¼å¤§å¤ç»§æ¿èªViewGroupæåç§Layoutï¼å å«æåViewã
ä¾å¦ï¼ä¸ä¸ªåºç¨å çåºé¨å¯¼èªæ¡ä¸çæ¡ç®ï¼ä¸è¬é½æ¯ä¸é¢ä¸ºå¾æ ï¼ä¸é¢æ¯æåï¼é£ä¹è¿ä¸¤ä¸ªå°±å¯ä»¥ç¨èªå®ä¹ViewGroupç»åæ为ä¸ä¸ªVeiwï¼æä¾ä¸¤ä¸ªå±æ§åå«ç¨æ¥è®¾ç½®æååå¾çå³å¯ï¼è¿æ ·ä½¿ç¨èµ·æ¥ä¼æ¹ä¾¿å¾å¤ã
å¨æ²¡æç°æçViewï¼éè¦èªå·±å®ç°çæ¶åï¼å°±ä½¿ç¨èªå®ä¹Viewï¼ä¸è¬ç»§æ¿èªViewï¼SurfaceViewæå ¶ä»çViewï¼ä¸å å«åViewã
ä¾å¦ï¼å®ä¹ä¸ä¸ªæ¯æèªå¨å è½½ç½ç»å¾ççImageViewï¼æå¶ä½ä¸ç§ç¹æ®çå¨ç»ææã
<b>ä¸è¬æ¥è¯´ï¼èªå®ä¹Viewå¨å¤§å¤æ°æ åµä¸é½ææ¿ä»£æ¹æ¡ï¼å©ç¨å¾çæè ç»åå¨ç»æ¥å®ç°ï¼ä½æ¯ä½¿ç¨åè å¯è½ä¼é¢ä¸´å åèè´¹è¿å¤§ï¼å¶ä½éº»ç¦æ´è¯¸å¤é®é¢ã</b>
Viewçæé å½æ°æåç§éè½½åå«å¦ä¸
å¯ä»¥çåºï¼å ³äºViewæé å½æ°çåæ°æå¤æå°ï¼å æé¤å 个ä¸å¸¸ç¨çï¼çä¸å¸¸ç¨çåç 究ã
<b>æå个åæ°çæé å½æ°å¨APIçæ¶åææ·»å ä¸ï¼æä¸è¬ä¸ä½¿ç¨ï¼æä¸èèã</b>
æä¸ä¸ªåæ°çæé å½æ°ä¸ç¬¬ä¸ä¸ªåæ°æ¯é»è®¤çStyleï¼è¿éçé»è®¤çStyleæ¯æå®å¨å½åApplicationæActivityæç¨çThemeä¸çé»è®¤Styleï¼ä¸åªæå¨æç¡®è°ç¨çæ¶åæä¼çæï¼ä»¥ç³»ç»ä¸çImageButton为ä¾è¯´æï¼
<b>注æï¼å³ä½¿ä½ å¨Viewä¸ä½¿ç¨äºStyleè¿ä¸ªå±æ§ä¹ä¸ä¼è°ç¨ä¸ä¸ªåæ°çæé å½æ°ï¼æè°ç¨çä¾æ§æ¯ä¸¤ä¸ªåæ°çæé å½æ°ã</b>
<b>ç±äºä¸ä¸ªåæ°çæé å½æ°ç¬¬ä¸ä¸ªåæ°ä¸è¬ä¸ç¨ï¼æä¸èèï¼ç¬¬ä¸ä¸ªåæ°çå ·ä½ç¨æ³ä¼å¨ä»¥åç¨å°çæ¶å详ç»ä»ç»ã</b>
æé¤äºä¸¤ä¸ªä¹åï¼åªå©ä¸ä¸ä¸ªåæ°å两个åæ°çæé å½æ°ï¼ä»ä»¬ç详æ å¦ä¸ï¼
以ä¸æ¹æ³è°ç¨çæ¯<b>ä¸ä¸ªåæ°</b>çæé å½æ°ï¼
以ä¸æ¹æ³è°ç¨çæ¯<b>两个åæ°</b>çæé å½æ°ï¼
å ³äºæé å½æ°å 讲è¿ä¹å¤ï¼å ³äºå¦ä½èªå®ä¹å±æ§å使ç¨attrsä¸çå 容ï¼å¨åé¢ä¼è¯¦ç»è®²è§£ï¼ç®ååªéè¦ç¥éè¿ä¸¤ä¸ªæé å½æ°å¨ä½æ¶è°ç¨å³å¯ã
========
æµéView大å°ä½¿ç¨çæ¯onMeasureå½æ°ï¼æ们å¯ä»¥ä»è¿ä¸¤ä¸ªåæ°ååºå®½é«çç¸å ³æ°æ®ï¼
ä»ä¸é¢å¯ä»¥çåº onMeasure å½æ°ä¸æ widthMeasureSpec å heightMeasureSpec è¿ä¸¤ä¸ª int ç±»åçåæ°ï¼ 毫æ çé®ä»ä»¬æ¯å宽é«ç¸å ³çï¼ <b>ä½å®ä»¬å ¶å®ä¸æ¯å®½åé«ï¼ èæ¯ç±å®½ãé«ååèªæ¹åä¸å¯¹åºç模å¼æ¥åæçä¸ä¸ªå¼ï¼</b>
å¨intç±»åçä½äºè¿å¶ä½ä¸ï¼-è¿ä¸¤ä½è¡¨ç¤ºæ¨¡å¼,~0è¿ä¸åä½è¡¨ç¤ºå®½åé«çå®é å¼ã
以æ°å¼(äºè¿å¶ä¸º: )为ä¾(å ¶ä¸æ¨¡å¼åå®é æ°å¼æ¯è¿å¨ä¸èµ·çï¼ä¸ºäºå±ç¤ºæå°ä»ä»¬åå¼äº)ï¼
å®é ä¸å ³äºä¸é¢çä¸è¥¿äºè§£å³å¯ï¼å¨å®é è¿ç¨ä¹ä¸åªéè¦è®°ä½æä¸ç§æ¨¡å¼ï¼ç¨ MeasureSpec ç getSizeæ¯è·åæ°å¼ï¼ getModeæ¯è·å模å¼å³å¯ã
å¦æ对Viewç宽é«è¿è¡ä¿®æ¹äºï¼<b>ä¸è¦è°ç¨super.onMeasure(widthMeasureSpec,heightMeasureSpec);</b>
è¦è°ç¨<b>setMeasuredDimension(widthsize,heightsize);</b> è¿ä¸ªå½æ°ã
======
è¿ä¸ªå½æ°å¨è§å¾å¤§å°åçæ¹åæ¶è°ç¨ï¼
onSizeChangedå¦ä¸ï¼
å¯ä»¥çåºï¼å®åå个åæ°ï¼åå«ä¸º 宽度ï¼é«åº¦ï¼ä¸ä¸æ¬¡å®½åº¦ï¼ä¸ä¸æ¬¡é«åº¦ã
è¿ä¸ªå½æ°æ¯è¾ç®åï¼æ们åªéå ³æ³¨ 宽度(w), é«åº¦(h) å³å¯ï¼è¿ä¸¤ä¸ªåæ°å°±æ¯Viewæç»ç大å°ã
=========
<b>ç¡®å®å¸å±çå½æ°æ¯onLayoutï¼å®ç¨äºç¡®å®åViewçä½ç½®ï¼å¨èªå®ä¹ViewGroupä¸ä¼ç¨å°ï¼ä»è°ç¨çæ¯åViewçlayoutå½æ°ã</b>
ä¸è¿å ³äºViewçlayoutå½æ°æ们ä¸è¬æ éå ³æ³¨ï¼å 为å¨ä¸è¬æ åµä¸æ们åªéå ³æ³¨Viewèªèº«çåæ ç³»å³å¯ï¼é¤éViewç¶æä¸å¨ç¶VIewæå¤ä½ç½®ç¸å ³ã
å¨èªå®ä¹ViewGroupä¸ï¼onLayoutä¸è¬æ¯å¾ªç¯ååºåViewï¼ç¶åç»è¿è®¡ç®å¾åºå个åViewä½ç½®çåæ å¼ï¼ç¶åç¨ä»¥ä¸å½æ°è®¾ç½®åViewä½ç½®ã
å个åæ°åå«ä¸ºï¼
å ·ä½å¯ä»¥åè åæ ç³» è¿ç¯æç« :
PSï¼å ³äºonLayoutè¿ä¸ªå½æ°å¨è®²è§£èªå®ä¹ViewGroupçæ¶åä¼è¯¦ç»è®²è§£ã
========
onDrawæ¯å®é ç»å¶çé¨åï¼ä¹å°±æ¯æ们çæ£å ³å¿çé¨åï¼ä½¿ç¨çæ¯Canvasç»å¾ã
å ³äºCanvasç»å¾å¦åä¸ç« å§ï¼æ¬æ¥æ³åä¸äºå ³äºCanvasåºæ¬æä½ççï¼å¯æ¯ç¯å¹ 太é¿äºQAQï¼ ç个尾巴ä¸ä¸ç¯ååå§ï¼æ¯ç«Canvasç»å¾ä¹æ¯ä¸ä¸ªæ¯è¾åºå¤§çä¸è¥¿ï¼ä¹ä¸æ¯ä¸è¨ä¸¤è¯å°±è½è®²æç½çï¼å°±å°è¿éå§ã
======
èªå®ä¹å®Viewä¹åï¼ä¸è¬ä¼å¯¹å¤æ´é²ä¸äºæ¥å£ï¼ç¨äºæä½Viewçç¸å ³å±æ§ï¼æ§å¶Viewçç¶æçï¼æè éè¦çå¬Viewçååï¼å ·ä½è¿æ¯ç¨åå讲å§(继ç»æå)ã
PS ï¼å®é ä¸ViewGroupæ¯Viewçä¸ä¸ªåç±»ã
View
ViewGroup
View.MeasureSpec
onMeasureï¼MeasureSpecæºç æµç¨ æ路详解
Androidä¸èªå®ä¹æ ·å¼ä¸Viewçæé å½æ°ä¸ç第ä¸ä¸ªåæ°defStyleçæä¹
android viewæé å½æ°ç 究
Android Viewæé æ¹æ³ç¬¬ä¸åæ°ä½¿ç¨æ¹æ³è¯¦è§£
Android èªå®ä¹View onMeasureæ¹æ³çå®ç°
Android APIæå(äº)èªå®ä¹æ§ä»¶ä¹ onMeasure
Androidä¸Viewçç»å¶è¿ç¨ onMeasureæ¹æ³ç®è¿°