Step by step examples to teach you TimeLineView.

1. TimeLineView

A simple Timeline View that demonstrates the power of ConstraintLayout and RecyclerView. No drawing, just plug in and play..

Android Timeline View Library demonstrate the the power of ConstraintnLayout and RecyclerView.

Here is the demo:

TimeLineView Tutorial

TimeLineView Tutorial

TimeLineView Tutorial

Follow these steps to use it:

Step 1. Include library

Using Gradle:

TimelineView is currently available in on Jitpack so add the following line before every other thing if you have not done that already.

allprojects {
  repositories {
    ...
    maven { url 'https://jitpack.io' }
  }
}

Then add the following line

dependencies {
  implementation 'com.github.po10cio:TimeLineView:1.0.2'
}

Using Maven

Also add the following lines before adding the maven dependency

<repositories>
  <repository>
    <id>jitpack.io</id>
    <url>https://jitpack.io</url>
  </repository>
</repositories>

Then add the dependency

<dependency>
  <groupId>com.github.po10cio</groupId>
  <artifactId>TimeLineView</artifactId>
  <version>1.0.2</version>
</dependency>

Step 2. Add to Layout

In your XML layout include the TimelineView as follows:

<me.jerryhanks.stepview.TimeLineView
  android:id="@+id/timelineView"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:layout_marginBottom="8dp"
  android:layout_marginLeft="8dp"
  android:layout_marginRight="8dp"
  android:layout_marginTop="16dp">

Step 3: Write Code

Then in your Kotlin code, do the following:
Create a class that extends TimeLine

class MyTimeLine(status: Status, var title: String?, var content: String?) : TimeLine(status) {
  override fun equals(other: Any?): Boolean {
    if (this === other) return true
    if (javaClass != other?.javaClass) return false
        other as MyTimeLine

    if (title != other.title) return false
    if (content != other.content) return false

    return true
  }

  override fun hashCode(): Int {
    var result = if (title != null) title!!.hashCode() else 0
    result = 31 * result + if (content != null) content!!.hashCode() else 0

    return result
  }

  override fun toString(): String {
    return "MyTimeLine{" +
      "title='" + title + ''' +
      ", content='" + content + ''' +
      '}'
  }
}

TimeLine has three Statuses:

  • Status.COMPLETED
  • Status.UN_COMPLETED
  • Status.ATTENTION
    You can choose from any of the statuses depending on the status of the item you want to represent.
    Create an Array of your TimeLine
val timeLines = mutableListOf<MyTimeLine>()
  .apply {
    add(MyTimeLine(Status.COMPLETED, getString(R.string.s_title_1), getString(R.string.s_content_1)))
    add(MyTimeLine(Status.COMPLETED, getString(R.string.s_title_2), getString(R.string.s_content_2)))
    add(MyTimeLine(Status.COMPLETED, getString(R.string.s_title_3), getString(R.string.s_content_3)))
    add(MyTimeLine(Status.COMPLETED, getString(R.string.s_title_4), getString(R.string.s_content_4)))
    add(MyTimeLine(Status.COMPLETED, getString(R.string.s_title_5), getString(R.string.s_content_5)))
    add(MyTimeLine(Status.COMPLETED, getString(R.string.s_title_6), getString(R.string.s_content_6)))
    add(MyTimeLine(Status.COMPLETED, getString(R.string.s_title_7), getString(R.string.s_content_7)))
  }

Create the IndicatorAdapter and add it to the TimeLineView

val adapter = IndicatorAdapter(mutableListOf(), this, object : TimeLineViewCallback<MyTimeLine> {
  override fun onBindView(model: MyTimeLine, container: FrameLayout, position: Int): View {
    val view = layoutInflater
      .inflate(R.layout.sample_time_line,
      container, false)

    (view.findViewById<TextView>(R.id.tv_title)).text = model.title
    (view.findViewById<TextView>(R.id.tv_content)).text = model.content

    return view
  }
})

timelineView.setIndicatorAdapter(adapter)
adapter.swapItems(timeLines)
  • Swaps the old items with the new items
fun swapItems(timeLines: List<T>)
  • Update a single item given the index
fun updateItem(timeline: T, position: Int)
  • Adds a list of items to the list starting from the given index
fun addItems(vararg items: T, index: Int = itemCount)

Full Example

Let us look at a full TimeLineView Example based on this library:

Step 1. Design Layouts

For this project let's create the following layouts:

(a). activity_main.xml

Our activity_main layout.

This layout will represent our Main Activity's layout. Specify android.support.constraint.ConstraintLayout as it's root element then inside it place the following widgets:

  1. TextView
  2. Button
  3. TimeLineView
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="me.jerryhanks.demo.MainActivity">

    <TextView
        android:id="@+id/caption"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_alignParentTop="true"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:textAppearance="@style/TextAppearance.AppCompat.Light.SearchResult.Title"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="@string/delivery_status" />

    <Button
        android:id="@+id/btnAddToTop"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="addToTop"
        android:text="@string/add_to_top"
        android:textAllCaps="false" />

    <Button
        android:id="@+id/btnAddToBottom"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="addToBottom"
        android:text="@string/add_to_bottom"
        android:textAllCaps="false"
        app:layout_constraintEnd_toEndOf="parent" />

    <me.jerryhanks.timelineview.TimeLineView
        android:id="@+id/timelineView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginBottom="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginTop="16dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/caption"
        app:layout_constraintVertical_bias="0.0" />

</android.support.constraint.ConstraintLayout>

Step 2. Write Code

Finally we need to write our code as follows:

(a). MyTimeLine.kt

Our MyTimeLine class.

Create a Kotlin file named MyTimeLine.kt .

Then extend the TimeLine(status) and add its contents as follows:

First override these callbacks:

  1. equals(other: Any?): Boolean.
  2. hashCode(): Int.
  3. toString(): String.

Here is the full code:

package replace_with_your_package_name

import me.jerryhanks.timelineview.model.Status
import me.jerryhanks.timelineview.model.TimeLine

/**
 * @author <@Po10cio> on 10/18/17 for StepViewApp
 */

 class MyTimeLine(status: Status, var title: String?, var content: String?) : TimeLine(status) {

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false

        other as MyTimeLine

        if (title != other.title) return false
        if (content != other.content) return false

        return true
    }

    override fun hashCode(): Int {
        var result = if (title != null) title!!.hashCode() else 0
        result = 31 * result + if (content != null) content!!.hashCode() else 0
        return result
    }

    override fun toString(): String {
        return "MyTimeLine{" +
                "title='" + title + ''' +
                ", content='" + content + ''' +
                '}'
    }

}

(b). MainActivity.kt

Our MainActivity class.

Create a Kotlin file named MainActivity.kt and then extend the AppCompatActivity and add its contents as follows:

First override these callbacks:

  1. onCreate(savedInstanceState: Bundle?).
  2. onBindView(model: MyTimeLine, container: FrameLayout, position: Int): View.

Then we will be creating the following functions:

  1. addToTop().
  2. addToBottom().

(a). Our addToBottom() function

Write the addToBottom() function as follows:

    fun addToBottom() {
        val timeLine = MyTimeLine(Status.ATTENTION, "New Bottom One", "hahahaha")
        adapter.addItems(timeLine)
        timelineView.scrollToBottom()
    }

(b). Our addToTop() function

Write the addToTop() function as follows:

    fun addToTop() {
        val timeLine = MyTimeLine(Status.ATTENTION, "New Top One", "heiheihei")
        adapter.addItems(timeLine, index = 2)
        timelineView.scrollToTop()
    }

Here is the full code:

package replace_with_your_package_name

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.view.View
import android.widget.FrameLayout
import android.widget.TextView
import kotlinx.android.synthetic.main.activity_main.*
import me.jerryhanks.timelineview.IndicatorAdapter
import me.jerryhanks.timelineview.interfaces.TimeLineViewCallback
import me.jerryhanks.timelineview.model.Status

class MainActivity : AppCompatActivity() {

    private lateinit var adapter: IndicatorAdapter<MyTimeLine>

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val timeLines = mutableListOf<MyTimeLine>()
                .apply {
                    add(MyTimeLine(Status.COMPLETED, getString(R.string.s_title_1), getString(R.string.s_content_1)))
                    add(MyTimeLine(Status.COMPLETED, getString(R.string.s_title_2), getString(R.string.s_content_2)))
                    add(MyTimeLine(Status.COMPLETED, getString(R.string.s_title_3), getString(R.string.s_content_3)))
                    add(MyTimeLine(Status.COMPLETED, getString(R.string.s_title_4), getString(R.string.s_content_4)))
                    add(MyTimeLine(Status.COMPLETED, getString(R.string.s_title_5), getString(R.string.s_content_5)))
                    add(MyTimeLine(Status.COMPLETED, getString(R.string.s_title_6), getString(R.string.s_content_6)))
                    add(MyTimeLine(Status.COMPLETED, getString(R.string.s_title_7), getString(R.string.s_content_7)))

                    // more data
                    add(MyTimeLine(Status.COMPLETED, getString(R.string.s_title_8), getString(R.string.s_content_8)))
                    add(MyTimeLine(Status.COMPLETED, getString(R.string.s_title_9), getString(R.string.s_content_9)))
                    add(MyTimeLine(Status.COMPLETED, getString(R.string.s_title_10), getString(R.string.s_content_10)))
                    add(MyTimeLine(Status.COMPLETED, getString(R.string.s_title_11), getString(R.string.s_content_11)))
                    add(MyTimeLine(Status.COMPLETED, getString(R.string.s_title_12), getString(R.string.s_content_12)))
                }

        adapter = IndicatorAdapter(mutableListOf(), this, object : TimeLineViewCallback<MyTimeLine> {
            override fun onBindView(model: MyTimeLine, container: FrameLayout, position: Int): View {
                val view = layoutInflater
                        .inflate(R.layout.sample_time_line,
                                container, false)

                (view.findViewById<TextView>(R.id.tv_title)).text = model.title
                (view.findViewById<TextView>(R.id.tv_content)).text = model.content

                return view
            }
        })
        timelineView.setIndicatorAdapter(adapter)
        adapter.swapItems(timeLines)

        //set the title
//        caption.text = getString(R.string.timeline_of_world_war_i)
        caption.text = getString(R.string.delivery_status)

        //set add buttons onclick lsisteners
        btnAddToTop.setOnClickListener({ addToTop() })
        btnAddToBottom.setOnClickListener({ addToBottom() })

    }

    /**
     * Adds an item to the top of the list and scroll to the top
     */
    fun addToTop() {
        val timeLine = MyTimeLine(Status.ATTENTION, "New Top One", "heiheihei")
        adapter.addItems(timeLine, index = 2)
        timelineView.scrollToTop()
    }

    /**
     * Add an item to the bottom of the list and scroll to the bottom
     */
    fun addToBottom() {
        val timeLine = MyTimeLine(Status.ATTENTION, "New Bottom One", "hahahaha")
        adapter.addItems(timeLine)
        timelineView.scrollToBottom()
    }
}

Reference

Download the code below:

No. Link
1. Download Full Code
2. Read more here.
3. Follow code author here.

More

Here are some more examples related to TimeLineView.