Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.

The effect

Use the following code:

<com.lloydfinch.mooneffect.NumberView
    android:id="@+id/tv_number"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="#FF0000"
    android:padding="8dp"

    app:nv_num0="@drawable/ic_0"
    app:nv_num1="@drawable/ic_1"
    app:nv_num2="@drawable/ic_2"
    app:nv_num3="@drawable/ic_3"
    app:nv_num4="@drawable/ic_4"
    app:nv_num5="@drawable/ic_5"
    app:nv_num6="@drawable/ic_6"
    app:nv_num7="@drawable/ic_7"
    app:nv_num8="@drawable/ic_8"
    app:nv_num9="@drawable/ic_9"

    app:nv_number="12345689" />
Copy the code

Use nv_num to specify the corresponding numeric image, and then use nv_number to specify the number.

Analysis & Code

It is impossible for us to draw such text, it is too laborious, so we can use the 10 digital pictures from 0 to 9 to establish a corresponding relationship with the numbers, and then find the pictures and draw them according to the numbers.

So we need:

  • 1 provides a total of 10 digital pictures from 0 to 9.
  • Establish the mapping of each digit to the picture.
  • 3 Break down the numbers into digits and find the corresponding picture according to each digit.
  • 4 Draw the corresponding picture.

Ok, now that the logic is in place, let’s implement it:

  • 1 We provide 10 digital images from IC_0 to IC_9, and we define a style so that users can manually specify these images.
<! --> <declare-styleable name="NumberView"> <! - to show the Numbers -- -- > < attr name = "nv_number format =" string "/" > <! <attr name="nv_num1" format="reference" /> <attr name="nv_num2" format="reference" /> <attr name="nv_num3" format="reference" /> <attr name="nv_num4" format="reference" /> <attr name="nv_num5" format="reference" /> <attr name="nv_num6" format="reference" /> <attr name="nv_num7" format="reference" /> <attr name="nv_num8" format="reference" /> <attr name="nv_num9" format="reference" /> <attr name="nv_num0" format="reference" /> </declare-styleable>Copy the code
  • 2 We set up a Map to store the mapping between numbers and pictures:
Private Map<Integer, Integer> numsMap = new HashMap<>(); Numsmap. put(0, array.getResourceId(r.tyleable.numberView_nv_num0, 0)); numsMap.put(1, array.getResourceId(R.styleable.NumberView_nv_num1, 0)); numsMap.put(2, array.getResourceId(R.styleable.NumberView_nv_num2, 0)); numsMap.put(3, array.getResourceId(R.styleable.NumberView_nv_num3, 0)); numsMap.put(4, array.getResourceId(R.styleable.NumberView_nv_num4, 0)); numsMap.put(5, array.getResourceId(R.styleable.NumberView_nv_num5, 0)); numsMap.put(6, array.getResourceId(R.styleable.NumberView_nv_num6, 0)); numsMap.put(7, array.getResourceId(R.styleable.NumberView_nv_num7, 0)); numsMap.put(8, array.getResourceId(R.styleable.NumberView_nv_num8, 0)); numsMap.put(9, array.getResourceId(R.styleable.NumberView_nv_num9, 0));Copy the code
  • 3 Disassemble the numbers into digits and save the pictures corresponding to each number for drawing. Pay attention to the sequence here.
private void refreshNumber() { int length = text.length(); Pics = new Bitmap[length]; Int number = integer.parseint (text); while (number ! = 0) {// We take the units digit backwards and forwards, So from the picture is put forward after pics. [- length] = BitmapFactory decodeResource (getResources (), numsMap. Get (number % 10)); number /= 10; } } catch (NumberFormatException e) { e.printStackTrace(); }}Copy the code
  • 4. Draw the image, which takes the padding into account.
@Override protected void onDraw(Canvas canvas) { float left = getPaddingStart(); float top = getPaddingTop(); For (Bitmap PIC: pics) {canvas.drawBitmap(PIC, left, top, mPaint); left += pic.getWidth(); }}Copy the code

One caveat here is that the onMeasure() function needs to be handled to prevent irregularities in the digital image given by the user.

The complete code looks like this:

Public class NumberView extends View {private String text = "0"; private Paint mPaint; Private Map<Integer, Integer> numsMap = new HashMap<>(); Private Bitmap[] pics; public NumberView(Context context) { this(context, null); } public NumberView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public NumberView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.NumberView); text = array.getString(R.styleable.NumberView_nv_number); numsMap.put(0, array.getResourceId(R.styleable.NumberView_nv_num0, 0)); numsMap.put(1, array.getResourceId(R.styleable.NumberView_nv_num1, 0)); numsMap.put(2, array.getResourceId(R.styleable.NumberView_nv_num2, 0)); numsMap.put(3, array.getResourceId(R.styleable.NumberView_nv_num3, 0)); numsMap.put(4, array.getResourceId(R.styleable.NumberView_nv_num4, 0)); numsMap.put(5, array.getResourceId(R.styleable.NumberView_nv_num5, 0)); numsMap.put(6, array.getResourceId(R.styleable.NumberView_nv_num6, 0)); numsMap.put(7, array.getResourceId(R.styleable.NumberView_nv_num7, 0)); numsMap.put(8, array.getResourceId(R.styleable.NumberView_nv_num8, 0)); numsMap.put(9, array.getResourceId(R.styleable.NumberView_nv_num9, 0)); array.recycle(); init(); } private void init() { mPaint = new Paint(); mPaint.setAntiAlias(true); refreshNumber(); } private void refreshNumber() {int length = text.length(); pics = new Bitmap[length]; Int number = integer.parseint (text); while (number ! = 0) { pics[--length] = BitmapFactory.decodeResource(getResources(), numsMap.get(number % 10)); number /= 10; } } catch (NumberFormatException e) { e.printStackTrace(); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMeasureMode = MeasureSpec.getMode(widthMeasureSpec); int heightMeasureMode = MeasureSpec.getMode(heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); If (widthMeasureMode == MeasureSpec.AT_MOST) {widthSize = 0; if (widthMeasureMode == MeasureSpec. widthSize += (getPaddingStart() + getPaddingEnd()); for (Bitmap pic : pics) { widthSize += pic.getWidth(); If (heightMeasureMode == MeasureSpec.AT_MOST) {heightSize = 0; heightSize += (getPaddingTop() + getPaddingBottom()); int maxHeight = 0; for (Bitmap pic : pics) { maxHeight = Math.max(pic.getHeight(), maxHeight); } heightSize += maxHeight; } setMeasuredDimension(MeasureSpec.makeMeasureSpec(widthSize, widthMeasureMode), MeasureSpec.makeMeasureSpec(heightSize, heightMeasureMode)); } @Override protected void onDraw(Canvas canvas) { float left = getPaddingStart(); float top = getPaddingTop(); For (Bitmap PIC: pics) {canvas.drawBitmap(PIC, left, top, mPaint); left += pic.getWidth(); }} public void setText(String text) {this.text = text; refreshNumber(); }}Copy the code

conclusion

The core idea is just one: this thing can’t be drawn in a moment, so don’t worry about it. Even if it is finally drawn, it will be a waste of time and energy to install an X. In other words, don’t do anything that doesn’t pay your bills.

The same is true: for custom views that inherit directly from a View, you need to manually handle wrap_content and padding.