Skip to content

YamamotoDesu/RecyclerViewBasic

Repository files navigation

RecyclerViewBasic

Set up a RecyclerView

スクリーンショット 2022-09-21 8 22 58

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/creatureRecyclerView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

list_item_creature.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="@dimen/list_item_creature_height"
    xmlns:tools="https://schemas.android.com/tools"
    >

    <ImageView
        android:id="@+id/creatureImage"
        android:layout_width="@dimen/list_item_creature_height"
        android:layout_height="@dimen/list_item_creature_height"
        android:layout_marginStart="@dimen/padding_standard"
        android:contentDescription="@string/content_description_creature_image"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@drawable/thumbnail_creature_cat_derp" />

   <TextView
        android:id="@+id/fullNameText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="@dimen/padding_half"
        android:layout_marginEnd="@dimen/padding_standard"
        android:layout_marginStart="@dimen/padding_standard"
        android:layout_marginTop="@dimen/padding_half"
        android:textColor="@android:color/black"
        android:textSize="@dimen/creature_name_text"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toEndOf="@+id/creatureImage"
        app:layout_constraintTop_toTopOf="parent"
        android:text="Full Name" />

</androidx.constraintlayout.widget.ConstraintLayout>

ViewGroupExtentions.kt

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.LayoutRes

fun ViewGroup.inflate(@LayoutRes layoutRes: Int, attachToRoot: Boolean = false): View {
    return LayoutInflater.from(context).inflate(layoutRes, this, attachToRoot)
}

CreatureAdapter.kt

class CreatureAdapter(private val creatures: List<Creature>): RecyclerView.Adapter<CreatureAdapter.ViewHolder>() {

    class ViewHolder(v: View) : RecyclerView.ViewHolder(v) {

    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        return ViewHolder(parent.inflate(R.layout.list_item_creature))
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {

    }

    override fun getItemCount() = creatures.count()

}

AllFragment.kt

class AllFragment : Fragment() {

  private val adapter = CreatureAdapter(CreatureStore.getCreatures())

  companion object {
    fun newInstance(): AllFragment {
      return AllFragment()
    }
  }

  override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
    return inflater.inflate(R.layout.fragment_all, container, false)
  }

  override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    creatureRecyclerView.layoutManager = LinearLayoutManager(activity)
    creatureRecyclerView.adapter = adapter
  }
}

CreatureStore.kt

object CreatureStore {

  private const val TAG = "CreatureStore"

  private lateinit var creatures: List<Creature>
  private lateinit var foods: List<Food>

  fun loadCreatures(context: Context) {
    val gson = Gson()
    val json = loadJSONFromAsset("creatures.json", context)
    val listType = object : TypeToken<List<Creature>>() {}.type
    creatures = gson.fromJson(json, listType)
    creatures
      .filter { Favorites.isFavorite(it, context) }
      .forEach { it.isFavorite = true }
    Log.v(TAG, "Found ${creatures.size} creatures")
  }

  fun loadFoods(context: Context) {
    val gson = Gson()
    val json = loadJSONFromAsset("foods.json", context)
    val listType = object : TypeToken<List<Food>>() {}.type
    foods = gson.fromJson(json, listType)
    Log.v(TAG, "Found ${foods.size} food items")
  }

  fun getCreatures() = creatures

  fun getCreatureById(id: Int) = creatures.firstOrNull { it.id == id }

  fun getFoodById(id: Int) = foods.firstOrNull { it.id == id }

  private fun loadJSONFromAsset(filename: String, context: Context): String? {
    var json: String? = null
    try {
      val inputStream = context.assets.open(filename)
      val size = inputStream.available()
      val buffer = ByteArray(size)
      inputStream.read(buffer)
      inputStream.close()
      json = String(buffer)
    } catch (ex: IOException) {
      Log.e(TAG, "Error reading from $filename", ex)
    }
    return json
  }
}

Bind the Views

スクリーンショット 2022-09-21 8 57 17

CreatureAdapter.kt

    class ViewHolder(v: View) : RecyclerView.ViewHolder(v) {
        private lateinit var creature: Creature

        fun bind(creature: Creature) {
            this.creature = creature
            val context = itemView.context
            itemView.creatureImage.setImageResource(context.resources.getIdentifier(creature.uri, null, context.packageName))
            itemView.fullNameText.text = creature.fullName
        }
    }
    
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bind(creatures[position])
    }

Challenge: Add More Data

スクリーンショット 2022-09-21 9 09 57

list_item_creature.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="@dimen/list_item_creature_height">

    <ImageView
        android:id="@+id/creatureImage"
        android:layout_width="@dimen/list_item_creature_height"
        android:layout_height="@dimen/list_item_creature_height"
        android:layout_marginStart="@dimen/padding_standard"
        android:contentDescription="@string/content_description_creature_image"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@drawable/thumbnail_creature_cat_derp" />

    <TextView
        android:id="@+id/fullNameText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="@dimen/padding_standard"
        android:layout_marginTop="@dimen/padding_half"
        android:layout_marginEnd="@dimen/padding_standard"
        android:layout_marginBottom="@dimen/padding_half"
        android:text="Full Name"
        android:textColor="@android:color/black"
        android:textSize="@dimen/creature_name_text"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintBottom_toTopOf="@id/nickname"
        app:layout_constraintStart_toEndOf="@+id/creatureImage"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_chainStyle="packed" />

    <TextView
        android:id="@+id/nickname"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="@dimen/padding_standard"
        android:layout_marginBottom="@dimen/padding_half"
        android:text="Nick Name"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toEndOf="@id/creatureImage"
        app:layout_constraintTop_toBottomOf="@id/fullNameText" />

</androidx.constraintlayout.widget.ConstraintLayout>

CreatureAdapter.kt

        fun bind(creature: Creature) {
            this.creature = creature
            val context = itemView.context
            itemView.creatureImage.setImageResource(context.resources.getIdentifier(creature.uri, null, context.packageName))
            itemView.fullNameText.text = creature.fullName
            itemView.nickname.text = creature.nickname

        }

Respond to Clicks

スクリーンショット 2022-09-21 9 09 57

CreatureActivity.kt

class CreatureActivity : AppCompatActivity() {

  private lateinit var creature: Creature

  companion object {
    private const val EXTRA_CREATURE_ID = "EXTRA_CREATURE_ID"

    fun newIntent(context: Context, creatureId: Int): Intent {
      val intent = Intent(context, CreatureActivity::class.java)
      intent.putExtra(EXTRA_CREATURE_ID, creatureId)
      return intent
    }
  }

CreatureAdapter.kt

    class ViewHolder(v: View) : View.OnClickListener, RecyclerView.ViewHolder(v) {
        private lateinit var creature: Creature

        init {
            itemView.setOnClickListener(this)
        }

        fun bind(creature: Creature) {
            this.creature = creature
            val context = itemView.context
            itemView.creatureImage.setImageResource(context.resources.getIdentifier(creature.uri, null, context.packageName))
            itemView.fullNameText.text = creature.fullName
            itemView.nickname.text = creature.nickname

        }

        override fun onClick(view: View?) {
            view?.let {
                val context = it.context
                val intent = CreatureActivity.newIntent(context, creature.id)
                context.startActivity(intent)
            }
        }
    }

Challenge: Build the Favorites Screen

スクリーンショット 2022-09-22 8 14 51

fragment_favorites.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/favoritesRecyclerView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />


</androidx.constraintlayout.widget.ConstraintLayout>

FavoritesFragment

class FavoritesFragment : Fragment() {

  private val adapter = CreatureAdapter(mutableListOf())

  companion object {
    fun newInstance(): FavoritesFragment {
      return FavoritesFragment()
    }
  }

  override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    favoritesRecyclerView.layoutManager = LinearLayoutManager(activity)
    favoritesRecyclerView.adapter = adapter
  }

  override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
    return inflater.inflate(R.layout.fragment_favorites, container, false)
  }

  override fun onResume() {
    super.onResume()
    activity?.let {
      CreatureStore.getFavoriteCreatures(it)?.let {
        favorites ->
        adapter.updateCreatures(favorites)
      }
    }
  }

CreatureAdapter

class CreatureAdapter(private val creatures: MutableList<Creature>): RecyclerView.Adapter<CreatureAdapter.ViewHolder>() {

    fun updateCreatures(creatures: List<Creature>) {
        this.creatures.clear()
        this.creatures.addAll(creatures)
        notifyDataSetChanged()
    }
    

その他変更

build.gradle

android {
    compileSdkVersion 31
    defaultConfig {
        applicationId "com.raywenderlich.android.creatures"
        minSdkVersion 21
        targetSdkVersion 31
        
android {
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

gradle.properties(app)

distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip

build.gradle(Project)

 kotlin_version = '1.6.0'
    <activity
      android:name=".ui.SplashActivity"
      android:theme="@style/SplashTheme"
      android:exported="true">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>

Releases

No releases published

Packages

No packages published

Languages