Giter Site home page Giter Site logo

thulasiram-sarathy / rxjava-search-like-netflix Goto Github PK

View Code? Open in Web Editor NEW
4.0 2.0 2.0 3.29 MB

Android Netflix app like search using Rxjava with Epoxy recyclerview

Home Page: https://medium.com/@thulasiram.sarathy/android-search-using-rxjava-epoxy-recyclerview-retrofit-e906d0cf5c36

License: Apache License 2.0

Kotlin 100.00%
android rxjava2 rxandroid2 epoxy retrofit2 kotlin tmdb-movie-search mvvm-android livedata-viewmodel airbnb-epoxy

rxjava-search-like-netflix's Introduction

Rxjava-Search-like-Netflix

Android Search using Rxjava (Epoxy Recyclerview, Retrofit)

In this article I have implemented Android search using RxJava for instant search results. Various libraries such as Airbnb’s Epoxy recyclerview, Retrofit for network call is implemented for fetching results from the TMDB api.



Getting started

Demo link: Check here!!!

Usages:

  1. RxJava
  2. Epoxy library
  3. Retrofit
  4. TMDB API

RxJava

For the basic implementation of a search view in RxJava I have used four operators namely debounce, filter, distinctUntilChanged and switchMap.

Debounce : Returns an Observable that mirrors the source ObservableSource, except that it drops items emitted by the source ObservableSource that are followed by newer items before a timeout value expires. The timer resets on each emission.
In Other words using the debounce will prevent the search request or letters being sent to the server immediately, meaning debounce(300,TimeUnit.MILLISECONDS) will wait for 300 milliseconds in sending every request to the server. If your search query is “armageddon” and for each the my character tying speed is 100 milliseconds per letter then the request query sent to the server would be arm, armage, armageddo since it debounce wait for 300 ms of every request.

If no debounce is used the request to the server be like a, ar, arm, arma…. 4 network call is being made for 4 characters typed which is a costly one.

Filter : Filters items emitted by an ObservableSource by only emitting those that satisfy a specified predicate.
Filter operator are used in the search feature to filter out needless queries such as empty requests or if I need to send network request for queries more than 2 characters filter operator can be used. Suppose if, filter{it >2} is used and typing a search query the network request is sent only after tying arm because it allows network calls to be made only the queries greater than 2 characters.

DistinctUntilChanged: Returns an Observable that emits all items emitted by the source ObservableSource that are distinct from their immediate predecessors.
distinctUntilChanged operator is used to avoid unnecessary or identical network calls. This operator stops request to sent to the server if has already fetched, eg: if the my current query is “Dunkr” and I cancelling out the typed query by a character say “Dunk” since a network request has already been sent for “Dunk” the distinctUntilChanged operator wont allow a new network call instead it will retrieve from the previous result.

SwitchMap: Returns a new ObservableSource by applying a function that you supply to each item emitted by the source ObservableSource that returns an ObservableSource, and then emitting the items emitted by the most recently emitted of these ObservableSources.
switchMap is as powerful operator very much like other operators. Suppose if the search query is “Dunk “ and the network call is made and the observer is subscribed for this result and in the meantime another query is made which is for “Dunkr” and a network call is again made, what the switchMap operator does is that it helps the observer to be subscribed to the latest query which is “Dunkr” since the network response for the query “Dunk” is no longer required.

Epoxy Implementation:

Epoxy is an Android library for building complex screens in a RecyclerView.

build.gradle

    dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.core:core-ktx:1.2.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'

    //Airbnb Epoxy
    implementation "com.airbnb.android:epoxy:$epoxyVersion"
    kapt "com.airbnb.android:epoxy-processor:$epoxyVersion"
    implementation "com.airbnb.android:epoxy-paging:$epoxyVersion"

    implementation 'android.arch.lifecycle:extensions:1.1.1'

    implementation 'androidx.cardview:cardview:1.0.0'

    /* Retrofit - Networking library  */
    implementation 'com.squareup.retrofit2:retrofit:2.4.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
    implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
    implementation 'com.squareup.okhttp3:logging-interceptor:3.9.1'

    /* Picasso - Image Loading library  */
    implementation 'com.squareup.picasso:picasso:2.71828'

    /*RXJava*/
    implementation 'io.reactivex.rxjava2:rxjava:2.1.9'
    implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'

    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    androidTestImplementation 'androidx.test:rules:1.3.0-alpha02'
    }

SearchController.kt
class SearchController(private val listener:SearchClickListener) :TypedEpoxyController<List<SearchResponse.MovieApiResponse>>(){

    override fun buildModels(data: List<SearchResponse.MovieApiResponse>) {
        data.forEach {
            HomeViewModel_()
            .id(it.id)
            .image(it.poster_path)
            .moviename(it.title)
            .movieRating(it.voteAverage.div(2).toFloat())
            .movieOnClickListener { _, _, _, _ ->
                listener.onSearchMovieItemClicked(it)
            }
                .addTo(this)
        }

    }

}



EpoxyView.kt

@ModelView(autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT,fullSpan = false)
class HomeView @JvmOverloads constructor(context: Context,
                                          attributes: AttributeSet?=null,
                                          defStyle:Int = 0)
    :CardView(context,attributes,defStyle)
{

    private val posterImage: ImageView
    private val movieName: TextView
    private val movieRating: RatingBar
    private val movielayouyView: CardView

    init {
        View.inflate(context, R.layout.movie_item,this)

        posterImage = findViewById(R.id.posterImageView)
        movieName = findViewById(R.id.movieName)
        movieRating = findViewById(R.id.movieRating)
        movielayouyView = findViewById(R.id.movieCardLayout)
    }

    @ModelProp
    fun setImage(url:String?)
    {
        posterImage.loadMovieImage(url)

    }

    @ModelProp
    fun setMoviename(movie:String?)
    {
        movieName.text = movie

    }
    @ModelProp
    fun setMovieRating(rating:Float)
    {
        movieRating.rating = rating

    }

    @CallbackProp
    fun setMovieOnClickListener(listener: OnClickListener?) {
        movielayouyView.setOnClickListener(listener)
    }


}



SearchActivty.kt

       val searchListController = SearchController(object : SearchClickListener {
            override fun onSearchMovieItemClicked(movie: SearchResponse.MovieApiResponse) {

            }
        })

        searchRecyclerview.adapter =searchListController.adapter
        searchRecyclerview.setItemSpacingDp(4)

        searchViewModel.apiMovieData().observe(this , Observer {
            searchListController.setData(it)
        })

        searchRecyclerview.setupGridManager(searchListController)



SearchViewModel.kt

    fun fetchMoviesList(query: String){
            val observable = restApi.getMoviesList(query,TMDB_KEY)
            observable?.subscribeOn(Schedulers.io())
                ?.observeOn(AndroidSchedulers.mainThread())
                ?.compose(RxSingleSchedulers.DEFAULT.applySchedulers<SearchResponse>())
                ?.subscribe({ loginResponse ->
                    apiData.postValue(loginResponse)

                }, { error ->
                   // handle loading during error
                }
                )

    }

   
    fun fetchMovie(){
        val observable = Observable.just(apiData.value?.results)
        observable.subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe { loginResponse ->
                getMovieLiveData.postValue(loginResponse)
            }
    }



RestApi.kt

interface RestApi {

    @GET("search/movie")
    fun getMoviesList(@Query("query") query: String?,@Query("api_key") api_key:String
    ): Single<SearchResponse>

    companion object {

        fun create(httpUrl: HttpUrl): RestApi {
            val okHttpClient = OkHttpClient.Builder()
                .build()
            return Retrofit.Builder()
                .baseUrl(httpUrl)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .client(okHttpClient)
                .build()
                .create(RestApi::class.java)
        }


        fun create(): RestApi = create(HttpUrl.parse(BASE_URL)!!)
    }

}



Thanks for going through my blog post and send me feedback so that I could update myself for future posts.
!!!! Cheers !!!!

rxjava-search-like-netflix's People

Contributors

thulasiram-sarathy avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.