preface
The background of the Activity/Window/View is the most commonly used View background, followed by the background of the Activity, and the background of the Window. What is the correlation and difference between these three? Through this article, you will learn:
View background principle and use 2. Window background principle and use 3. Activity background principle and use 4
1. Principle and use of View background
Let’s start with a simple illustration:
This article focuses on the analysis of background rendering, content rendering please move:Android custom View Draw process (part 1)
In daily use, have you ever thought about two questions:
2. Why can view.scrollto (xx) only move content
Draw the background of a View: view.draw (xx)
#View.java public void draw(Canvas canvas) { ... -------------(1) drawBackground(canvas); . if (! verticalEdges && ! HorizontalEdges) {/ / -- -- -- -- -- -- -- -- -- -- -- -- -- - (2) / / draw their own content ontouch (canvas); // Draw sub-layout dispatchDraw(canvas); . // foreground, highlight, etc return; }... }Copy the code
As can be seen from points (1) and (2) above, the background is drawn first and then the content, so the content area will cover part of the background area.
The second problem is the drawBackground(canvas) method:
# view. Java private void Drawable (Canvas Canvas) {// final Drawable background = mBackground; If (background == null) {// No background, no need to draw return; } // set background Drawable and set its size setBackgroundBounds(); / / support hardware acceleration if (canvas. IsHardwareAccelerated () && mAttachInfo! = null && mAttachInfo.mThreadedRenderer ! = null) {// Draw background, And return Drawable ------------------(1) mBackgroundRenderNode = getDrawableRenderNode(background, mBackgroundRenderNode); final RenderNode renderNode = mBackgroundRenderNode; if (renderNode ! = null && renderNode. HasDisplayList ()) {/ / mapped setBackgroundRenderNodeProperties (renderNode); DrawRenderNode (renderNode); renderNode (renderNode); return; }} final int scrollX = mScrollX; final int scrollY = mScrollY; If ((scrollX | scrollY) = = 0) {/ / not offset, directly draw the background. The draw (canvas); } else {/ / now the canvas back translation -- -- -- -- -- -- -- -- -- -- -- -- -- -- - (2) canvas. Translate (scrollX, scrollY); / / draw the background. The draw (canvas); Canvas. Translate (-scrollx, -scrolly); }}Copy the code
(1) Where the background is actually drawn:
#View.java private RenderNode getDrawableRenderNode(Drawable drawable, RenderNode RenderNode) {if (RenderNode == null) {// Create RenderNode RenderNode = RenderNode.create(drawable.getClass().getName(), new ViewAnimationHostBridge(this)); renderNode.setUsageHint(RenderNode.USAGE_BACKGROUND); } final Rect bounds = drawable.getbounds (); final int width = bounds.width(); final int height = bounds.height(); / / map to get specific background Canvas final RecordingCanvas Canvas = renderNode. BeginRecording (width, height); // Translate canvas. Translate (-bound.left, -bound.top); Try {// draw the background drawable.draw(canvas); Rendernode.endrecording (); renderNode.endrecording (); renderNode.endrecording (); }... return renderNode; }Copy the code
As you can see, a new Canvas is generated, the background is drawn with this Canvas, and the drawing is recorded in the renderNode of the background. (2) As you may have noticed, why pan the Canvas here? For software drawing, the Canvas passed from RootView is the same, that is, the entire ViewTree shares one Canvas. For View drawing, the order of method calls is as follows:
draw(x)->dispatchDraw(x)->child.draw(x1,x2,x3)->child.draw(x)
In the child. The draw (x1, x2, x3) method, determine whether the need for mobile content (mScrollX = 0 | | mScrollY! = 0), move Canvas if necessary, as follows:
canvas.translate(-mScrollX, -mScrollY)
Copy the code
Notice I’m doing it backwards. At this point, the canvas has been moved. When child.draw(xx) is called, it is the draw(xx) method analyzed above: 1. Draw the background first; 2
When you draw the background, move the Canvas back, draw the background, and then move it back. When the content is drawn again, the Canvas is still shifted (-mscrollx, -mscrolly), so when the content is drawn, it will be shifted, but when the background is drawn, it will not change, which answers the second question.
The above only answers the second question for software drawing, so why not pan Canvas when hardware accelerated drawing? Here is a brief conclusion:
In hardware-accelerated drawing, each View has its own Canvas and RenderNode, and the corresponding background also has its own Canvas and RenderNode, so even if the Canvas of the View is shifted, the Canvas of the background will not be affected. Therefore, the background Canvas does not need to be panted against mScrollX and mScrollY.
View drawing details please move:Android custom View Draw process (part 1)
Diagram the background drawing process:
#View.java public void setBackground(Drawable background){... } public void setBackgroundColor(@ColorInt int color){... } public void setBackgroundResource(@DrawableRes int resid){... }...Copy the code
Again through XML static configuration:
android:background="@color/colorGreen"
android:background="@drawable/test"
...
Copy the code
The resulting Drawable object is assigned to the View member variable mBackground, and is drawn with the Drawable when the background [drawBackground()] is drawn.
2. Principle and use of Window background
To set the Window background, we need to get the Window object. There are two common places to use the Window object: Activity and Dialog. Window is an abstract class whose implementation class is PhoneWindow, so the Window pointer in the Activity and Dialog is actually a PhoneWindow object. Get the Window reference as follows:
Activity.getWindow()
Dialog.getWindow()
Copy the code
Let’s see how Windows sets the background:
#Window.java public abstract void setBackgroundDrawable(Drawable drawable); #PhoneWindow.java @Override public final void setBackgroundDrawable(Drawable drawable) { //mBackgroundDrawable If (drawable! Drawable = mBackgroundDrawable) {// mBackgroundDrawable = drawable; if (mDecor ! = null) {/ / mDecor is known as DecorView / / mDecor call DecorView method here. The setWindowBackground (drawable); . }}}Copy the code
PhoneWindow overrides the setBackgroundDrawable(xx) method in Window. DecorView setWindowBackground(xx) is called in this method.
#DecorView.java public void setWindowBackground(Drawable drawable) { if (mOriginalBackgroundDrawable ! = drawable) { mOriginalBackgroundDrawable = drawable; // Assign the View member variable mBackground, which sets the View's background updateBackgroundDrawable(); // If (Drawable!), always set this parameter to 255. 3. = null) { mResizingBackgroundDrawable = enforceNonTranslucentBackground(drawable, mWindow.isTranslucent() || mWindow.isShowingWallpaper()); } else { mResizingBackgroundDrawable = getResizingBackgroundDrawable( mWindow.mBackgroundDrawable, mWindow.mBackgroundFallbackDrawable, mWindow.isTranslucent() || mWindow.isShowingWallpaper()); } if (mResizingBackgroundDrawable ! = null) { mResizingBackgroundDrawable.getPadding(mBackgroundPadding); } else { mBackgroundPadding.setEmpty(); } drawableChanged(); }}Copy the code
You can see that setting the background for the Window is finally fed back into the DecorView. For the Activity example, set the background of the Activity Window to green:
ColorDrawable colorDrawable = new ColorDrawable();
colorDrawable.setColor(Color.GREEN);
getWindow().setBackgroundDrawable(colorDrawable);
Copy the code
The effect is as follows:
Method call flow:
3. Principle and use of Activity background
Normally, we would set the background in the Activity Theme:
<style name="activitytheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="android:windowBackground">#0033ff</item>
</style>
Copy the code
Activity onCreate(xx) calls setContentView(xx) and then calls PhoneWindow’s generateLayout(xx) method:
#PhoneWindow.java protected ViewGroup generateLayout(DecorView decor) { ... if (getContainer() == null) { if (mBackgroundDrawable == null) { ... If (a.hasValue(r.stringable.window_windowbackground)) {mBackgroundDrawable = a.getDrawable(R.styleable.Window_windowBackground); }}... }... If (getContainer () = = null) {/ / mDecor set DecorView setWindowBackground (mBackgroundDrawable); . }... }Copy the code
The background set in the Theme is taken out here and set to the DecorView background. Strictly speaking, an Activity does not have a background; its “background” refers to the Window’s background, just for convenience. The Activity has a default background. The value varies according to the theme. The default background color of this theme is:
@color/material_grey_50
#fffafafa
So when you want to make your Activity transparent, how do you do that? Directly set the background transparent, you will find that it does not achieve the effect, but black. You need to use it with another attribute:
<style name="activitytheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">#00000000</item>
</style>
Copy the code
4. Common background Settings
You can see from the above that when setting the background of both an Activity and a Window, you end up setting the background of the DecorView. We know that to display a View on the screen, we actually need to add the View to the Window. Call the following method:
WindowManager.addView(View view, ViewGroup.LayoutParams params)
Copy the code
This view acts as the Window RootView. Take a look at some common rootViews:
- The Activity/Dialog uses a DecorView as a RootView
- PopupWindow uses PopupDecorView(when no background is set)/PopupBackgroundView(when background is set)
- Normal hover window selects any View as RootView
Setting the background is actually setting the background of the RootView
The above example illustrates the Activity background Settings, and the following describes the commonly used pop-up background Settings and matters needing attention.
Dialog background Settings
Let’s start with a simple Demo
private void showDialog(Context context) { Dialog dialog = new Dialog(context); FrameLayout frameLayout = new FrameLayout(context); TextView textView1 = new TextView(context); textView1.setText("hello"); frameLayout.addView(textView1, new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); // frameLayout.setBackgroundColor(Color.RED); ---------->(1) dialog.setContentView(frameLayout); dialog.getWindow().setLayout(800, 800); ColorDrawable colorDrawable = new ColorDrawable(); colorDrawable.setColor(Color.TRANSPARENT); // dialog.getWindow().setBackgroundDrawable(colorDrawable); --------->(2) dialog.show(); }Copy the code
Add TextView to FrameLayout and FrameLayout as Dialog ContentView as follows:
As you can see, the Dialog defaults to a background that is a rectangle with rounded corners. Now open comment (1) : Set the FrameLayout background effect as follows:
If you find that the rounded corner is missing, which background is the rounded corner background? Comment (1) out, comment (2) open, and set
colorDrawable.setColor(Color.RED);
Copy the code
The effect is as follows:
There are dark areas on the outer layer for no reason. Change the background color to transparent:
colorDrawable.setColor(Color.TRANSPARENT);
Copy the code
When I look at the effect again, I see that the entire Dialog has no background.
On this basis, open comment (1) with the following effect:
The above operation may be rather convoluted, but in fact, there are two expressions:
2. In general, don’t change your Dialog background to ColorDrawable. There will be a black background. Either make the background transparent and then set the background of the contentView; Or change the background to Shape.
Set Dialog background in two ways:
/ / dynamic dialog. GetWindow (). SetBackgroundDrawable (colorDrawable); / / static / / set style < style name = "myDialog" > < item name = "android: windowBackground" > @ android: color/transparent < / item > < / style >Copy the code
PopupWindow background Settings
PopupWindow does not use Window and does not use DecorView as RootView.
private void showPopupWindow(Context context, View anchor) { TextView textView1 = new TextView(context); textView1.setText("heloo jj"); PopupWindow popupWindow = new PopupWindow(textView1, 300, 300, true); ColorDrawable colorDrawable = new ColorDrawable(); // colorDrawable.setColor(Color.GREEN); -------------->(1) popupWindow.setBackgroundDrawable(colorDrawable); popupWindow.showAsDropDown(anchor); }Copy the code
Run the Demo above:
As you can see, PopupWindow has no background. Open comment (1) with the following effect:
The background has been added. What I want to express:
PopupWindow if no background is set, the background will be transparent.
When you set the PopupWindow background, a PopupBackgroundView is generated as the RootView for the PopupWindow, and setting the PopupWindow background is the background for setting the PopupBackgroundView
PopupWindow background can be set in two ways:
/ / dynamic popupWindow. SetBackgroundDrawable (colorDrawable); // static // set style <style name="myPopupWindow"> <item name=" Android :popupBackground">@color/green</item> </style>Copy the code
Normal floating window background Settings
Windowmanager.addview (View View, viewGroup.layoutParams params) You need to set the RootView background, which is the background of the view of the above method, otherwise the background will be black. Floating Windows have been analyzed many times, but for more details, go to Window/WindowManager
This article is based on Android 10.0