Урок 11. Работа с RecyclerView на примере TweetsRecyclerView – Devcolibri – Android для начинающих

Урок 11. Работа с RecyclerView на примере TweetsRecyclerView

Код начала урока:

gitzip

Структура урока:

Добавление разделителя информации о пользователе и списка твитов

Вначале вспомним, как должен выглядеть блок информации о пользователе.

Delimeter.png

Видим, что нам необходимо добавить View серого цвета после информации о пользователе и разместить под ней список RecyclerView. Мы бы могли поместить эту View просто под последним TextView, но View должна занимать всю ширину. Это трудно сделать потому что у RelativeLayout установлено свойство padding, которое делает отступ перед всеми вложенными элементами. Поэтому сделаем LinearLayout корневым элементом нашего layout, в свою очередь у него будет 3 вложенных элемента:

  • RelativeLayout с информацией о пользователе. Тот контейнер, который до этого момента являлся корневым.
  • View – разделитель информации.
  • RecyclerView – список твитов, который мы добавим чуть позже в этом уроке.

Наш layout после этих преобразований выглядит так:

activity_user_info.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="8dp">

        <de.hdodenhof.circleimageview.CircleImageView
            android:id="@+id/user_image_view"
            android:layout_width="96dp"
            android:layout_height="96dp"
            android:src="@mipmap/ic_launcher"/>

        <TextView
            android:id="@+id/user_name_text_view"
            style="@style/TextView.Primary.Header"
            android:layout_below="@id/user_image_view"
            android:layout_marginTop="@dimen/text_small_margin"
            android:text="Имя"/>

        <TextView
            android:id="@+id/user_nick_text_view"
            style="@style/TextView.Secondary"
            android:layout_below="@id/user_name_text_view"
            android:layout_marginTop="@dimen/text_small_margin"
            android:text="Ник"/>

        <TextView
            android:id="@+id/user_description_text_view"
            style="@style/TextView.Primary"
            android:layout_below="@id/user_nick_text_view"
            android:layout_marginTop="@dimen/text_small_margin"
            android:text="Описание"/>

        <TextView
            android:id="@+id/user_location_icon_text_view"
            style="@style/TextIcon"
            android:layout_alignBaseline="@id/user_location_text_view"
            android:layout_below="@id/user_description_text_view"
            android:layout_marginTop="@dimen/text_small_margin"
            android:text="@string/fa_map_marker"/>

        <TextView
            android:id="@+id/user_location_text_view"
            style="@style/TextView.Secondary"
            android:layout_below="@id/user_description_text_view"
            android:layout_marginStart="10dp"
            android:layout_marginTop="@dimen/text_small_margin"
            android:layout_toEndOf="@id/user_location_icon_text_view"
            android:text="Местоположение"/>

        <TextView
            android:id="@+id/following_count_text_view"
            style="@style/TextView.Primary.Bold"
            android:layout_below="@id/user_location_text_view"
            android:layout_marginTop="@dimen/text_small_margin"
            android:text="4"/>

        <TextView
            android:id="@+id/following_text_view"
            style="@style/TextView.Secondary"
            android:layout_below="@id/user_location_text_view"
            android:layout_marginStart="5dp"
            android:layout_marginTop="@dimen/text_small_margin"
            android:layout_toEndOf="@+id/following_count_text_view"
            android:text="@string/following_hint"/>

        <TextView
            android:id="@+id/followers_count_text_view"
            style="@style/TextView.Primary.Bold"
            android:layout_below="@id/user_location_text_view"
            android:layout_marginStart="20dp"
            android:layout_marginTop="@dimen/text_small_margin"
            android:layout_toEndOf="@+id/following_text_view"
            android:text="4"/>

        <TextView
            android:id="@+id/followers_text_view"
            style="@style/TextView.Secondary"
            android:layout_below="@id/user_location_text_view"
            android:layout_marginStart="10dp"
            android:layout_marginTop="@dimen/text_small_margin"
            android:layout_toEndOf="@+id/followers_count_text_view"
            android:text="@string/followers_hint"/>

    </RelativeLayout>

    <View
        android:id="@+id/delimeter_view"
        android:layout_width="match_parent"
        android:layout_height="6dp"
        android:layout_marginTop="@dimen/text_small_margin"
        android:background="@color/gray_mercury"/>
</LinearLayout>

Наш RelativeLayout практически не изменился. Единственное что мы изменили – поменяли значения атрибута android:layout_height c match_parent на wrap_content. Это необходимо, чтобы нашему разделителю хватило места на экране.

Мы просто сделали корневым элементом LinearLayout и добавили нашу View в качестве элемента, который находится под RelativeLayout.

Знакомство с элементом RecyclerView

RecyclerView (рус. переиспользующий компонент) представляет из себя компонент для отображения элементов списка. Как раз он и нужен нам для того, чтобы отобразить список твитов пользователя.

Напомним, что в 7 уроке мы уже создали с вами файл tweet_item_view. В этом занятии мы просто свяжем его с java кодом, чтобы динамически добавлять во View информацию.

Данная тема довольна сложна для понимая с первого раза, поэтому помимо пошагового разбора работы с RecyclerView в данном уроке рекомендуем ознакомиться с полезными материалами прикреплёнными в завершении урока для закрепления материала.

Вначале надо добавить строку implementation 'com.android.support:recyclerview-v7:26.1.0' в файл build.gradle, чтобы иметь доступ в коде к RecyclerView.

build.gradle(Module:app)

dependencies {

    // остальные элементы выше не изменились

    implementation 'com.android.support:recyclerview-v7:26.1.0'
}

Далее добавим наш RecyclerView в layout файл. Не будем добавлять никаких зависимостей от соседних элементов, т.к. его родителем является LinearLayout. Просто поместим его после нашей View разделителя. И свяжем его с элементом в java коде.

activity_user_info.xml

<LinearLayout>

    <!-- Остальные элементы сверху не изменились-->

    <View
        android:id="@+id/delimeter_view"
        android:layout_width="match_parent"
        android:layout_height="6dp"
        android:layout_marginTop="@dimen/text_small_margin"
        android:background="@color/gray_mercury"/>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/tweets_recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>

UserInfoActivity.java

public class UserInfoActivity extends AppCompatActivity {

    // остальные поля не изменились

    private RecyclerView tweetsRecyclerView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        // остальной код выше не изменился

        initRecyclerView();
    }

    private void initRecyclerView() {
        tweetsRecyclerView = findViewById(R.id.tweets_recycler_view);
    }
}

В Activity мы создали отдельный метод initRecyclerView(), который вызываем в методе onCreate().

Для работы с RecyclerView необходим LayoutManager, а также Adapter. LayoutManager отвечает за форму отображения элементов: обычная линейная, в виде сетки, в виде шахматной сетки. В данном случае нам нужен обычный линейный список, поэтому используем LinearLayoutManager:

UserInfoActivity.java

public class UserInfoActivity extends AppCompatActivity {

    // остальной код выше не изменился

    private void initRecyclerView() {

        // остальной код не изменился

        tweetsRecyclerView.setLayoutManager(new LinearLayoutManager(this));
    }
}

Если вы сейчас запустите приложение, то увидите, что ничего визуально не изменилось. Это потому что список пуст. Чтобы в нём была какая-то информация его надо заполнить элементами. Для этого нам понадобится класс Adapter (переходник) – класс, который отвечает за связь элементов java кода с View-компонентами. Т.е. получая набор java объектов, мы должны подать его на вход в адаптер, который преобразует его уже в набор View-компонентов, которые и использует в дальнейшем RecyclerView.

Давайте создадим новый пакет adapter, где и разместим класс TweetAdapter. Вспоминаем, что надо нажать по пакету colibri.dev.com.colibritweet правой кнопкой и выбрать New -> Package. После этого вводим имя adapter.

После этого в этом пакете создаём новый java класс (New -> Java class), который называем TweetAdapter. Выглядеть результат должен так:

TweetAdapter.png

Работа с TweetAdapter

Adapter, используемый при работе с RecyclerView обязан определить объект типа ViewHolder, который предоставляет доступ ко всем View-компонентам в каждой строке списка. Давайте модифицируем наш адаптер, добавив необходимые для RecyclerView зависимости:

TweetAdapter.java

package colibri.dev.com.colibritweet.adapter;

import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import colibri.dev.com.colibritweet.R;

// Унаследовали наш адаптер от RecyclerView.Adapter
// Здесь же указали наш собственный ViewHolder, который предоставит нам доступ к View-компонентам
public class TweetAdapter extends RecyclerView.Adapter<TweetAdapter.TweetViewHolder> {

    // Предоставляет прямую ссылку на каждый View-компонент
    // Используется для кэширования View-компонентов и последующего быстрого доступа к ним
    class TweetViewHolder extends RecyclerView.ViewHolder {
        // Ваш ViewHolder должен содержать переменные для всех
        // View-компонентов, которым вы хотите задавать какие-либо свойства
        // в процессе работы пользователя со списком
        private ImageView userImageView;
        private TextView nameTextView;
        private TextView nickTextView;
        private TextView creationDateTextView;
        private TextView contentTextView;
        private ImageView tweetImageView;
        private TextView retweetsTextView;
        private TextView likesTextView;

        // Мы также создали конструктор, который принимает на вход View-компонент строкИ
        // и ищет все дочерние компоненты
        public TweetViewHolder(View itemView) {
            super(itemView);
            userImageView = itemView.findViewById(R.id.profile_image_view);
            nameTextView = itemView.findViewById(R.id.author_name_text_view);
            nickTextView = itemView.findViewById(R.id.author_nick_text_view);
            creationDateTextView = itemView.findViewById(R.id.creation_date_text_view);
            contentTextView = itemView.findViewById(R.id.tweet_content_text_view);
            tweetImageView = itemView.findViewById(R.id.tweet_image_view);
            retweetsTextView = itemView.findViewById(R.id.retweets_text_view);
            likesTextView = itemView.findViewById(R.id.likes_text_view);
        }
    }
}

Теперь, когда мы определили базовый адаптер и ViewHolder, нам нужно заполнить наш адаптер. Сначала давайте создадим переменную для списка твитов:

TweetAdapter.java

package colibri.dev.com.colibritweet.adapter;

import java.util.ArrayList;
import java.util.List;

import colibri.dev.com.colibritweet.pojo.Tweet;

public class TweetAdapter extends RecyclerView.Adapter<TweetAdapter.TweetViewHolder> {

    private List<Tweet> tweetList = new ArrayList<>();

    // остальной код не изменился
}

Для того, чтобы работа с нашим адаптером была гибкой, нам нужны какие-то рычаги для управления наполнением адаптера. Давайте добавим два метода для этого. Один будет служить для наполнения коллекции, другой – для очистки, если нам понадобится обновить информацию на экране:

TweetAdapter.java

import java.util.Collection;

public class TweetAdapter extends RecyclerView.Adapter<TweetAdapter.TweetViewHolder> {

    // остальной код выше не изменился

    public void setItems(Collection<Tweet> tweets) {
        tweetList.addAll(tweets);
        notifyDataSetChanged();
    }

    public void clearItems() {
        tweetList.clear();
        notifyDataSetChanged();
    }

    // остальной код ниже не изменился
}

Оба этих метода содержат в себе вызов одного и того же метода notifyDataSetChanged(). Он предназначен для того, чтобы дать адаптеру знать, что список элементов изменился и ему нужно позаботиться о том, чтобы перерисовать элементы на экране.

Каждый адаптер должен реализовывать 3 главных метода:

  • onCreateViewHolder(ViewGroup parent, int viewType) – метод вызывается для создания объекта ViewHolder, в конструктор которого необходимо передать созданный View-компонент, с которым в дальнейшем будут связываться java объекты. Метод вызывается без нашего личного вмешательства, т.к. RecyclerView в себе инкапсулирует логику переиспользования элементов;
  • onBindViewHolder(TweetViewHolder holder, int position) – этот метод отвечает за связь java объекта и View. Метод также вызывается без нашего участия. Он будет вызываться чаще, чем метод onCreateViewHolder из-за того, что View компоненты будут переиспользоваться и в один и тот же визуальный элемент в процессе жизнедеятельности списка будут устанавливаться разные данные;
  • getItemCount() – сообщает количество элементов в списке.

Давайте реализуем их:

TweetAdapter.java

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class TweetAdapter extends RecyclerView.Adapter<TweetAdapter.TweetViewHolder> {

    // остальной код выше не изменился

    @Override
    public TweetViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.tweet_item_view, parent, false);
        return new TweetViewHolder(view);
    }

    @Override
    public void onBindViewHolder(TweetViewHolder holder, int position) {
    }

    @Override
    public int getItemCount() {
        return tweetList.size();
    }

    // остальной код ниже не изменился
}

Отлично, теперь осталось добавить код, который будет заполнять данными наши View-компоненты в TweetViewHolder:

TweetAdapter.java

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

import com.squareup.picasso.Picasso;

public class TweetAdapter extends RecyclerView.Adapter<TweetAdapter.TweetViewHolder> {
    private static final String TWITTER_RESPONSE_FORMAT="EEE MMM dd HH:mm:ss ZZZZZ yyyy"; // Thu Oct 26 07:31:08 +0000 2017
    private static final String MONTH_DAY_FORMAT = "MMM d"; // Oct 26

    // остальной код не изменился

    @Override
    public void onBindViewHolder(TweetViewHolder holder, int position) {
        holder.bind(tweetList.get(position));
    }

    class TweetViewHolder extends RecyclerView.ViewHolder {

        // остальной код не изменился

        public void bind(Tweet tweet) {
            nameTextView.setText(tweet.getUser().getName());
            nickTextView.setText(tweet.getUser().getNick());
            contentTextView.setText(tweet.getText());
            retweetsTextView.setText(String.valueOf(tweet.getRetweetCount()));
            likesTextView.setText(String.valueOf(tweet.getFavouriteCount()));

            String creationDateFormatted = getFormattedDate(tweet.getCreationDate());
            creationDateTextView.setText(creationDateFormatted);

            Picasso.with(itemView.getContext()).load(tweet.getUser().getImageUrl()).into(userImageView);

            String tweetPhotoUrl = tweet.getImageUrl();
            Picasso.with(itemView.getContext()).load(tweetPhotoUrl).into(tweetImageView);

            tweetImageView.setVisibility(tweetPhotoUrl != null ? View.VISIBLE : View.GONE);
        }

        private String getFormattedDate(String rawDate) {
            SimpleDateFormat utcFormat = new SimpleDateFormat(TWITTER_RESPONSE_FORMAT, Locale.ROOT);
            SimpleDateFormat displayedFormat = new SimpleDateFormat(MONTH_DAY_FORMAT, Locale.getDefault());
            try {
                Date date = utcFormat.parse(rawDate);
                return displayedFormat.format(date);
            } catch (ParseException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

Всё, что делает данный код – используя ранее созданные View-компоненты, он заполняет их новыми данными. Таким образом происходит переиспользование View-компонентов без лишнего создания новых. Именно с этим связана следующая строка кода метода bind нашего TweetViewHolder:

TweetAdapter.java

tweetImageView.setVisibility(tweetPhotoUrl != null ? View.VISIBLE : GONE);

Если бы переиспользования элементов не было, то достаточно было бы написать строку:

TweetAdapter.java

if (tweetPhotoUrl == null) {
    tweetImageView.setVisibility(GONE);
}

Но из-за того, что наши элементы переиспользуются, то один раз скрыв изображение на одном элементе, оно будет скрыто на нём и при повторном использовании. Даже в том случае, если оно должно быть показано. Это нас не устраивает.

Также давайте подробнее остановимся на методе форматирования даты, использующегося при заполнении данными нашего компонента:

TweetAdapter.java

public class TweetAdapter extends RecyclerView.Adapter<TweetAdapter.TweetViewHolder> {
    private static final String TWITTER_RESPONSE_FORMAT="EEE MMM dd HH:mm:ss ZZZZZ yyyy"; // Thu Oct 26 07:31:08 +0000 2017
    private static final String MONTH_DAY_FORMAT = "MMM d"; // Oct 26

        // остальной код выше не изменился

        private String getFormattedDate(String rawDate) {
            SimpleDateFormat utcFormat = new SimpleDateFormat(TWITTER_RESPONSE_FORMAT, Locale.ROOT);
            SimpleDateFormat displayedFormat = new SimpleDateFormat(MONTH_DAY_FORMAT, Locale.getDefault());
            try {
                Date date = utcFormat.parse(rawDate);
                return displayedFormat.format(date);
            } catch (ParseException e) {
                throw new RuntimeException(e);
            }
        }
}

Т.к. различные сервисы предоставляют дату в самых различных форматах, то возникает необходимость создавать механизмы, которые бы преобразовывали дату одного формата в формат необходимый нам.

Константа TWITTER_RESPONSE_FORMAT представляет собой формат, в котором мы получим дату от Twitter-сервиса. Такую дату отображать пользователю было бы странно. Нас больше устроил бы более простой вариант MONTH_DAY_FORMAT, когда мы отображаем только месяц и день.

Метод String getFormattedDate(String rawDate) принимает на вход дату в формате, в котором мы получим её от сервиса. На выходе же мы получаем готовую к использованию строку даты.

Для начала нам необходимо из строк шаблонов создать java объекты SimpleDateFormat, при помощи которых мы и будем производить преобразования. Т.к. напрямую строку даты одного формата в строку другого формата преобразовать мы не можем, то нам нужен переходный элемент:

TweetAdapter.java

Date date = utcFormat.parse(rawDate);

Данной операцией мы преобразовали строку, пришедшую нам от сервиса Twitter, в соответствии с шаблоном к обычном java объекту Date, которым теперь можно манипулировать каким угодно образом. Нам остаётся просто отформатировать вновь созданный объект даты в соответствии с необходимым нам шаблоном:

TweetAdapter.java

return clientFormat.format(date);

Добавление TweetAdapter к списку RecyclerView

Вот и всё, теперь осталось только сказать нашему RecyclerView, что за наполнение элементов в нём отвечает созданный нами адаптер. Достаточно добавить следующий код в UserInfoActivity:

UserInfoActivity.java

public class UserInfoActivity extends AppCompatActivity {

    // остальной код выше не изменился

    private TweetAdapter tweetAdapter;

    private void initRecyclerView() {

        // остальной код выше не изменился

        tweetAdapter = new TweetAdapter();
        tweetsRecyclerView.setAdapter(tweetAdapter);
    }
}

Теперь давайте убедимся, что наш список действительно работает. Для этого:

  • создадим метод Collection<Tweet> getTweets(), который будет возвращать нам коллекцию объектов-заглушек.
  • создадим метод loadTweets(), где вызовём метод getTweets() и отобразим результат в экземпляре TweetAdapter.

UserInfoActivity.java

public class UserInfoActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        // остальной код выше не изменился

        loadTweets();
    }

    private void loadTweets() {
        Collection<Tweet> tweets = getTweets();
        tweetAdapter.setItems(tweets);
    }

    private Collection<Tweet> getTweets() {
        return Arrays.asList(
                new Tweet(getUser(), 1L, "Thu Dec 13 07:31:08 +0000 2017", "Очень длинное описание твита 1",
                        4L, 4L, "https://www.w3schools.com/w3css/img_fjords.jpg"),

                new Tweet(getUser(), 2L, "Thu Dec 12 07:31:08 +0000 2017", "Очень длинное описание твита 2",
                        5L, 5L, "https://www.w3schools.com/w3images/lights.jpg"),

                new Tweet(getUser(), 3L, "Thu Dec 11 07:31:08 +0000 2017", "Очень длинное описание твита 3",
                        6L, 6L, "https://www.w3schools.com/css/img_mountains.jpg")
        );
    }
}

Теперь давайте запустим наше приложение и посмотрим, что получилось.

Result.png

Отличная работа. Подытожим всё вышесказанное схемой, которая используется в официальной документации для отображения принципа работы c RecyclerView:

RecyclerView.png

В этой схеме чётко разграничены 3 слоя:
View-слой – это непосредственно сам RecyclerView, а также его «помощник» LayoutManager, который отвечает за форму отображения элементов.
Dataset – это данные, которые необходимо отобразить в списке. Могут быть представлены в любом виде: как в нашем случае – методом-заглушкой или реально запрашиваемыми с сервера данными, как мы это сделаем в следующих уроках.
* Adapter представляет собой натуральный переходник между сырыми данными и пользовательским интерфейсом, готовым к их отображению.

Вот и всё! Сегодня мы с вами приблизились к пониманию очень непростой темы.

По итогу этого урока мы полностью подготовили наш экран списка твитов для их просмотра. Осталось только получить их.

На всякий случай отобразим полный листинг кода UserInfoActivity и TweetAdapter:

UserInfoActivity.java

package colibri.dev.com.colibritweet;

import java.util.Arrays;
import java.util.Collection;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.widget.ImageView;
import android.widget.TextView;

import com.squareup.picasso.Picasso;

import colibri.dev.com.colibritweet.adapter.TweetAdapter;
import colibri.dev.com.colibritweet.pojo.Tweet;
import colibri.dev.com.colibritweet.pojo.User;

public class UserInfoActivity extends AppCompatActivity {
    private ImageView userImageView;
    private TextView nameTextView;
    private TextView nickTextView;
    private TextView descriptionTextView;
    private TextView locationTextView;
    private TextView followingCountTextView;
    private TextView followersCountTextView;

    private RecyclerView tweetsRecyclerView;
    private TweetAdapter tweetAdapter;

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

        userImageView = findViewById(R.id.user_image_view);
        nameTextView = findViewById(R.id.user_name_text_view);
        nickTextView = findViewById(R.id.user_nick_text_view);
        descriptionTextView = findViewById(R.id.user_description_text_view);
        locationTextView = findViewById(R.id.user_location_text_view);
        followingCountTextView = findViewById(R.id.following_count_text_view);
        followersCountTextView = findViewById(R.id.followers_count_text_view);

        initRecyclerView();

        loadUserInfo();
        loadTweets();
    }

    private void loadTweets() {
        Collection<Tweet> tweets = getTweets();
        tweetAdapter.setItems(tweets);
    }

    private Collection<Tweet> getTweets() {
        return Arrays.asList(
                new Tweet(getUser(), 1L, "Thu Dec 13 07:31:08 +0000 2017", "Очень длинное описание твита 1",
                        4L, 4L, "https://www.w3schools.com/w3css/img_fjords.jpg"),

                new Tweet(getUser(), 2L, "Thu Dec 12 07:31:08 +0000 2017", "Очень длинное описание твита 2",
                        5L, 5L, "https://www.w3schools.com/w3images/lights.jpg"),

                new Tweet(getUser(), 3L, "Thu Dec 11 07:31:08 +0000 2017", "Очень длинное описание твита 3",
                        6L, 6L, "https://www.w3schools.com/css/img_mountains.jpg")
        );
    }

    private void initRecyclerView() {
        tweetsRecyclerView = findViewById(R.id.tweets_recycler_view);
        tweetsRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        tweetAdapter = new TweetAdapter();
        tweetsRecyclerView.setAdapter(tweetAdapter);
    }

    private void loadUserInfo() {
        User user = getUser();
        displayUserInfo(user);
    }

    private void displayUserInfo(User user) {
        Picasso.with(this).load(user.getImageUrl()).into(userImageView);
        nameTextView.setText(user.getName());
        nickTextView.setText(user.getNick());
        descriptionTextView.setText(user.getDescription());
        locationTextView.setText(user.getLocation());

        String followingCount = String.valueOf(user.getFollowingCount());
        followingCountTextView.setText(followingCount);

        String followersCount = String.valueOf(user.getFollowersCount());
        followersCountTextView.setText(followersCount);
    }

    private User getUser() {
        return new User(
                1L,
                "http://i.imgur.com/DvpvklR.png",
                "DevColibri",
                "devcolibri",
                "Sample description",
                "USA",
                42,
                42
        );
    }
}

TweetAdapter.java

package colibri.dev.com.colibritweet.adapter;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Locale;

import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.squareup.picasso.Picasso;

import colibri.dev.com.colibritweet.R;
import colibri.dev.com.colibritweet.pojo.Tweet;

public class TweetAdapter extends RecyclerView.Adapter<TweetAdapter.TweetViewHolder> {
    private static final String TWITTER_RESPONSE_FORMAT="EEE MMM dd HH:mm:ss ZZZZZ yyyy"; // Thu Oct 26 07:31:08 +0000 2017
    private static final String MONTH_DAY_FORMAT = "MMM d"; // Oct 26

    private List<Tweet> tweetList = new ArrayList<>();

    @Override
    public TweetViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.tweet_item_view, parent, false);
        return new TweetViewHolder(view);
    }

    @Override
    public void onBindViewHolder(TweetViewHolder holder, int position) {
        holder.bind(tweetList.get(position));
    }

    @Override
    public int getItemCount() {
        return tweetList.size();
    }

    public void setItems(Collection<Tweet> tweets) {
        tweetList.addAll(tweets);
        notifyDataSetChanged();
    }

    public void clearItems() {
        tweetList.clear();
        notifyDataSetChanged();
    }

    class TweetViewHolder extends RecyclerView.ViewHolder {
        private ImageView userImageView;
        private TextView nameTextView;
        private TextView nickTextView;
        private TextView creationDateTextView;
        private TextView contentTextView;
        private ImageView tweetImageView;
        private TextView retweetsTextView;
        private TextView likesTextView;

        public TweetViewHolder(View itemView) {
            super(itemView);
            userImageView = itemView.findViewById(R.id.profile_image_view);
            nameTextView = itemView.findViewById(R.id.author_name_text_view);
            nickTextView = itemView.findViewById(R.id.author_nick_text_view);
            creationDateTextView = itemView.findViewById(R.id.creation_date_text_view);
            contentTextView = itemView.findViewById(R.id.tweet_content_text_view);
            tweetImageView = itemView.findViewById(R.id.tweet_image_view);
            retweetsTextView = itemView.findViewById(R.id.retweets_text_view);
            likesTextView = itemView.findViewById(R.id.likes_text_view);
        }

        public void bind(Tweet tweet) {
            nameTextView.setText(tweet.getUser().getName());
            nickTextView.setText(tweet.getUser().getNick());
            contentTextView.setText(tweet.getText());
            retweetsTextView.setText(String.valueOf(tweet.getRetweetCount()));
            likesTextView.setText(String.valueOf(tweet.getFavouriteCount()));

            String creationDateFormatted = getFormattedDate(tweet.getCreationDate());
            creationDateTextView.setText(creationDateFormatted);

            Picasso.with(itemView.getContext()).load(tweet.getUser().getImageUrl()).into(userImageView);

            String tweetPhotoUrl = tweet.getImageUrl();
            Picasso.with(itemView.getContext()).load(tweetPhotoUrl).into(tweetImageView);

            tweetImageView.setVisibility(tweetPhotoUrl != null ? View.VISIBLE : View.GONE);
        }

        private String getFormattedDate(String rawDate) {
            SimpleDateFormat utcFormat = new SimpleDateFormat(TWITTER_RESPONSE_FORMAT, Locale.ROOT);
            SimpleDateFormat displayedFormat = new SimpleDateFormat(MONTH_DAY_FORMAT, Locale.getDefault());
            try {
                Date date = utcFormat.parse(rawDate);
                return displayedFormat.format(date);
            } catch (ParseException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

Полезные материалы:

Полный листинг изменений кода:

Code diff

УВИДЕТЬ ВСЕ Добавить заметку
Вы
Добавить ваш комментарий
 

Сайт использует cookie-файлы для того, чтобы вам было удобнее им пользоваться. Для продолжения работы с сайтом, вам необходимо принять использование cookie-файлов.

Я ознакомлен(а)