Using Kotlin to achieve left-right sliding effect of UC header ViewPager

Keywords: Android github Java Fragment

For reprinting, please indicate the source: A column on maple leaves

In the previous article, we explained a multi-line text display control, which we often encounter in the actual development process: there are two TextView control line display, when the content of the first TextView can not be displayed on more than one line, we need to display the second TextView at the end of the second line of the first TextView, and when the second line of the second TextView is also displayed on the second line. If not, the second line of the first TextView ends with "..." At the end, the second TextView is shown in the last paragraph of the second line, and the previous article introduced a custom control that implements this requirement.

In this article, we will introduce a left-right sliding effect using kotlin, which imitates UC header ViewPager. This project is to learn the use of kotlin and basic grammar. In the process of implementation, two main points need to be noticed: one is the hiding animation effect of UC header in the sliding process, and the other is the hiding effect of sliding across multiple Tab clicks.

The github address of this project: android-xmviewpager Welcome to star t and follow.

Before introducing the specific instructions, let's take a look at the simple implementation effect.

Implementation specification

This project is implemented by TabLayout+ViewPager. Let's first look at the implementation of the layout file of the whole page.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".java.TabLayoutActivity"
    android:orientation="vertical">
    <android.support.design.widget.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:layout_scrollFlags="scroll|enterAlways"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>

        <android.support.design.widget.TabLayout
            android:id="@+id/tabs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:tabIndicatorColor="#ADBE107E"
            app:tabIndicatorHeight="0dp"
            app:tabMode="scrollable"
            app:tabPadding="0dp"
            />

    </android.support.design.widget.AppBarLayout>

    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

</LinearLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

It can be found that it is implemented by TabLayout and ViewPager. In the process of implementation, the onTabSelectedListener listening of TabLayout is rewritten because of the requirement of shielding multiple sliding effects when clicking across multiple Tabs.

/**
     * Custom function,: Unit denotes that the function does not return a value
     */
    fun initViewPager() : Unit {
        /**
         * Get initialization data
         */
        val titles = ViewData().getTitles()

        /**
         * as Similar to type override in java
         */
        val toolbar = findViewById(R.id.toolbar) as Toolbar
        setSupportActionBar(toolbar)
        mViewPager = findViewById(R.id.viewpager) as ViewPager
        mTabLayout = findViewById(R.id.tabs) as TabLayout

        /**
         * Cyclic traversal through in keyword
         * When calling the method of mTabLayout variable, because mTabLayout may be empty, add it when calling method!!
         * titles[] The same function as the titles.get method
         * titles.indices Gets the subscript of the array
         */
        for (i in titles.indices) {
            mTabLayout!!.addTab(mTabLayout!!.newTab().setText(titles[i]))
        }

        val fragments = ArrayList<Fragment>()

        /**
         * Loop traversal adds Fragment of ViewPager
         */
        for (i in titles.indices) {
            val listFragment = MListFragment()
            val bundle = Bundle()
            val sb = StringBuffer()
            for (j in 1..8) {
                sb.append(titles[i]).append(" ")
            }
            bundle.putString("content", sb.toString())
            listFragment.arguments = bundle
            fragments.add(listFragment)
        }

        val mFragmentAdapteradapter = MFragmentAdapter(supportFragmentManager, fragments, titles)
        mViewPager!!.adapter = mFragmentAdapteradapter
        mViewPager!!.adapter = mFragmentAdapteradapter
        mTabLayout!!.setupWithViewPager(mViewPager)
        mTabLayout!!.setTabsFromPagerAdapter(mFragmentAdapteradapter)

        /**
         * Custom Settings ViewPager Switch Animation
         */
        mViewPager!!.setPageTransformer(true, MTransformer())

        /**
         * Create internal anonymous classes (mainly interfaces) through object: TabLayout. OnTabSelectedListener
         */
        mTabLayout!!.setOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
            override fun onTabReselected(tab: TabLayout.Tab?) {
            }

            override fun onTabUnselected(tab: TabLayout.Tab?) {
            }

            override fun onTabSelected(tab: TabLayout.Tab?) {
                /**
                 * control variable
                 */
                if (isOk) {
                    isOk = false
                    val currentItemIndex = mViewPager!!.currentItem

                    if (Math.abs(currentItemIndex - tab!!.position) > 1) {
                        /**
                         * Click back
                         */
                        if (currentItemIndex <= tab!!.position) {
                            mViewPager!!.setCurrentItem(tab.position - 1, false)
                            mViewPager!!.setCurrentItem(tab.position, true)
                        }
                        /**
                         * Forward Click
                         */
                        else {
                            mViewPager!!.setCurrentItem(tab.position + 1, false)
                            mViewPager!!.setCurrentItem(tab.position, true)
                        }
                    } else {
                        mViewPager!!.setCurrentItem(tab.position, true)
                    }

                    isOk = true
                }
            }
        })

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98

Then we can continue to look at the implementation of initialization data:

/**
 * Created by aaron on 16/9/14.
 * Mainly used for saving ViewPager data in the interface
 */
class ViewData {

    /**
     * This method is used to obtain ViewPager TAB display data.
     */
    fun getTitles() : ArrayList<String> {
        /**
         * Create an object of this class by its class name, where the collection framework in java is directly invoked
         */
        val titles = ArrayList<String>()

        titles.clear()
        titles.add("Recommend")
        titles.add("video")
        titles.add("Hotspot")
        titles.add("entertainment")
        titles.add("Sports")
        titles.add("Beijing")
        titles.add("Finance")
        titles.add("science and technology")
        titles.add("automobile")
        titles.add("Sociology")
        titles.add("Funny")
        titles.add("Military")
        titles.add("History")
        titles.add("Rising knowledge")
        titles.add("NBA")
        titles.add("Bisexual")

        return titles
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

Well, one of the things to note is that viewPager's setCurrentItem method sets viewPager's current display Item to the specified item, and we can see that setCurrentItem here has two parameters, the first one is to display the position of the current Item, and the second one is to boolean type, indicating whether there is a sliding effect, such as the current one in ViewPager. The first item, and we clicked on the eighth item of TabLayout, and if we call setCurrent Item (8, true), it means that we will slide to the eighth item of ViewPager and have a scrolling effect. Let's make a change. When the distance between the TabLayout and the current Item is greater than one Item, we first slide to the front of the current Item and have no sliding effect. Then we execute the setCurrentItem method once, which blocks the effect of multiple Item scrolling across multiple Tab clicks.

In the process of implementation, we also need to achieve the effect of sliding coverage. At first, we thought about using the setPage Transformer method of ViewPager for a long time, but we still couldn't realize this idea. Later, we got it through the advice of our colleagues. It is to animate the sub-View in each item of ViewPager, so that the animation effect of the requirement can be achieved.

The following is a self-rewritten setPageTransformer class:

/**
 * Created by aaron on 16/9/13.
 * Custom Implementation of ViewPager Switching Animation Effect
 */
class MTransformer : ViewPager.PageTransformer {

    /**
     * Callback method to rewrite the switching animation of viewpager
     */
    override fun transformPage(view: View, position: Float) {
        val pageWidth = view.width
        val wallpaper = view.findViewById(R.id.recycler_view)
        if (position < -1) { // [-Infinity,-1)
            wallpaper.translationX = 0.toFloat()
            view.translationX = 0.toFloat()
        } else if (position <= 1) { // [-1,1]
            wallpaper.translationX = pageWidth * getFactor(position)
            view.translationX = 8 * position
        } else { // (1,+Infinity]
            wallpaper.translationX = 0.toFloat()
            view.translationX = 0.toFloat()
        }
    }

    private fun getFactor(position: Float): Float {
        return -position / 2
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

We can see that in our custom Page Transformer, we get the sub-View of sliding Item by findViewById method, and perform translationX operation on the sub-View, thus realizing the hiding effect of sliding Item.

In addition, because this paper mainly introduces the use of Kotlin, more knowledge about Kotlin can be referred to:

Basic Syntax - Kotlin Programing

Kotlin: Swift of Android Event

Application of Kotlin in Android Project

Of course, more specific about the implementation of this control can be downloaded source reference.

Conclusion:

The above is a small project implemented by Kotlin, which imitates the left and right sliding effect of UC header ViewPager. Of course, it is still not perfect, for students interested in source code can go to github to see the specific implementation. Project address: android-xmviewpager


In addition, for github project, students interested in open source project analysis can refer to me:
Github project resolution (1) - > upload Android project to GitHub
Github Project Resolution (II) - > Publish Android Project to JCenter Code Library
Github project resolution (3) - > leakcanary for Android memory leak monitoring
Github Project Resolution (IV) - > Dynamic Change of TextView Font Size
Github Project Resolution (V) - > Android Logging Framework
Github Project Resolution (6) - > Custom Implementation ButterKnife Framework
Github Project Resolution (7) - > Prevent buttons from clicking repeatedly
Github Project Resolution (8) - > Five Ways to Get Component Width During Activity Startup
Github Project Analysis (9) - > Five Ways to Realize Activity Jump Painting
Github Project Analysis (10) - > Quick Integration of Two-Dimensional Code Scanning Library with Several Lines of Code
Github Project Resolution (11) - > A Simple, Powerful Custom Advertising Activity Bullet Window
Github Project Resolution (12) - > A Simple Multi-Line Text Display Control

(function () {('pre.prettyprint code').each(function () { var lines = (this).text().split(′\n′).length;varnumbering = $('
    ').addClass('pre-numbering').hide(); (this).addClass(′has−numbering′).parent().append(numbering); for (i = 1; i

    Posted by jcbones on Fri, 29 Mar 2019 08:45:28 -0700