sequence

I have not combed through the knowledge of the View system, and I used to write a custom View involving details on Google. Recently, I worked on an AOSP project under Deepin. I was ready to learn how to write a custom View again from source code. Before starting, I wrote a simple custom View to review the process of custom View.

TaggedSeekBar adds a TAB to the SeekBar to display the progress value, which can also be used as a ProgressBar. The result is as follows:

knowledge

TaggedSeekBar is a direct descendant of the View and, while primitive, does contain most of the knowledge of a custom View. Such as:

  1. View coordinate system
  2. Paint Basic Properties
  3. Canvas basic drawing function
  4. OnMeasure Measures itself
  5. OnTouchEvent handles user interaction

Details will not expand to say, there are a lot of big men have written specifically.

process

When writing a custom View, the most important first step is to “unwrap” it. Good unpacking makes the parameters in XML easier to understand and simplifies the calculation of draw coordinates in onDraw.

I split the TaggedSeekBar into three parts: Progress bar, Thumb and Tag (the tag is divided into arrow and body), as shown here (see my soulful hand drawing) :

Split to meet the needs of the main, as far as possible to ensure good scalability. Once you’re done, you can start coding

1. Configure XML attributes

When the names of multiple custom ATTRs conflict, the definition of the attR can be extracted to the outer layer. The structure looks like this:

<? xml version="1.0" encoding="utf-8"? > <resources> <attr name="progressWidth" format="dimension|reference"/>
    <declare-styleable name="TaggedSeekBar">
        <attr name="progressWidth"/>
    </declare-styleable>
    <declare-styleable name="XXProgressBar">
        <attr name="progressWidth"/>
    </declare-styleable>
</resources>
Copy the code

2. Read the properties and set the default values

The disadvantage of XML configuration parameters is that you can’t restrict attribute associations, and each attribute may not be written, so you need to fill in default values when retrieving attributes.

Default values should be set with View element dependencies in mind, and fixed values should be minimized to keep things as easy as possible.

3. Measurement and positioning

The layout mode of View is in line with our requirements, and there is no need to rewrite onLayout. OnMeasure needs to be rewritten to support WRAP_content. The progress bar is horizontal, so width can be as wide as possible, height can be wrap_content, and minimum height is fine as long as the full content is displayed.

// When height is AT_MOST,  //height = tagHeight+ThumbHeight+indicatorHeight height = paddingBottom + paddingTop + thumbRadius * 2 + thumbStrokeWidth * 2 + indicatorHeight + tagHeight).toInt()Copy the code

4. Draw in layers

The post-drawn content in onDraw is overlaid, which determines the order in which coordinates are computed. TaggedSeekBar is drawn in the following order: Progress bar background -> Progress bar -> Thumb -> Tag

1. Background color of progress bar (rounded rectangle)

2. Progress bar (rounded rectangle, covered with background color)

3.Thumb

4. TagIndicator

You can use Path to draw graphics not included in the API. Reset is required before reuse.

5. Tag (rounded rectangle)

5. Respond to the Touch event

Event processing starts with the response ACTION_DOWN. After getting the coordinates of the event, the first step is to determine whether the click position supports dragging.

If it’s not supported, consider it nothing, and if it is, start processing ACTION_MOVE and redrawing itself.

The Listener configuration is mainly to meet the requirements. There are only two types of Listener configuration: one is to call back the progress as you drag the Listener, and the other is to call back once after you release the Listener.

Tips

I ran into some minor problems during coding and blogging. Note this by the way.

1. Name conflicts in attrs. XML

I didn’t pay much attention to it before. I usually get by with a different name. The correct solution looks like this:

2. Record a GIF on a real machine

When running code in an emulator, you can record giFs directly using LICEcap. Running on a phone is a bit more complicated. Adb does not support recording giFs, so you can record videos and convert them to GIFs. Conversion tool recommended ffmpeg command, a line of code to fix:

  • -i | input file
  • – vf scale = 360: – 1 | conversion scale size at the same time, the aspect ratio of 360:1, 1 said to keep adaptive high proportion

Just contact FFMPEG, feel a lot of use, write it separately after the study.

The full code and sample project can be viewed at Github.

I’ve been using Kotlin for a year now, and I haven’t blogged about the UI for almost a year. I’ve revamped the project, and there are other custom View updates.

Any questions are welcome to comment and exchange