2017-01-21 10 views
7

Мне нужно создать свой собственный TextView, поэтому я узнал о StaticLayout, чтобы нарисовать текст на холсте. Это предпочтительнее использовать непосредственно Canvas.drawText(), или так говорит documentation. Однако в документации нет примеров того, как это сделать. Существует только неопределенная ссылка на StaticLayout.Builder, являющийся более новым способом сделать это.Как используется StaticLayout в Android?

Я нашел пример here, но он немного устарел.

Я, наконец, работал, хотя как это сделать, поэтому я добавляю свое объяснение ниже.

ответ

40

StaticLayout (similar to DynamicLayout and BoringLayout) используется для компоновки и рисования текста на холсте. Он обычно используется для решения следующих задач:

  • Измерение того, как большой многострочный текст будет выложен.
  • Рисование текста на растровом изображении.
  • Создание пользовательского представления, которое обрабатывает собственный текстовый макет (в отличие от создания составного представления со встроенным TextView). TextView сам использует StaticLayoutinternally.

Измерение размера текста

Однолинейная

Если у вас есть только одну строку текста, вы можете измерить его с Paint или TextPaint.

String text = "This is some text." 

TextPaint myTextPaint = new TextPaint(); 
mTextPaint.setAntiAlias(true); 
mTextPaint.setTextSize(16 * getResources().getDisplayMetrics().density); 
mTextPaint.setColor(0xFF000000); 

float width = mTextPaint.measureText(text); 
float height = -mTextPaint.ascent() + mTextPaint.descent(); 

Multiline

Однако, если есть перенос строк и вам нужна высота, то лучше использовать StaticLayout. Вы предоставляете ширину, а затем можете получить высоту от StaticLayout.

String text = "This is some text. This is some text. This is some text. This is some text. This is some text. This is some text."; 

TextPaint myTextPaint = new TextPaint(); 
myTextPaint.setAntiAlias(true); 
myTextPaint.setTextSize(16 * getResources().getDisplayMetrics().density); 
myTextPaint.setColor(0xFF000000); 

int width = 200; 
Layout.Alignment alignment = Layout.Alignment.ALIGN_NORMAL; 
float spacingMultiplier = 1; 
float spacingAddition = 0; 
boolean includePadding = false; 

StaticLayout myStaticLayout = new StaticLayout(text, myTextPaint, width, alignment, spacingMultiplier, spacingAddition, includePadding); 

float height = myStaticLayout.getHeight(); 

Новый API

Если вы хотите использовать новый StaticLayout.Builder (доступный от API 23), вы можете получить макет так:

StaticLayout.Builder builder = StaticLayout.Builder.obtain(text, 0, text.length(), myTextPaint, width); 
StaticLayout myStaticLayout = builder.build(); 

Вы можете лавировать на дополнение с использованием точечной нотации:

StaticLayout.Builder builder = StaticLayout.Builder.obtain(text, 0, text.length(), myTextPaint, width) 
     .setAlignment(Layout.Alignment.ALIGN_NORMAL) 
     .setLineSpacing(spacingMultiplier, spacingAddition) 
     .setIncludePad(includePadding) 
     .setMaxLines(5); 
StaticLayout myStaticLayout = builder.build(); 

Написание текста по изображению

Я могу расширить это в будущем, но на данный момент см. this post для примера метода, который использует StaticLayout и возвращает растровое изображение.

Создание пользовательского текста обработки View

Вот пример пользовательского вида используя StaticLayout. Он ведет себя как простой TextView. Когда текст слишком длинный, чтобы поместиться на экране, он автоматически обматывает линии и увеличивает его высоту.

enter image description here

Код

MyView.java

public class MyView extends View { 

    String mText = "This is some text."; 
    TextPaint mTextPaint; 
    StaticLayout mStaticLayout; 

    // use this constructor if creating MyView programmatically 
    public MyView(Context context) { 
     super(context); 
     initLabelView(); 
    } 

    // this constructor is used when created from xml 
    public MyView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     initLabelView(); 
    } 

    private void initLabelView() { 
     mTextPaint = new TextPaint(); 
     mTextPaint.setAntiAlias(true); 
     mTextPaint.setTextSize(16 * getResources().getDisplayMetrics().density); 
     mTextPaint.setColor(0xFF000000); 

     // default to a single line of text 
     int width = (int) mTextPaint.measureText(mText); 
     mStaticLayout = new StaticLayout(mText, mTextPaint, (int) width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0, false); 

     // New API alternate 
     // 
     // StaticLayout.Builder builder = StaticLayout.Builder.obtain(mText, 0, mText.length(), mTextPaint, width) 
     //  .setAlignment(Layout.Alignment.ALIGN_NORMAL) 
     //  .setLineSpacing(1, 0) // multiplier, add 
     //  .setIncludePad(false); 
     // mStaticLayout = builder.build(); 
    } 

    @Override 
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
     // Tell the parent layout how big this view would like to be 
     // but still respect any requirements (measure specs) that are passed down. 

     // determine the width 
     int width; 
     int widthMode = MeasureSpec.getMode(widthMeasureSpec); 
     int widthRequirement = MeasureSpec.getSize(widthMeasureSpec); 
     if (widthMode == MeasureSpec.EXACTLY) { 
      width = widthRequirement; 
     } else { 
      width = mStaticLayout.getWidth() + getPaddingLeft() + getPaddingRight(); 
      if (widthMode == MeasureSpec.AT_MOST) { 
       if (width > widthRequirement) { 
        width = widthRequirement; 
        // too long for a single line so relayout as multiline 
        mStaticLayout = new StaticLayout(mText, mTextPaint, width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0, false); 
       } 
      } 
     } 

     // determine the height 
     int height; 
     int heightMode = MeasureSpec.getMode(heightMeasureSpec); 
     int heightRequirement = MeasureSpec.getSize(heightMeasureSpec); 
     if (heightMode == MeasureSpec.EXACTLY) { 
      height = heightRequirement; 
     } else { 
      height = mStaticLayout.getHeight() + getPaddingTop() + getPaddingBottom(); 
      if (heightMode == MeasureSpec.AT_MOST) { 
       height = Math.min(height, heightRequirement); 
      } 
     } 

     // Required call: set width and height 
     setMeasuredDimension(width, height); 
    } 

    @Override 
    protected void onDraw(Canvas canvas) { 
     super.onDraw(canvas); 
     // do as little as possible inside onDraw to improve performance 

     // draw the text on the canvas after adjusting for padding 
     canvas.save(); 
     canvas.translate(getPaddingLeft(), getPaddingTop()); 
     mStaticLayout.draw(canvas); 
     canvas.restore(); 
    } 
} 

activity_main.xml

<?xml version="1.0" encoding="utf-8"?> 
<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:tools="http://schemas.android.com/tools" 
    android:id="@+id/activity_main" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:padding="@dimen/activity_vertical_margin" 
    tools:context="com.example.layoutpractice.MainActivity"> 

    <com.example.layoutpractice.MyView 
     android:layout_centerHorizontal="true" 
     android:background="@color/colorAccent" 
     android:padding="10dp" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content"/> 
</RelativeLayout> 

Примечание

  • This, this и this были полезны в изучении, как сделать вид пользовательской обработки текста.

  • См. Creating a View Class, если вы хотите добавить пользовательские атрибуты, которые могут быть установлены из кода или xml.