1. 程式人生 > >Android文字排版實現

Android文字排版實現

        在專案中有一個小功能需要實現,就是對多行文字進行排版佈局,每一行的內容又分為兩部分,左邊為標題,右邊為描述,左邊內容長度不確定,右邊的內容需要對齊,如有換行也需要對齊右邊的文字。

效果圖

       效果圖如下圖所示:

這裡寫圖片描述

       可以看到內容分成了兩部分,左邊的顏色與右邊不一致,右邊的描述文案統一對齊。

實現方案

       以上功能,由於輸入內容輸入行數不確定,並且左邊的文案長度也不確定,因此不能直接在佈局中實現,基於此這裡主要實現了以下6種方式

方案1

       採用自定義控制元件的方式,繼承TextView,重新onDraw函式,實現如下:


/**
 * 計算出左邊最長的顯示字串maxLeftWidth,之後draw每一行字元,右邊的描述從maxLeftWidth開始draw
 * 當一行顯示不完全時,折行並且空出maxLeftWidth的空格長度
 */
public class TypographyView1 extends TextView {

    private Paint leftPaint = new Paint();
    private Paint rightPaint = new Paint();
    private int fullWidth;
    private float textSize;
    private
JSONArray array; private int middlePadding = 0; float maxLeftWidth = 0; int itemSize = 0; public TypographyView1(Context context) { super(context); init(); } public TypographyView1(Context context, AttributeSet attrs) { super(context, attrs); init(); } public
TypographyView1(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { textSize = getResources().getDimensionPixelSize(R.dimen.text_size_13); leftPaint.setAntiAlias(true); leftPaint.setTextSize(textSize); leftPaint.setColor(getResources().getColor(R.color.color_black_999999)); rightPaint.setAntiAlias(true); rightPaint.setTextSize(textSize); rightPaint.setColor(getResources().getColor(R.color.color_black)); middlePadding = getResources().getDimensionPixelSize(R.dimen.padding_value); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); fullWidth = getWidth();// 整個textView的寬度 } public void setText(JSONArray array) { this.array = array; if (array != null) { try { int size = itemSize = array.length(); for (int i = 0; i < size; ++i) { JSONArray o = (JSONArray) array.get(i); String key = o.getString(0); String value = o.getString(1); if (TextUtils.isEmpty(key) || TextUtils.isEmpty(value)) { itemSize--; continue; } float curWidth = leftPaint.measureText(key); if (curWidth > maxLeftWidth) { maxLeftWidth = curWidth; } } maxLeftWidth = maxLeftWidth + middlePadding; invalidate(); } catch (Exception e) { } } } boolean setHeight = false; @Override protected void onDraw(Canvas canvas) { if (array == null) { return; } int lineCount = 0; try { JSONArray item; float offsetY; for (int i = 0; i < itemSize; ++i) { item = (JSONArray) array.get(i); offsetY = (lineCount + 1) * textSize; canvas.drawText(item.getString(0), 0, offsetY, leftPaint); String value = item.getString(1); float valueWidth = rightPaint.measureText(value); if (valueWidth > fullWidth - maxLeftWidth) {// 一行顯示不完 char[] textCharArray = value.toCharArray(); float charWidth; float drawWidth = maxLeftWidth; for (int j = 0; j < textCharArray.length; j++) { charWidth = rightPaint.measureText(textCharArray, j, 1); if (fullWidth - drawWidth < charWidth) { lineCount++; drawWidth = maxLeftWidth; offsetY += textSize; } canvas.drawText(textCharArray, j, 1, drawWidth, offsetY, rightPaint); drawWidth += charWidth; } } else { canvas.drawText(value, maxLeftWidth, offsetY, rightPaint); } lineCount += 2; } if (!setHeight) { setHeight((lineCount + 1) * (int) textSize); setHeight = true; } } catch (JSONException e) { e.printStackTrace(); } } }

       添加了setText(JSONArray array)作為資料輸入,並且在這裡面測量了左邊title的最大寬度,之後呼叫invalidate觸發重繪,在onSizeChanged獲取整個控制元件的寬度,重繪會呼叫onDraw函式,這裡不需要呼叫super函式,TextView的onDraw函式做了非常多的操作,解析傳入的資料,分別一行一行呼叫canvas來進行drawText操作,當繪製描述時,先計算寬度,如果超過剩餘控制元件說明需要換行,最後呼叫setHeight設定高度,這個加一個判斷條件,因為會觸發requestLayout()進行重新佈局和invalidate()進行重繪,如果不加判斷會一直重繪。

方案2

       方式2與方式1差不多,不同為所有計算都在onDraw函式中:

/**
 * 該方式與方式1很類似,只是所有的計算都放在了onDraw方法中。
 */
public class TypographyView2 extends TextView {

    private Paint paint1 = new Paint();
    private Paint paint2 = new Paint();
    private int middlePadding = 0;
    int width;
    private float textSize;
    private JSONArray array;

    public TypographyView2(Context context) {
        super(context);
        init();
    }

    public TypographyView2(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public TypographyView2(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        textSize = getResources().getDimensionPixelSize(R.dimen.text_size_13);
        paint1.setAntiAlias(true);
        paint1.setTextSize(textSize);
        paint1.setColor(getResources().getColor(R.color.color_black_999999));
        paint2.setAntiAlias(true);
        paint2.setTextSize(textSize);
        paint2.setColor(getResources().getColor(R.color.color_black));
        middlePadding = getResources().getDimensionPixelSize(R.dimen.padding_value);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        width = getWidth();// 整個textView的寬度
    }

    public void setText(JSONArray array) {
        this.array = array;
        if (array != null) {
            invalidate();
        }
    }

    boolean setHeight = false;

    @Override
    protected void onDraw(Canvas canvas) {
        // super.onDraw(canvas);
        int lineCount = 0;
        int size = array.length();
        float maxLeftWidth = 0;
        float drawWidth = 0;
        try {
            for (int i = 0; i < size; ++i) {
                JSONArray o = (JSONArray) array.get(i);
                String key = o.getString(0);
                float v = paint1.measureText(key);
                if (v > maxLeftWidth) {
                    maxLeftWidth = v;
                }
            }
            maxLeftWidth = maxLeftWidth + middlePadding;
            for (int i = 0; i < size; ++i) {
                JSONArray o = (JSONArray) array.get(i);
                String key = o.getString(0);
                canvas.drawText(key, 0, (lineCount + 1) * textSize, paint1);
                String value = o.getString(1);
                char[] textCharArray = value.toCharArray();
                float charWidth;
                drawWidth = maxLeftWidth;
                for (int j = 0; j < textCharArray.length; j++) {
                    charWidth = paint1.measureText(textCharArray, j, 1);
                    if (width - drawWidth < charWidth) {
                        lineCount++;
                        drawWidth = maxLeftWidth;
                    }
                    canvas.drawText(textCharArray, j, 1, drawWidth, (lineCount + 1) * textSize, paint2);
                    drawWidth += charWidth;
                }
                lineCount += 2;
            }
            if (!setHeight) {
                setHeight((lineCount + 1) * (int) textSize + 5);
                setHeight = true;
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }
}

       該方案的實現是不太好的,方案1也是在此基礎上進行調整的,在這裡放出來只是為了說明,所有的計算不要全部放在onDraw裡面,因為該方法可能會反覆呼叫多次,這樣就降低了效能。

方案3

       將資料來源拼接成SpannableString,重寫onDraw函式,根據內容draw每一個字元:

/**
 * 該方法,是需要顯示的內容先拼接成SpannableString,在onDraw方法中獲取所有的char字元,一個一個比較
 * 當為分號是,表示為key與value的分隔符。
 */
public class TypographyView3 extends TextView {

    private Paint leftPaint = new Paint();
    private Paint rightPaint = new Paint();
    int width;
    private String text;
    private float textSize;
    float maxLeftWidth = 0;
    private int middlePadding = 0;

    public TypographyView3(Context context) {
        super(context);
        init();
    }

    public TypographyView3(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public TypographyView3(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        textSize = getResources().getDimensionPixelSize(R.dimen.text_size_13);
        textSize = getResources().getDimensionPixelSize(R.dimen.text_size_13);
        leftPaint.setAntiAlias(true);
        leftPaint.setTextSize(textSize);
        leftPaint.setColor(getResources().getColor(R.color.color_black_999999));
        rightPaint.setAntiAlias(true);
        rightPaint.setTextSize(textSize);
        rightPaint.setColor(getResources().getColor(R.color.color_black));
        middlePadding = getResources().getDimensionPixelSize(R.dimen.padding_value);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        width = getWidth();// 整個textView的寬度
    }

    public void setText(JSONArray data) {
        if (data == null) {
            return;
        }
        try {
            int size = data.length();
            for (int i = 0; i < size; ++i) {
                JSONArray o = (JSONArray) data.get(i);
                String key = o.getString(0);
                float v = leftPaint.measureText(key);
                if (v > maxLeftWidth) {
                    maxLeftWidth = v;
                }
            }
            maxLeftWidth += middlePadding;
            SpannableStringBuilder ssb = new SpannableStringBuilder();
            for (int i = 0; i < size; ++i) {
                addItem((JSONArray) data.get(i), ssb, i != 0);
            }
            setText(ssb, BufferType.SPANNABLE);
        } catch (Exception e) {

        }
    }

    private void addItem(JSONArray item, SpannableStringBuilder ssb, boolean breakLine) {
        try {
            if (item == null || item.length() == 0) {
                return;
            }
            String key = item.getString(0);
            String value = (item.length() >= 2) ? item.getString(1) : "";
            if (TextUtils.isEmpty(key) && TextUtils.isEmpty(value)) {
                return;
            }
            if (breakLine) {// 換行
                ssb.append("\r\n");
                ssb.append("\r\n");
            }
            SpannableString span = new SpannableString(key);
            //            span.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.colorAccent)), 0, key
            // .length(),
            //                    Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
            ssb.append(span);
            ssb.append(value);

        } catch (JSONException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // super.onDraw(canvas);
        int lineCount = 0;
        text = this.getText().toString();
        if (text == null)
            return;
        char[] textCharArray = text.toCharArray();
        // 已繪的寬度
        float drawWidth = 0;
        float charWidth;
        Paint paint = leftPaint;
        for (int i = 0; i < textCharArray.length; i++) {
            charWidth = leftPaint.measureText(textCharArray, i, 1);

            if (textCharArray[i] == '\n') {
                lineCount++;
                drawWidth = 0;
                paint = leftPaint;
                continue;
            }
            if (width - drawWidth < charWidth) {
                lineCount++;
                drawWidth = maxLeftWidth;
            }
            if (i > 1 && textCharArray[i - 1] == ':') {
                drawWidth = maxLeftWidth;
                paint = rightPaint;
            }
            canvas.drawText(textCharArray, i, 1, drawWidth, (lineCount + 1) * textSize, paint);
            drawWidth += charWidth;
        }
        //may be need set height
        //setHeight((lineCount + 1) * (int) textSize + 5);
    }
}

       這裡先計算左邊title的最大寬度,同時將所有的資料拼接成一個SpannableStringBuilder,呼叫setText函式會觸發重繪,在onDraw函式中進行處理,由於未重新super函式,因此SpannableString的setSpan函式失效,該方案主要根據分隔符來進行分割,因此分隔符需要唯一。

方案4

       採用GridLayout方式實現,但是原始控制元件有展示問題,因此對此進行了修改:

public class Typography4Activity extends BaseActivity {

    public static void start(Context context) {
        Intent intent = new Intent();
        intent.setClass(context, Typography4Activity.class);
        context.startActivity(intent);
    }

    private LinearLayout root;
    private Paint leftPaint = new Paint();
    private float textSize;
    private float maxLeftWidth;
    private int middlePadding = 0;
    private float maxRightWidth;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        root = (LinearLayout) LayoutInflater.from(this).inflate(R.layout.activity_typography4, null);
        setContentView(root);
        initPaint();
        findViews();
        loadData();
    }

    private void initPaint() {
        textSize = getResources().getDimensionPixelSize(R.dimen.text_size_13);
        leftPaint.setAntiAlias(true);
        leftPaint.setTextSize(textSize);
        leftPaint.setColor(getResources().getColor(R.color.color_black_999999));
        middlePadding = getResources().getDimensionPixelSize(R.dimen.padding_value);
    }

    private void findViews() {

    }

    private void loadData() {
        addGridLayout(DataSource.getArray());
        TextView view = new TextView(this);
        view.setText("修改後的實現");
        view.setGravity(Gravity.CENTER);
        view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 160));
        root.addView(view);
        addModifyGridLayout(DataSource.getArray());
    }

    private void addGridLayout(JSONArray data) {
        try {
            GridLayout layout = createGridLayout();
            int size = data.length();
            for (int i = 0; i < size; ++i) {
                JSONArray item = (JSONArray) data.get(i);
                String key = item.getString(0);
                String value = (item.length() >= 2) ? item.getString(1) : "";
                GridLayout.Spec row = GridLayout.spec(i);
                GridLayout.Spec col1 = GridLayout.spec(0);
                GridLayout.Spec col2 = GridLayout.spec(1);
                GridLayout.LayoutParams params = new GridLayout.LayoutParams(row, col1);

                TextView title = getLeftTextView(key);
                layout.addView(title, params);

                params = new GridLayout.LayoutParams(row, col2);
                TextView desc = getRightTextView(value);
                layout.addView(desc, params);
            }
            root.addView(layout);
        } catch (Exception e) {

        }
    }

    @NonNull
    private TextView getRightTextView(String value) {
        TextView desc = new TextView(this);
        desc.setTextSize(13);
        desc.setTextColor(getResources().getColor(R.color.black));
        desc.setText(value);
        return desc;
    }

    @NonNull
    private TextView getLeftTextView(String key) {
        TextView title = new TextView(this);
        title.setText(key);
        title.setPadding(0, middlePadding, middlePadding, 0);
        title.setTextColor(getResources().getColor(R.color.color_black_999999));
        title.setTextSize(13);
        return title;
    }

    private void addModifyGridLayout(JSONArray data) {
        try {
            calculateLeftMaxWidth(data);
            GridLayout layout = createGridLayout();
            int size = data.length();
            for (int i = 0; i < size; ++i) {
                JSONArray item = (JSONArray) data.get(i);
                GridLayout.Spec row = GridLayout.spec(i);

                String key = item.getString(0);
                GridLayout.Spec col1 = GridLayout.spec(0);
                GridLayout.LayoutParams params = new GridLayout.LayoutParams(row, col1);

                TextView title = getLeftTextView(key);
                layout.addView(title, params);

                String value = (item.length() >= 2) ? item.getString(1) : "";
                GridLayout.Spec col2 = GridLayout.spec(1);
                params = new GridLayout.LayoutParams(row, col2);

                TextView desc = getRightTextView(value);
                params.width = (int) maxRightWidth;
                params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
                layout.addView(desc, params);
            }
            root.addView(layout);
        } catch (Exception e) {

        }
    }

    private void calculateLeftMaxWidth(JSONArray data) {
        try {
            DisplayUtil.init(this);// 這個可以在應用程式起來的時候init
            int size = data.length();
            for (int i = 0; i < size; ++i) {
                JSONArray o = (JSONArray) data.get(i);
                String key = o.getString(0);
                String value = o.getString(1);
                if (TextUtils.isEmpty(key) || TextUtils.isEmpty(value)) {
                    continue;
                }
                float curWidth = leftPaint.measureText(key);
                if (curWidth > maxLeftWidth) {
                    maxLeftWidth = curWidth;
                }
            }
            maxLeftWidth = maxLeftWidth + middlePadding;
            maxRightWidth = DisplayUtil.screenWidth - DisplayUtil.dp2px(this, 32 + 10) - maxLeftWidth;
        } catch (Exception e) {

        }
    }

    private GridLayout createGridLayout() {
        GridLayout layout = new GridLayout(this);
        layout.setColumnCount(2);
        //layout.setRowCount(5);
        layout.setOrientation(GridLayout.HORIZONTAL);
        return layout;
    }
}

       如果直接建立一個GridLayout,裡面新增每一項,如果描述過長都導致顯示不全,這個是系統的一個bug,計算的寬度有問題,因此需要對此方案進行更改。
       更改方式為先計算左邊佔用的最大寬度,在新增右邊的項時,設定佈局引數控制最大的長度。

方案5

       採用每一行一個佈局,手動一行一行進行新增:

public class Typography5Activity extends BaseActivity {

    public static void start(Context context) {
        Intent intent = new Intent();
        intent.setClass(context, Typography5Activity.class);
        context.startActivity(intent);
    }


    private LinearLayout root;
    private Paint leftPaint = new Paint();
    private float textSize;
    private float maxLeftWidth;
    private int middlePadding = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        root = (LinearLayout) LayoutInflater.from(this).inflate(R.layout.activity_typography5, null);
        setContentView(root);
        initPaint();
        loadData();
    }

    private void initPaint() {
        textSize = getResources().getDimensionPixelSize(R.dimen.text_size_13);
        leftPaint.setAntiAlias(true);
        leftPaint.setTextSize(textSize);
        leftPaint.setColor(getResources().getColor(R.color.color_black_999999));
        middlePadding = getResources().getDimensionPixelSize(R.dimen.padding_value);
    }

    private void loadData() {
        JSONArray array = DataSource.getArray();
        calculateLeftMaxWidth(array);
        if (array != null) {
            try {
                int size = array.length();
                for (int i = 0; i < size; ++i) {
                    JSONArray o = (JSONArray) array.get(i);
                    String key = o.getString(0);
                    String value = o.getString(1);
                    addItem(key, value);
                }
            } catch (Exception e) {

            }
        }
    }

    private void calculateLeftMaxWidth(JSONArray data) {
        try {
            int size = data.length();
            for (int i = 0; i < size; ++i) {
                JSONArray o = (JSONArray) data.get(i);
                String key = o.getString(0);
                String value = o.getString(1);
                if (TextUtils.isEmpty(key) || TextUtils.isEmpty(value)) {
                    continue;
                }
                float curWidth = leftPaint.measureText(key);
                if (curWidth > maxLeftWidth) {
                    maxLeftWidth = curWidth;
                }
            }
            maxLeftWidth = maxLeftWidth + middlePadding;
        } catch (Exception e) {

        }
    }

    private void addItem(String key, String value) {
        LinearLayout layout = getItemLayout();
        TextView left = (TextView) layout.findViewById(R.id.left);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);

        params.width = (int) maxLeftWidth;
        left.setLayoutParams(params);
        left.setText(key);

        TextView right = (TextView) layout.findViewById(R.id.right);
        right.setText(value);

        root.addView(layout);
    }

    private LinearLayout getItemLayout() {
        LinearLayout layout = (LinearLayout) LayoutInflater.from(this).inflate(R.layout.compose_item_layout, null);
        return layout;
    }
}

       改方案也需要先計算左邊的最大佔用寬度,來設定右邊佔用的大小,每一項的佈局如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    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:orientation="horizontal"
    android:paddingTop="@dimen/text_padding_10"
    tools:context=".activity.Typography1Activity">

    <TextView
        android:id="@+id/left"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginRight="@dimen/text_padding_10"
        android:textColor="@color/color_black_999999"
        android:textSize="@dimen/text_size_13"/>

    <TextView
        android:id="@+id/right"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:textColor="@color/black"
        android:textSize="@dimen/text_size_13"/>


</LinearLayout>

       每一行有兩個TextView,左邊寬度為自適應,右邊佔據剩下左右的位置,在計算出左邊最大寬度後,重新設定左邊每一個TextView佔用的寬度。

方案6

       方式與1差不多,但是不在繼承TextView,而是直接繼承View:

public class TypographyView4 extends View {

    private Paint leftPaint = new Paint();
    private Paint rightPaint = new Paint();
    private int fullWidth;
    private float textSize;
    private JSONArray array;
    private int middlePadding = 0;
    float maxLeftWidth = 0;
    int itemSize = 0;

    public TypographyView4(Context context) {
        super(context);
        init();
    }

    public TypographyView4(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public TypographyView4(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        textSize = getResources().getDimensionPixelSize(R.dimen.text_size_13);
        leftPaint.setAntiAlias(true);
        leftPaint.setTextSize(textSize);
        leftPaint.setColor(getResources().getColor(R.color.color_black_999999));
        rightPaint.setAntiAlias(true);
        rightPaint.setTextSize(textSize);
        rightPaint.setColor(getResources().getColor(R.color.color_black));
        middlePadding = getResources().getDimensionPixelSize(R.dimen.padding_value);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        fullWidth = getWidth();// 整個textView的寬度
    }

    public void setText(JSONArray array) {
        this.array = array;
        if (array != null) {
            try {
                int size = itemSize = array.length();
                for (int i = 0; i < size; ++i) {
                    JSONArray o = (JSONArray) array.get(i);
                    String key = o.getString(0);
                    String value = o.getString(1);
                    if (TextUtils.isEmpty(key) || TextUtils.isEmpty(value)) {
                        itemSize--;
                        continue;
                    }
                    float curWidth = leftPaint.measureText(key);
                    if (curWidth > maxLeftWidth) {
                        maxLeftWidth = curWidth;
                    }
                }
                maxLeftWidth = maxLeftWidth + middlePadding;
                invalidate();
            } catch (Exception e) {

            }
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (array == null) {
            return;
        }
        int lineCount = 0;
        try {
            JSONArray item;
            float offsetY;
            for (int i = 0; i < itemSize; ++i) {
                item = (JSONArray) array.get(i);
                offsetY = (lineCount + 1) * textSize;
                canvas.drawText(item.getString(0), 0, offsetY, leftPaint);

                String value = item.getString(1);
                float valueWidth = rightPaint.measureText(value);
                if (valueWidth > fullWidth - maxLeftWidth) {// 一行顯示不完
                    char[] textCharArray = value.toCharArray();
                    float charWidth;
                    float drawWidth = maxLeftWidth;
                    for (int j = 0; j < textCharArray.length; j++) {
                        charWidth = rightPaint.measureText(textCharArray, j, 1);
                        if (fullWidth - drawWidth < charWidth) {
                            lineCount++;
                            drawWidth = maxLeftWidth;
                            offsetY += textSize;
                        }
                        canvas.drawText(textCharArray, j, 1, drawWidth, offsetY, rightPaint);
                        drawWidth += charWidth;
                    }
                } else {
                    canvas.drawText(value, maxLeftWidth, offsetY, rightPaint);
                }
                lineCount += 2;
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }
}

       該方案主要繼承自View,不再繼承TextView,由於在在上述方案中不在呼叫super,因此TextView已經退化為一個View,因此直接繼承View。

總結

       因為左邊的寬度不確定,因此所有的方案都進行了同樣的一個操作,就是測量了左邊顯示的最大寬度,後續的操作再根據該寬度進行調整。上述的方案中1,2,3,6都只需用一個View來進行顯示,4,5都需要多個View進行顯示。
       完整的程式碼可以在檢視連結上進行檢視。