Solve problems with minimal code.

Direct up effect

Demand analysis

As shown in the figure above, you need to add a dot before each line of text. What implementation can you think of?

Combination of the view? pass… Can you kill a chicken with a sword?

DrawableLeft is the best way to use native implementation! DrawableLeft on multiple lines is centered vertically with the height of the entire text, which is definitely not what we want.

So let’s take a look at TextView source code.

Source code analysis

DrawableLeft: drawableLeft: drawableLeft: drawableLeft: drawableLeft

/ / the constructor TextView case com. Android. Internal. R.s tyleable. TextView_drawableLeft: drawableLeft = al-qeada etDrawable (attr); break;Copy the code

Where will it be used next

/ / the constructor TextView / / This call will save the initial left/right drawables setCompoundDrawablesWithIntrinsicBounds ( drawableLeft, drawableTop, drawableRight, drawableBottom);Copy the code

See what this method does?

@android.view.RemotableViewMethod public void setCompoundDrawablesWithIntrinsicBounds(@Nullable Drawable left, @Nullable Drawable top, @Nullable Drawable right, @Nullable Drawable bottom) { if (left ! = null) { left.setBounds(0, 0, left.getIntrinsicWidth(), left.getIntrinsicHeight()); } if (right ! = null) { right.setBounds(0, 0, right.getIntrinsicWidth(), right.getIntrinsicHeight()); } if (top ! = null) { top.setBounds(0, 0, top.getIntrinsicWidth(), top.getIntrinsicHeight()); } if (bottom ! = null) { bottom.setBounds(0, 0, bottom.getIntrinsicWidth(), bottom.getIntrinsicHeight()); } setCompoundDrawables(left, top, right, bottom); }Copy the code

If you look at this method, you can see that setBounds can set the default drawable size. The setCompoundDrawables() method does some state saving and redrawing applications. There are a lot of breakthrough points when you look at this.

First to autotype setCompoundDrawablesWithIntrinsicBounds () to set the Bounds of drawableLeft, but can’t, because to realize the function of demand, need to rely on the current line number for dynamic calculation, This method is called in the constructor and the number of lines is never zero. It is not possible to make changes by overwriting this method.

The onMeasure() and onLayout() methods are also not suitable for operation. And onDraw(), let’s see.

int vspace = bottom - top - compoundPaddingBottom - compoundPaddingTop; int hspace = right - left - compoundPaddingRight - compoundPaddingLeft; // IMPORTANT: The coordinates computed are also used in invalidateDrawable() // Make sure to update invalidateDrawable() when changing  this code. if (dr.mShowing[Drawables.LEFT] ! = null) { canvas.save(); canvas.translate(scrollX + mPaddingLeft + leftOffset, scrollY + compoundPaddingTop + (vspace - dr.mDrawableHeightLeft) / 2); dr.mShowing[Drawables.LEFT].draw(canvas); canvas.restore(); }Copy the code

This is not operable either, but we can get a hint of how the drawableleft is centered with the text. It’s the height of the text subtracting the height of the drawableleft, and then dividing by 2, so it’s perfectly centered.

Again change path, we said before, setCompoundDrawablesWithIntrinsicBounds () method, it can set the Bounds of drawableleft, but wrong time, then we will give it to find the right timing. OnLayout () is a good time to rearrange the text as it changes. Rearrange the text so you can use the scalpel!

To solve the problem

The core of this is how to calculate the difference between the display position to the first line and the display position to the middle position according to the height of the text. Because TextView is centered in onDraw() (we can’t change that), we fill it out in onLayout() by moving the drawableleft up on onLayou(), In onDraw(), the position is moved down, the offset is erased, and no matter how many lines of text there are, the effect is the same as the first line of text.

Get it, Get it

Layout in XML files

/ / in the XML file layout < com. Kejiyuanren. SpecialTextView android: id = "@ + id/special_text_view" android: drawableLeft = "@ drawable/icon" android:drawablePadding="10dp" android:layout_width="wrap_content" android:layout_height="wrap_content"/>Copy the code

The implementation code

public class SpecialTextView extends TextView { private static final String TAG = "SpecialTextView"; public SpecialTextView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); SetText (" Techno techno techno techno techno techno techno techno techno techno "+" techno techno techno techno techno techno techno techno techno techno techno techno techno techno techno techno techno techno techno techno techno techno techno techno "); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); handleLeftDrawable(); } private void handleLeftDrawable() { Drawable leftDrawable = getCompoundDrawables()[0]; if (leftDrawable == null) { return; Int lineCount = math.min (getLineCount(), getMaxLines())); Int vsPace = getBottom() -getTop () -getCompoundPaddingBottom () -getCompoundPaddingTop (); Int verticalOffset = (int) (-1 * (vsPace * (1-1.0f/lineCount)) / 2); / / reset Bounds leftDrawable. SetBounds (0, verticalOffset, leftDrawable. GetIntrinsicWidth (), leftDrawable.getIntrinsicHeight() + verticalOffset); }}Copy the code

Xiaobian blog series

Android problem resolution notes