background

In the project’s practice, the UI of the Banner changes frequently. I wanted to implement a BannerView that could change its UI at will.

The problem

  • implementationBannerViewTo solve what?
  • What should it have as a common dependency library?

We’ll answer that at the end of the article.

plan

The most common UI in Android is lists, and then we use RecyclerView to create different lists. If we understand the Banner as a list, RecyclerView can freely realize the item style characteristics also fit our needs to replace the Banner UI at will. Then based on RecyclerView to achieve BannerView should be our first choice.

If use RecyclerView to achieve Banner. The first problem is how to solve the problem of only switching one item per drag, similar to the sliding switching effect of ViewPager. Happily in RecyclerView provides a SnapHelper, we use an extension of its class PagerSnapHelper to assist sliding will solve our switching effect problem.

Another problem is how to achieve continuous sliding switch, here we use inRecyclerViewAdd one data item at the beginning and one at the end of each data item. As shown in figure:As shown in the figure above, whenRecyclerViewWhen sliding to the light green page 3, it will statically switch to the dark green page 3, and when sliding to the light green page 1, it will statically switch to the dark green page 1 (the UI and data of two pages 3 and two pages 1 are consistent, so the user can’t perceive that the program triggered the switching action), thus realizing the continuous sliding switch. You can see the logic.

practice

The overall functional logic is very simple, the core logic only needs to process RecyclerView to slide to the corresponding position to switch. We use onScrollStateChanged to listen to the sliding state of RecyclerView, judge the current page when the sliding stops and switch accordingly.

// Add slide listener
addOnScrollListener(object : RecyclerView.OnScrollListener() {
    override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
        super.onScrollStateChanged(recyclerView, newState)
        // Determine the sliding state
        when (newState) {
            RecyclerView.SCROLL_STATE_DRAGGING -> {
                // When the user drags...
                switch(recyclerView, pagerSnap)
            }
            RecyclerView.SCROLL_STATE_IDLE -> {
                // When the slide stops...
                switch(recyclerView, pagerSnap)
            }
            else-> {}}}})Copy the code

Core switching logic

private fun switch(recyclerView: RecyclerView, snap: PagerSnapHelper) {
    val layoutManager: LinearLayoutManager = recyclerView.layoutManager as LinearLayoutManager
    valcurrentView: View = snap.findSnapView(layoutManager) ? :return
    // Get the number of lists
    val bannerCount: Int = layoutManager.itemCount
    // Get the current slide page
    _currentPosition = layoutManager.getPosition(currentView)
    when (_currentPosition) {
        0- > {// If the current page is the first item in the list data
            // Follow the example above to switch to the penultimate item in the list
            _currentPosition = bannerCount - 2
            recyclerView.scrollToPosition(_currentPosition)
        }
        bannerCount - 1- > {// If the current page is the last item in the list
            // Follow the example above to switch to the second item in the list
            _currentPosition = 1
            recyclerView.scrollToPosition(_currentPosition)
        }
        else-> {}}}Copy the code

Another problem here is that if we only switch according to the end of the slide, then when we quickly slide, we may not finish the slide of the page switch, we will slide to the next page, at this time, because the monitor of the slide end is not called back, so our switch method is not executed. If we slide to the last item or the first item of RecyclerView data, we will pause because there is no data behind so that we can’t continue to slide. So we do a toggle as soon as the user starts dragging, which gives us an almost infinite sliding effect.

The custom

How to customize the Banner UI style, because we are extended from RecyclerView, so theoretically inherited all the characteristics of RecyclerView. Now we can use BannerView just like RecyclerView. Custom Banner UI style, as long as a custom item layout. BannerView data types can also be customized based on business data.

// Inherit the BannerAdapter // customize the data type based on the business data // customize the item layout
class CustomAdapter : BannerAdapter<CustomModel>(R.layout.item_custom) {
    
    override fun onBindViewHolder(holder: BannerViewHolder, item: CustomModel, position: Int) {
        // Get the control on the custom layout through holder.findViewById()}}Copy the code

A DefaultBannerAdapter is also provided for easy use, but the data type only supports String. If no custom requirements can be used directly.

val defaultAdapter: DefaultBannerAdapter = object : DefaultBannerAdapter() {
    override fun onBind(data: String, view: ImageView) {
        // Can handle some work
        // Set the ImageView properties, load images, etc}}Copy the code

So the basic function is done. The same idea applies to a text-type Banner, TextBannerView. A DefaultTextBannerAdapter is also provided for easy use. If customization is required, you can simply inherit the BannerAdapter and implement your own adapter. The end result would look something like this:

The last

Now let’s answer the question at the beginning of the article:

  • Custom styles are now easy to implement, eliminating the frequent need to modify the UI.
  • BannerViewNo longer dependent on any third party libraries, while allowing us to use simple and fast with high scalability, as a public dependency library should be qualified.

For more usage methods and properties, visit the project homepage: github.com/mminng/Bann…

Thank you very much!