Drawable Resource Tour (II): Shape Drawable Detailed Explanation and Application (Imitated Wechat Radar Scanning)

Keywords: Android xml encoding Mobile

About the author: Speedy
Wechat Public Number: Android Tour
Blog Home Page: http://blog.csdn.net/speedystone
Nuggets column: https://juejin.im/user/57e082d60bd1d00057ff8c45

I. Preface

In our daily development, we need a lot of pictures to beautify our APP. Besides enhancing the aesthetics of UI, the large use of pictures also brings many problems, such as OOM (memory overflow), APK bulky volume, memory leak and so on. In fact, in most cases, we can use Shape Drawable to draw the graphics we need, thus effectively avoiding many problems caused by using pictures. ShapeDrawable can be understood as a graph drawn by color. It can be either a pure color picture or a gradient image. Shape Drawable is used with other hierarchical Drawables to draw very gorgeous graphics and animation effects.

2. Shape Drawable Explanation

ShapeDrawable corresponds to the < Shape > tag. Its grammar is as follows:

<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape=["rectangle" | "oval" | "line" | "ring"] >
    <corners
        android:radius="integer"
        android:topLeftRadius="integer"
        android:topRightRadius="integer"
        android:bottomLeftRadius="integer"
        android:bottomRightRadius="integer" />
    <gradient
        android:angle="integer"
        android:centerX="integer"
        android:centerY="integer"
        android:centerColor="integer"
        android:endColor="color"
        android:gradientRadius="integer"
        android:startColor="color"
        android:type=["linear" | "radial" | "sweep"]
        android:useLevel=["true" | "false"] />
    <padding
        android:left="integer"
        android:top="integer"
        android:right="integer"
        android:bottom="integer" />
    <size
        android:width="integer"
        android:height="integer" />
    <solid
        android:color="color" />
    <stroke
        android:width="integer"
        android:color="color"
        android:dashWidth="integer"
        android:dashGap="integer" />
</shape>

Detailed description of labels and attributes:

< shape >

android:shape Representation: Shape of a graph, which defines the following four types of shapes

  • Rectangle: Rectangle. Default shape, you can draw right rectangle, round rectangle, arc, etc.
  • oval: Elliptical. When the length is the same as the width, it means the right circle.
  • line: line, you can draw solid and dotted lines.
  • ring: ring, you can draw a progress bar.

When android:shape= "ring", it has the following special attributes:

  • android:thickness thickness, the thickness of the ring = outer radius - Inner radius. When it exists with thickness Ratio, thickness shall prevail.
  • Android: The inner radius of an innerRadius circle. When it coexists with innerRadius Ratio, the innerRadius shall prevail.
  • Android: Inner Radius Ratio has a radius of 9 as a percentage of the entire Drawable width. If n, then the inner radius = width / n.
  • android:thicknessRatio thickness as a percentage of the width of the entire Drawable, default value is 3. If n, then thickness = width / n.
  • android:useLevel should generally use false, otherwise it may not achieve the desired effect unless it is used as Level List Drawable.

Angle (only applicable to android:shape= "rectangle") indicates the degree of roundness of the four corners of a shape. The radius of each roundness must be greater than 1, and no roundness exists on the side.

  • android:radius sets the same angle for four corners at the same time. It has low priority and will be covered by the following.
  • Android: TopLeft Radius Angle in the upper left corner
  • Android: Top Right Radius Angle in the upper right corner
  • Android: Bottom Left Radius Angle at the lower left corner
  • Android: Bottom Right Radius Angle at the lower right corner

< gradient > Representation: Gradient effect

  • android:angle gradient angle, default is 0, its value must be 45 integer times. 0 means from left to right, 90 means from top to bottom. The specific effect changes with the angle adjustment, and the angle influences the direction of gradual change.
  • Android: abscissa points of center X gradient Center
  • Android: the ordinate point of center Y gradient Center
  • Android: Center Color Gradient Intermediate Color
  • android:endColor Gradient End Color
  • Android: radius of gradientRadius gradient (valid only if android:type is radio)
  • android:startColor Gradient Initial Color
  • android:type Gradient Type

Linear linear gradient (default)
Radial radial gradient
sweep scan line gradient
android:useLevel is generally false and is valid when Drawable is a StateList Drawable.

< padding > denotes: inner margin

  • Android: top bottom left right sets the inner margins of the upper and lower sides respectively.

< size > indicates: shape size

  • android:height specifies shape height
  • android:width specifies shape width

Explanation: Strictly speaking, shape does not have width and height, but we specify size to have so-called width and height. When shapes are used as the background of View s, shapes are stretched, so the width does not fix the shape size as much as it is specified (there is no absolute width or height for Drawable s).

Solid > Represents: Pure color filling (exclusive from gradient, pure or gradient only one)

  • android:color specifies the color of shape

Stroke > Represents: Stroke

  • android:width, the wider the shape's edge, the thicker the shape's edge line
  • Android: Colors for the Edge
  • Android: Spacing between dashGap dotted lines
  • Android: width of dashWidth dashed line

Note: If either android:dashWidth or android:dashGap is zero, the dotted line effect cannot be displayed.

3. ShapeDrawable Application Case (Imitated Wechat Radar Scanning)

First look at the screenshot of the operation effect:

Note: This case uses Layer Drawable, which will be highlighted later. Here is a brief introduction: Layer Drawable corresponds to < layer-list > label. Layer Drawable represents a hierarchical set of Drawables. By placing different Drawables on different layers, a superimposed effect can be achieved.

1. Drawing radar ripple: / res/drawable/wave.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <!--Outermost circle-->
        <shape android:shape="oval">
            <solid android:color="#10FFFFFF" />
            <size
                android:width="600dp"
                android:height="600dp" />
            <stroke
                android:color="#10B8B8B8"
                android:dashWidth="1dp" />
        </shape>
    </item>

    <item
        android:bottom="100dp"
        android:left="100dp"
        android:right="100dp"
        android:top="100dp">
        <!--Intermediate circle-->
        <shape android:shape="oval">
            <solid android:color="#1CFFFFFF" />
            <stroke
                android:color="#1CB8B8B8"
                android:dashWidth="1dp" />
        </shape>
    </item>

    <item
        android:bottom="200dp"
        android:left="200dp"
        android:right="200dp"
        android:top="200dp">

        <!--Central circle-->
        <shape android:shape="oval">
            <solid android:color="#2CFFFFFF" />
            <stroke
                android:color="#2CB8B8B8"
                android:dashWidth="1dp" />
        </shape>
    </item>
</layer-list>

Preview effect:

Drawing Scanning Beam: / res/drawable/light_beam.xml

<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">

    <size
        android:width="500dp"
        android:height="500dp" />

    <gradient
        android:endColor="#AAAAAAAA"
        android:startColor="#00000000"
        android:type="sweep"
        />
</shape>

Preview effect:

Activity layout file: / res/layout/activity_shape_drawable.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FA000000"
    android:clipToPadding="false"
    android:fitsSystemWindows="true"
    tools:context="com.speedy.shapedrawabledemo.ShapeDrawableActivity">

    <ImageView
        android:id="@+id/ivWave"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="5dp"
        android:src="@drawable/wave" />

    <ImageView
        android:id="@+id/ivLightbeam"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/light_beam"
        />

</FrameLayout>

Activity code:

/**
 *  Created by Speedy on 2017/5/09.
 */
public class ShapeDrawableActivity extends AppCompatActivity {

    private ImageView ivLightbeam;

    private ObjectAnimator radarScanAnim;   // Scanning Animation

    private int width;
    private int height;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_shape_drawable);
        ivLightbeam = (ImageView) findViewById(R.id.ivLightbeam);
    }


    @Override
    protected void onResume() {
        super.onResume();
        startScan();
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);

        if(height == 0 || width == 0){

            //Get screen length and width
            width = ScreenUtils.getScreenWidth(this);
            height = ScreenUtils.getScreenHeight(this);

            //Calculating the Scanning Circle Diameter Based on the Screen Length and Width
            int diameter= (int) Math.sqrt(Math.pow(height,2)+ Math.pow(width,2));

            //Modify the size of the beam so that it can scan the entire screen
            FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(diameter,diameter);
            ivLightbeam.setLayoutParams(layoutParams);

            //Move the center of the scanning beam to the content center of the screen
            int offsetX = (width-diameter)/2;
            int offsetY = (height-diameter)/2 + ScreenUtils.getStatusHeight(this)/2;
            ivLightbeam.setX(offsetX);
            ivLightbeam.setY(offsetY);
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        stopScan();
    }

    // Start scanning
    private void startScan(){
        radarScanAnim = ObjectAnimator.ofFloat(ivLightbeam,"rotation",0f,360f);
        radarScanAnim.setDuration(2000);    //Scan a lap in 2 seconds
        radarScanAnim.setInterpolator(new LinearInterpolator());
        radarScanAnim.setRepeatCount(ObjectAnimator.INFINITE);//Cyclic scanning

        ivLightbeam.setVisibility(View.VISIBLE);
        radarScanAnim.start();
    }

   // Stop scanning
    private void stopScan(){
        ivLightbeam.setVisibility(View.GONE);
        radarScanAnim.end();
    }
}

Operating effect screenshot:

Finally, the operation effect is as follows:

Explanation: The recorded GIF loses frames seriously, so the scan looks incoherent, and it will not work like this on the mobile phone.

Demo source download address: http://download.csdn.net/detail/easyer2012/9837222

Posted by Gordicron on Fri, 28 Jun 2019 11:31:37 -0700