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