Поиск по этому блогу

вторник, 11 июля 2017 г.

Read more кнопка в TextView

Недавно встала задача сделать кнопку read more на одном экране в проекте, погуглив нашел несколько вариантов и выбрал для себя тот который мне подходит, решил записать сюда что бы не забыть что у меня он такой есть :) Задача очень простая, у нас есть текствью с текстом, мы хотим что бы наш код резал текст на допустим 5 строк и по нажатию на текствью у нас оно разворачивается и показывает весь текст. Задача довольно простая, по этому давайте быстрей реализуем ее. Я сразу приведу скриншоты что бы вы понимали как это будет выглядеть.

 

Для того что бы нам достигнуть такого эфекта, нам нужно создать кастомную текствьюху и просто ее использовать вместо стандартной, что я собственно и сделал. У нас есть текствью и в нем мы будем резать и отображать сразу за обрезанным текстом текст «read more...».

Для начала нам нужно создать файлик attrs, в нем у нас будет одна установка на количество строк которые мы будем резать наш текст.

attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="ExpandableTextView">
        <attr name="trimLength" format="integer" />
    </declare-styleable>
</resources>

А дальше напишем наш кастомный класс.

ExpandableTextView.java
import android.content.Context;
import android.content.res.TypedArray;
import android.text.Html;
import android.text.SpannableStringBuilder;
import android.util.AttributeSet;
import android.view.View;

import com.project.readmorebutton.R;

public class ExpandableTextView  extends android.support.v7.widget.AppCompatTextView implements View.OnClickListener {

    private static final int DEFAULT_TRIM_LENGTH = 100;
    private static final String ELLIPSIS = " <html><body><font color='#3088CD'>read more...</font></body></html>";

    private CharSequence originalText;
    private CharSequence trimmedText;
    private BufferType bufferType;
    private boolean trim = true;
    private int trimLength;

    public ExpandableTextView(Context context, AttributeSet attrs) {
        super(context, attrs);

        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ExpandableTextView);
        trimLength = typedArray.getInt(R.styleable.ExpandableTextView_trimLength, DEFAULT_TRIM_LENGTH);
        typedArray.recycle();

        setOnClickListener(this);
    }

    private void setText() {
        super.setText(Html.fromHtml(getDisplayableText().toString()), bufferType);
    }

    private CharSequence getDisplayableText() {
        return trim ? trimmedText : originalText;
    }

    @Override
    public void setText(CharSequence text, BufferType type) {
        originalText = text;
        trimmedText = getTrimmedText();
        bufferType = type;
        setText();
    }

    private CharSequence getTrimmedText() {
        if (originalText != null && originalText.length() > trimLength) {
            return new SpannableStringBuilder(originalText, 0, trimLength + 1).append(ELLIPSIS);
        } else {
            return originalText;
        }
    }

    @Override
    public void onClick(View view) {
        trim = !trim;
        setText();
        requestFocusFromTouch();
    }

    public void setTrimLength(int trimLength) {
        this.trimLength = getHeight(trimLength);
        trimmedText = getTrimmedText();
        setText();
    }

    private int getHeight(int lineCount) {
        return (int) ((lineCount * getLineCount() + (lineCount > 0 ? (lineCount - 1) * getPaint().getFontSpacing() : 0)) / 1.5f);
    }
}

Что же у нас тут происходит? А все очень просто, у нас есть переменная ELLIPSIS, как по ней видно у нас там html код, он у нас будет вставляться сразу после текста который мы обрежем. Дальше в конструкторе у нас идет настройка дефолтных настроек по обрезке текста — сколько нам его надо будет обрезать. 

Потом идет метод setText() который сетит текст в html как видно из кода в этом методе.
Далее getDisplayableText() в котором мы проверяем резать нам текст или нет который мы щас будет отображать, если trim = true то отображаем обрезанный, если нет то оригинальный.
Потом идет стандартный метод setText() который мы заоверрайдили и в нем сетапим все нужные нам данные, а это текст и обрезанный текст, у нас еще есть буфер тайп, но он нужен потому что переопределить обычный setText() не получится, нужен еще какой-то параметер для этого.
В getTrimmedText() мы режем текст в нужном нам количестве.
По onClick() — тут я думаю понятно что происходит :) По клику делаем или тру или фолс переменную trim и вызываем setText() что бы обновить текст.
setTrimLength() — метод который мы вызываем для установки количества строк в длинном тексте.
Ну и метод getHeight() у нас для расчета количества строк.

Как же это все нам использовать? Да очень просто, создаем в activity_main обычную текствью, только переименовываем ее на наш ExpandableTextView, и все, считайте что все уже готово, останется только проинициализировать все это потом в месте где вы ходите ее использовать, ну в общем, смотрите.

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="20dp">

    <com.project.readmorebutton.view.ExpandableTextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        android:textSize="18sp"
        app:trimLength="5" />

</LinearLayout>

А дальше в нашем классе где мы вставили этот текствью мы прописываем текст который мы хотим что бы оно подрезало и количество строк на которые мы хотим что бы оно отрезало и вставило «read more...».

MainActivity.java
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;

import com.project.readmorebutton.view.ExpandableTextView;

public class MainActivity extends AppCompatActivity {

    private int MAX_LINES = 5;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ExpandableTextView text = (ExpandableTextView) findViewById(R.id.text);
        text.setTrimLength(MAX_LINES);
        text.setText(getString(R.string.text), TextView.BufferType.NORMAL);
    }
}

Ну и текст у меня был в стринг файле, по сути это не важно это я сделал просто для красоты, но его я тоже приведу.

string.xml
<resources>
    <string name="app_name">ReadMoreButton</string>

    <string name="text">We all need a hotel from time to time, one way or another.  We check in, have a shower, sleep and leave. It is mostly enough for most of us to have the rates and the service in a satisfactory fashion, if our neighbors are not noisy.But have you ever thought how hotels emerged? Accommodation is of course a basic need of humanity, as is travelling. But do you know where the first hotel was opened? Or where the word “motel” comes from? Have you ever heard about the celebrities spending a life in hotels? We’ve compiled 10 interesting facts about hotel in this list for you… And of course, don’t forget to visit our list of “Interesting Facts about Hostels” once you finish reading this one… Moreover, the following is a full list of our articles related to hotels and tourism, don’t forget to have a look!</string>
</resources>

И вот собственно и все. Вот такая вот простая шняжка, выглядит красиво, работает так как и задумывалось, в общем красотень. :) Надеюсь кому-то это будет полезно, а не только мне. :)

Исходники:
GitHub