The custom control is actually very simple. The control is actually very simple.

Source: Internet
Author: User

The custom control is actually very simple. The control is actually very simple.

Respect Original reprinted Please note: From AigeStudio (http://blog.csdn.net/aigestudio) Power by Aige infringement must be investigated!

ArtilleryTown Building

There will be a huge number of things around the end of the year. Recently, our series of tutorials have not been updated due to long-planned travel events. We have been waiting for about a month, this is a lot of fun ~~ Let's talk less about it. Let's take a look at our key points in this section, in the previous section, because the clipXXX method of Canvas has never been involved before, we gave a certain introduction to it first and made a summary of the method of the Path class, as I mentioned earlier, the Canvas method can be divided into several categories. clipXXX is used for calculation, drawXXX is used for various types of operations, and Canvas is used for various transformation operations, in this section, we will take a look at some specific content about the Canvas Transformation Operation. Before explaining it, let's first understand a design concept about "layer, why is it a design concept? Because in many places, of course, it is not just development, but also design and other fields, you can see it. What is layer? When you were a child, you must have painted images, such as the following:


A squirrel, several trees, two birds, and one day, these simple pictures actually contain the simplest concept of "layer, from the figure, we can know that the squirrel must be in front of the tree and the ground, while the relationship between the tree and the ground is vague, either in front of the tree or in front of the ground, the day must be at the bottom layer. we can infer from the general logic that the second layer of the day is the second to the last layer, from the bottom layer to the top layer, we have such a hierarchical relationship: Day-bird-tree/ground-ground/tree-squirrel. It seems like so, but it's useless for visual testing ...... What's the significance? Don't worry. Just think about it. If you don't want a squirrel, but want to put a cat in front ...... Or you want to hide the squirrel behind the tree ...... In this case, you get a headache, and you have to wipe the other parts accidentally, at this time, you will wonder if you can have such a function that allows different elements to be individually painted on a piece of paper in a certain order until the last element is painted. the elements on the paper are integrated to form a complete picture? The existence of such a function can greatly improve the drawing efficiency and achieve more drawing functions. Based on such an assumption, the concept of "layer" came into being:


As shown in, the bottom layer is a circle, the second layer is a blue ellipse, and the top layer is two blue circles, different elements in the three layers form the right image, which is the most intuitive and simple embodiment of the layer. In Android, we can use the saveXXX and restoreXXX methods of Canvas to simulate similar effects of layers:

Public class LayerView extends View {private Paint mPaint; // brush object private int mViewWidth, mViewHeight; // control width and height public LayerView (Context context, AttributeSet attrs) {super (context, attrs); // instantiate the Paint brush object and set its Identifier value mPaint = new Paint (Paint. ANTI_ALIAS_FLAG | Paint. DITHER_FLAG) ;}@ Overrideprotected void onSizeChanged (int w, int h, int oldw, int oldh) {/** get control width and height */mViewWidth = w; mViewHeight = h ;} @ Overrideprotected void onDraw (Canvas canvas) {/** draw a red rectangle */mPaint. setColor (Color. RED); canvas. drawRect (mViewWidth/2F-200, mViewHeight/2F-200, mViewWidth/2F + 200, mViewHeight/2F + 200, mPaint ); /** Save the canvas and draw a blue rectangle */canvas. save (); mPaint. setColor (Color. BLUE); canvas. drawRect (mViewWidth/2F-100, mViewHeight/2F-100, mViewWidth/2F + 100, mViewHeight/2F + 100, mPaint); canvas. restore ();}}
As shown in the code, we first draw a large red rectangle in the onDraw method, and then save the canvas to draw a small blue rectangle:


At this point, we try to rotate our canvas:

@ Overrideprotected void onDraw (Canvas canvas) {// rotate the canvas. rotate (30);/** draw a red rectangle */mPaint. setColor (Color. RED); canvas. drawRect (mViewWidth/2F-200, mViewHeight/2F-200, mViewWidth/2F + 200, mViewHeight/2F + 200, mPaint ); /** Save the canvas and draw a blue rectangle */canvas. save (); mPaint. setColor (Color. BLUE); canvas. drawRect (mViewWidth/2F-100, mViewHeight/2F-100, mViewWidth/2F + 100, mViewHeight/2F + 100, mPaint); canvas. restore ();}
As shown in the code, rotate for 30 degrees clockwise. Note that when we perform coordinate operations on Canvas (in fact, most other Android-related coordinates, by default, the origin coordinates are displayed in the upper left corner of the control. The effect is as follows:


We can see that the two rectangles are flying together, but we just want to make the blue and red do not move. What should we do? It is very simple. We only need to operate on the saved layers:

@ Overrideprotected void onDraw (Canvas canvas) {/** draw a red rectangle */mPaint. setColor (Color. RED); canvas. drawRect (mViewWidth/2F-200, mViewHeight/2F-200, mViewWidth/2F + 200, mViewHeight/2F + 200, mPaint ); /** Save the canvas and draw a blue rectangle */canvas. save (); mPaint. setColor (Color. BLUE); // rotate the canvas. rotate (30); canvas. drawRect (mViewWidth/2F-100, mViewHeight/2F-100, mViewWidth/2F + 100, mViewHeight/2F + 100, mPaint); canvas. restore ();}
We can see that we only rotate the blue rectangle:


At this point, we should have a new understanding of the principles of Canvas in the previous section. We have always called it a Canvas. In fact, Canvas is a container more accurately, if you think of Canvas as a Canvas, our "layer" is like a transparent piece of paper that is clipped to the Canvas. When these sheets correspond to Android, they are bitmaps encapsulated in the Canvas.

Besides the save () method Canvas, we also provide a series of saveLayerXXX methods to save the Canvas. Unlike the save () method, the saveLayerXXX method saves all operations to a new Bitmap without affecting the Bitmap of the current Canvas. The save () method operates in the current Bitmap, the saveLayerXXX method can only perform operations on Bitmap deformation and cropping. The saveLayerXXX method is omnipotent. Of course, there are many differences between the two methods. Although the save and saveLayerXXX methods are quite different, the functions of the two methods are similar in general applications. The above code can also be changed to this:

@ Overrideprotected void onDraw (Canvas canvas) {/** draw a red rectangle */mPaint. setColor (Color. RED); canvas. drawRect (mViewWidth/2F-200, mViewHeight/2F-200, mViewWidth/2F + 200, mViewHeight/2F + 200, mPaint ); /** Save the canvas and draw a blue rectangle */canvas. saveLayer (0, 0, mViewWidth, mViewHeight, null, Canvas. ALL_SAVE_FLAG); mPaint. setColor (Color. BLUE); // rotate the canvas. rotate (30); canvas. drawRect (mViewWidth/2F-100, mViewHeight/2F-100, mViewWidth/2F + 100, mViewHeight/2F + 100, mPaint); canvas. restore ();}
Of course, the same effect is achieved. SaveLayer allows us to set the region to be saved. For example, we can only save the same area as the blue box:

@ Overrideprotected void onDraw (Canvas canvas) {/** draw a red rectangle */mPaint. setColor (Color. RED); canvas. drawRect (mViewWidth/2F-200, mViewHeight/2F-200, mViewWidth/2F + 200, mViewHeight/2F + 200, mPaint ); /** Save the canvas and draw a blue rectangle */canvas. saveLayer (mViewWidth/2F-100, mViewHeight/2F-100, mViewWidth/2F + 100, mViewHeight/2F + 100, null, Canvas. ALL_SAVE_FLAG); mPaint. setColor (Color. BLUE); // rotate the canvas. rotate (30); canvas. drawRect (mViewWidth/2F-100, mViewHeight/2F-100, mViewWidth/2F + 100, mViewHeight/2F + 100, mPaint); canvas. restore ();}
At this time, if you run it, you will find that the blue square is gone, because the size of our layers is so small that the excess parts cannot be displayed. Then we can change the canvas to rotate:

canvas.rotate(5);
You can see the corner of the blue circle after the rotation:


Is it a bit similar to clipRect? So many may wonder why there is such a function to save a small canvas area? In fact, the reason is very simple. As we mentioned above, the saveLayerXXX method will save the operation to a new Bitmap. The Bitmap size depends on the size of the input parameter, bitmap is a very dangerous object. Many friends do not understand the principle of Bitmap, which often leads to OOM. In saveLayer, we will obtain a Bitmap of the same size based on the input parameters, although this Bitmap is empty, it will occupy a certain amount of memory space. We hope to save the saved region as small as possible, while saveLayer provides such a function, which can be mentioned below, the Bitmap of the Canvas object imported by the onDraw method is theoretically infinite before HW is introduced in Android. In fact, it is still calculated based on your image. After HW is introduced, this Bitmap is restricted. For details, you can try to draw an ultra-long path and run it. You can see the warning in Logcat.

Okay, let's keep it up. Next, in addition to saveLayer, Canvas also provides a saveLayerAlpha method. As the name suggests, this method can set the transparency of the Canvas when we save the Canvas:

@ Overrideprotected void onDraw (Canvas canvas) {/** draw a red rectangle */mPaint. setColor (Color. RED); canvas. drawRect (mViewWidth/2F-200, mViewHeight/2F-200, mViewWidth/2F + 200, mViewHeight/2F + 200, mPaint ); /** Save the canvas and draw a blue rectangle */canvas. saveLayerAlpha (mViewWidth/2F-100, mViewHeight/2F-100, mViewWidth/2F + 100, mViewHeight/2F + 100, 0x55, Canvas. ALL_SAVE_FLAG); mPaint. setColor (Color. BLUE); // rotate the canvas. rotate (5); canvas. drawRect (mViewWidth/2F-100, mViewHeight/2F-100, mViewWidth/2F + 100, mViewHeight/2F + 100, mPaint); canvas. restore ();}
We replace saveLayer with saveLayerAlpha and set the transparent value to 0x55. The following results can be obtained:


The blue square is translucent. Such easy! If you pay attention, you will find that save () also has an overloaded method save (int saveFlags), while saveLayer and saveLayerAlpha will also find another similar parameter, so why is this parameter used? There are six constant values in the Canvas:


These six constant values respectively identify what we restored after calling the restore method, except CLIP_SAVE_FLAG, MATRIX_SAVE_FLAG, and ALL_SAVE_FLAG, The saveLayerXXX methods are both common. The other three methods can only make the saveLayerXXX method effective. ALL_SAVE_FLAG is also a common identifier for beginners, CLIP_SAVE_FLAG and MATRIX_SAVE_FLAG are also easy to understand. One is the cropping position and the other is the transformed flag. The labels, FULL_COLOR_LAYER_SAVE_FLAG, and labels are only valid for saveLayer and saveLayerAlpha, CLIP_TO_LAYER_SAVE_FLAG indicates that the cropping operation on the current layer needs to align the layer boundary, and FULL_COLOR_LAYER_SAVE_FLAG indicates the current graph. The color mode of the layer must be at least 8 bits, while HAS_ALPHA_LAYER_SAVE_FLAG indicates that the pixel-by-pixel Alpha blending mode will be used in the current layer. For details about the color depth and Alpha blending, refer to Wikipedia, I will not talk about them here. These identifiers, especially layer identifiers, are far beyond the scope of this series. I will not talk about them much. We can use ALL_SAVE_FLAG directly at ordinary times, I have the opportunity to take a separate analysis of Android's color processing.

All the save, saveLayer, and saveLayerAlpha methods have an int type return value. This return value is used as an identifier to give a unique ID number for your current save operation, we can use the restoreToCount (int saveCount) method to specify which save operation to restore during restoration:

@ Overrideprotected void onDraw (Canvas canvas) {/** draw a red rectangle */mPaint. setColor (Color. RED); canvas. drawRect (mViewWidth/2F-200, mViewHeight/2F-200, mViewWidth/2F + 200, mViewHeight/2F + 200, mPaint ); /** save and crop the canvas to green */int saveID1 = canvas. save (Canvas. CLIP_SAVE_FLAG); canvas. clipRect (mViewWidth/2F-200, mViewHeight/2F-200, mViewWidth/2F + 200, mViewHeight/2F + 200); canvas. drawColor (Color. GREEN);/** Save the canvas and rotate it to draw a blue rectangle */int saveID2 = canvas. save (Canvas. MATRIX_SAVE_FLAG); // rotate the canvas. rotate (5); mPaint. setColor (Color. BLUE); canvas. drawRect (mViewWidth/2F-100, mViewHeight/2F-100, mViewWidth/2F + 100, mViewHeight/2F + 100, mPaint); canvas. restoreToCount (saveID1 );}
As shown in the code above, we save the canvas for the first time and obtain its return value:

int saveID1 = canvas.save(Canvas.CLIP_SAVE_FLAG);
Crop the canvas and fill in the color. Save the canvas for the second time and obtain its return value:

int saveID2 = canvas.save(Canvas.MATRIX_SAVE_FLAG);
Then draw a blue rectangle, and then we can only restore the canvas state of saveID1. Run it and you will find that the effect is no different:


Then let's try.

canvas.restoreToCount(saveID2);
The results are still the same ............ Many children's shoes are confused. Why? No, it's actually true. You think it's strange that you still don't understand save and restore. Here I will draw a rectangle after restore:

@ Overrideprotected void onDraw (Canvas canvas) {/** draw a red rectangle */mPaint. setColor (Color. RED); canvas. drawRect (mViewWidth/2F-200, mViewHeight/2F-200, mViewWidth/2F + 200, mViewHeight/2F + 200, mPaint ); /** save and crop the canvas to green */int saveID1 = canvas. save (Canvas. CLIP_SAVE_FLAG); canvas. clipRect (mViewWidth/2F-200, mViewHeight/2F-200, mViewWidth/2F + 200, mViewHeight/2F + 200); canvas. drawColor (Color. GREEN);/** Save the canvas and rotate it to draw a blue rectangle */int saveID2 = canvas. save (Canvas. MATRIX_SAVE_FLAG); // rotate the canvas. rotate (5); mPaint. setColor (Color. BLUE); canvas. drawRect (mViewWidth/2F-100, mViewHeight/2F-100, mViewWidth/2F + 100, mViewHeight/2F + 100, mPaint); canvas. restoreToCount (saveID2); mPaint. setColor (Color. YELLOW); canvas. drawRect (mViewWidth/2F-400, mViewHeight/2F-400, mViewWidth/2F + 400, mViewHeight/2F + 400, mPaint );}
You can see that

canvas.restoreToCount(saveID2);
Then a yellow rectangle is drawn:


But no matter how you increase the size of this rectangle, you will find that it is so big ...... That is to say, the yellow rectangle is actually dropped by clip. Further, the operation of drawing the yellow rectangle is actually done in the saveID1 state. We have mentioned before that the save and saveLayerXXX methods have essential differences. The saveLayerXXX method will perform all operations in a new Bitmap, while the save method relies on the stack, suppose we have the following code:

@ Overrideprotected void onDraw (Canvas canvas) {/** save and crop the canvas to fill in green */int saveID1 = Canvas. save (Canvas. CLIP_SAVE_FLAG); canvas. clipRect (mViewWidth/2F-300, mViewHeight/2F-300, mViewWidth/2F + 300, mViewHeight/2F + 300); canvas. drawColor (Color. YELLOW);/** save and crop the canvas to fill in green */int saveID2 = canvas. save (Canvas. CLIP_SAVE_FLAG); canvas. clipRect (mViewWidth/2F-200, mViewHeight/2F-200, mViewWidth/2F + 200, mViewHeight/2F + 200); canvas. drawColor (Color. GREEN);/** Save the canvas and rotate it to draw a blue rectangle */int saveID3 = canvas. save (Canvas. MATRIX_SAVE_FLAG); canvas. rotate (5); mPaint. setColor (Color. BLUE); canvas. drawRect (mViewWidth/2F-100, mViewHeight/2F-100, mViewWidth/2F + 100, mViewHeight/2F + 100, mPaint );}
In this case, there is a Stack inside the Canvas:


Canvas will save an underlying space by Default to draw something for us. When we do not call the save method, all the drawing operations are performed in this Default Stack ID, every time we call save, an ID will be saved to the Stack, and all the subsequent operations will be carried out in the space indicated by this ID until we call the restore method to restore the operation, the above code is saved three times without restore. The stack structure is shown in. If we continue to draw something, for example:

@ Overrideprotected void onDraw (Canvas canvas) {/** save and crop the canvas to fill in green */int saveID1 = Canvas. save (Canvas. CLIP_SAVE_FLAG); canvas. clipRect (mViewWidth/2F-300, mViewHeight/2F-300, mViewWidth/2F + 300, mViewHeight/2F + 300); canvas. drawColor (Color. YELLOW);/** save and crop the canvas to fill in green */int saveID2 = canvas. save (Canvas. CLIP_SAVE_FLAG); canvas. clipRect (mViewWidth/2F-200, mViewHeight/2F-200, mViewWidth/2F + 200, mViewHeight/2F + 200); canvas. drawColor (Color. GREEN);/** Save the canvas and rotate it to draw a blue rectangle */int saveID3 = canvas. save (Canvas. MATRIX_SAVE_FLAG); // rotate the canvas. rotate (5); mPaint. setColor (Color. BLUE); canvas. drawRect (mViewWidth/2F-100, mViewHeight/2F-100, mViewWidth/2F + 100, mViewHeight/2F + 100, mPaint); mPaint. setColor (Color. CYAN); canvas. drawRect (mViewWidth/2F, mViewHeight/2F, mViewWidth/2F + 200, mViewHeight/2F + 200, mPaint );}
We drew a blue rectangle after saveID3. As long as you are not a fool, you can see that this code is drawn in the space identified by saveID3, therefore, it will inevitably be rotated by the constraint of saveID3:


In addition, we can also see that this rectangle is not only rotated, but also clip ~ That is to say, saveID1 and saveID2 also have an impact on them. At this time, we try to restore the saveID2 after drawing what we want:

/** Save and crop the canvas to green */int saveID2 = canvas. save (Canvas. CLIP_SAVE_FLAG); canvas. clipRect (mViewWidth/2F-200, mViewHeight/2F-200, mViewWidth/2F + 200, mViewHeight/2F + 200); canvas. drawColor (Color. GREEN); canvas. restore ();
At the same time, the blue rectangle is larger:

canvas.drawRect(mViewWidth / 2F, mViewHeight / 2F, mViewWidth / 2F + 400, mViewHeight / 2F + 400, mPaint);
In this case, what effect do we get:


In fact, it is guessed that saveID2 no longer takes effect on the following saveID3, that is, when we call canvas. restore (), it indicates that the last save operation is completed or rolled back. Similarly, we can restore the saveID1:

/** Save and crop the canvas to green */int saveID1 = canvas. save (Canvas. CLIP_SAVE_FLAG); canvas. clipRect (mViewWidth/2F-300, mViewHeight/2F-300, mViewWidth/2F + 300, mViewHeight/2F + 300); canvas. drawColor (Color. YELLOW); canvas. restore ();
At this time, saveID3 will not be affected by the previous operation:


If saveID3 is restored before the blue rectangle is drawn:

/** Save the canvas and rotate it to draw a blue rectangle */int saveID3 = canvas. save (Canvas. MATRIX_SAVE_FLAG); canvas. rotate (5); mPaint. setColor (Color. BLUE); canvas. drawRect (mViewWidth/2F-100, mViewHeight/2F-100, mViewWidth/2F + 100, mViewHeight/2F + 100, mPaint); canvas. restore ();
The blue rectangle will be drawn on the Default Stack ID and will not be affected by other save states:


The restoreToCount (int saveCount) method we mentioned above accepts an id value. We can use this ID value to restore a specific stack space. The effect is similar. Every time we call restore to restore the Canvas, the corresponding save Stack space will pop up from the Stack. Canvas provides the getSaveCount () method to query the storage space in the current Stack:

@ Overrideprotected void onDraw (Canvas canvas) {System. out. println (canvas. getSaveCount ();/** save and crop the canvas to fill in green */int saveID1 = canvas. save (Canvas. CLIP_SAVE_FLAG); System. out. println (canvas. getSaveCount (); canvas. clipRect (mViewWidth/2F-300, mViewHeight/2F-300, mViewWidth/2F + 300, mViewHeight/2F + 300); canvas. drawColor (Color. YELLOW); canvas. restore ();/** save and crop the canvas to green */int saveID2 = canvas. save (Canvas. CLIP_SAVE_FLAG); System. out. println (canvas. getSaveCount (); canvas. clipRect (mViewWidth/2F-200, mViewHeight/2F-200, mViewWidth/2F + 200, mViewHeight/2F + 200); canvas. drawColor (Color. GREEN); canvas. restore ();/** Save the canvas and rotate it to draw a blue rectangle */int saveID3 = canvas. save (Canvas. MATRIX_SAVE_FLAG); System. out. println (canvas. getSaveCount (); // rotate the canvas. rotate (5); mPaint. setColor (Color. BLUE); canvas. drawRect (mViewWidth/2F-100, mViewHeight/2F-100, mViewWidth/2F + 100, mViewHeight/2F + 100, mPaint); canvas. restore (); System. out. println (canvas. getSaveCount (); mPaint. setColor (Color. CYAN); canvas. drawRect (mViewWidth/2F, mViewHeight/2F, mViewWidth/2F + 400, mViewHeight/2F + 400, mPaint );}
After running, you will see the following output of Logcat:


Okay, so far from understanding the layer, let's take a look at the transform operations in the Canvas. There are only a few types of transformations: translation, rotation, scaling, and miscutting, while our Canvas inherits the essence of transformation and also provides the corresponding methods. We have used many of the previous chapters, such as translate (float dx, float dy) the method has been used to translate the canvas for countless times. Here again, the translate method will change the origin coordinates of the canvas. The influence of the origin coordinates on the transformation is very important! Scale (float sx, float sy) scaling is also well understood, but it has an overloaded method scale (float sx, float sy, float px, float py ), the last two parameters are used to specify the zoom center. The first two parameters are used to specify the zoom ratio between 0 and 1:

Public class LayerView extends View {private Bitmap mBitmap; // Bitmap object private int mViewWidth, mViewHeight; // control width and height public LayerView (Context context, AttributeSet attrs) {super (context, attrs); // obtain the bitmap object mBitmap = BitmapFactory from the resource. decodeResource (getResources (), R. drawable. z) ;}@ Overrideprotected void onSizeChanged (int w, int h, int oldw, int oldh) {/** get control width/height */mViewWidth = w; mViewHeight = h; // The zoom Bitmap is consistent with the control. mBitmap = Bitmap. createScaledBitmap (mBitmap, mViewWidth, mViewHeight, true);} @ Overrideprotected void onDraw (Canvas canvas) {canvas. save (Canvas. MATRIX_SAVE_FLAG); canvas. scale (1.0F, 1.0F); canvas. drawBitmap (mBitmap, 0, 0, null); canvas. restore ();}}
When the scaling ratio is 1, it indicates no Scaling:


Let's change the zoom ratio:

canvas.scale(0.8F, 0.35F);
The screen effect is as follows:


We can see that the zoom center is in the upper left corner. We can use the scale overload method to change the zoom center:

canvas.scale(0.8F, 0.35F, mViewWidth, 0);
The effect is as follows:


The rotate (float degrees) method and the overloaded rotate (float degrees, float px, float py) method are similar to those used earlier. I will not say much about them. Only skew (float sx, float sy) we have mentioned a lot about the concept of miscut. In fact, it is not difficult to know the principle and how to change the method:

@Overrideprotected void onDraw(Canvas canvas) {canvas.save(Canvas.MATRIX_SAVE_FLAG);canvas.skew(0.5F, 0F);canvas.drawBitmap(mBitmap, 0, 0, null);canvas.restore();}
The two parameters are similar to the scale parameter, indicating the error ratio of the vertical direction. The effect of the above Code is as follows:


In the previous chapter, we talked about a similar Matrix used to specifically operate transformation. Before that, I also said that we will use this animal in many places, canvas also provides the corresponding method for us to set the Matrix to directly transform the Canvas:

@Overrideprotected void onDraw(Canvas canvas) {canvas.save(Canvas.MATRIX_SAVE_FLAG);Matrix matrix = new Matrix();matrix.setScale(0.8F, 0.35F);matrix.postTranslate(100, 100);canvas.setMatrix(matrix);canvas.drawBitmap(mBitmap, 0, 0, null);canvas.restore();}
The running effect is as follows:


Now, let's take a look at the simple operations on the storage, restoration, and transformation of Canvas. Some of the remaining draw methods are easy to understand and understand, but I have already discussed the difficulties one after another, as part of the custom control, we used six sections to describe the rendering, most of the content is Android, which provides us with perfect interface methods so that you don't have to worry about source code implementation during upper-layer development. The following sections will start with another focus: control measurement, but before that, I would like to give you a small example of page turning effect based on the previous knowledge.

Download this part of the source code: Portal

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.