Android:实现淘宝头条滚动广告的效果

仿淘宝头条滚动广告的实现。

1、需求示意

  • 淘宝效果

《Android:实现淘宝头条滚动广告的效果》 淘宝手机端滚动广告效果

近期项目中需要实现上图中类似于淘宝头条的滚动效果,查阅相关资料之后发现Android原生的ViewFlipper 和 AdapterViewFlipper就可以做到上述效果,所以,做一波相应的总结吧。下图为仿写效果。

  • 实现的效果

《Android:实现淘宝头条滚动广告的效果》 模拟实现效果

2、ViewFlipper和AdapterViewFlipper介绍

(1)、ViewFlipper

1) 继承关系

《Android:实现淘宝头条滚动广告的效果》 image

如上图,我们可以获知 ViewFlipper是ViewAnimator 的子类。而ViewAnimator是一个容器,当其中包含多个子View时,可以设置子View之间切换/显示时的动画效果。同一时刻只有一个子View显示在界面中

2) 常用属性

  • autoStart

是否自动切换子View。true——自动切换;false——不自动切换,此时,需要手动调用 startFlipping()/stopFlipping()来启动或停止切换。该属性对应的方法为:setAutoStart(boolean)

  • flipInterval

子View之间切换的间隔时长,单位毫秒 ms

  • inAnimation

定义view显示时的动画效果,该属性继承自 ViewAnimator

  • outAnimation

定义view隐藏时显示的动画效果,该属性继承自 ViewAnimator

  • animateFirstView

第一个子View是否显示动画效果.(注意,这个设置之后并没有看出啥效果)

(2)、AdapterViewFlipper

1)、继承关系

《Android:实现淘宝头条滚动广告的效果》 image

如上图,AdapterViewFlipper继承自AdapterViewAnimator,而后者又继承自AdapterView,看到这里是不是很熟悉?对的,ListView也是AdapterView的子类——确切的说是孙子类。

2)、常用属性

参考 ViewFlipper 中的常用属性。

(3)、两者的对比

1)、复用性

  • ViewFlipper中的子view只能在xml中提前写好,或者在代码中通过 addView 的形式添加,这种形式不具有复用性质,数据量比较少的时候可以使用

  • AdapterViewFlipper 具有复用属性,可以处理大量的数据

2)、动画效果

  • ViewFlipper在设置动画时接收的是Animation——补间动画,可以是单一动画效果,也可以是动画集合。
  • AdapterViewFlipper 在设置动画的时候接收的是 ObjectAnimator,只能是单一动画,不能使用动画集合

3)、关于默认的interval

  • ViewFlipper的默认Interval为3000ms,即 3秒
  • AdapterViewFlipper的默认Interval为10000ms,即 10秒

基于以上对比,在实际使用的时候可以根据需求动态决定使用哪一个。

3、完整示例代码——kotlin版:

关于ViewFlipper和AdapterViewFlipper的其他注意事项,请查阅代码中的注释内容

  • ViewFlipperActivity.kt

import android.animation.AnimatorInflater
import android.animation.ObjectAnimator
import android.graphics.Color
import android.os.Bundle
import android.view.animation.TranslateAnimation
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import com.cnpeng.android2.R
import kotlinx.android.synthetic.main.activity_view_flipper.*

/**
 * CnPeng 2018/11/30 12:14 PM
 * 功用:仿淘宝客户端中的 淘宝头条滚动广告
 * 说明:
 * - 1、
 * - 2、除了这两种方式之外,还可以考虑使用RecyclerView 或 ViewPager实现 该效果,代码省略
 */
class ViewFlipperActivity : AppCompatActivity() {
    private lateinit var mActivity: ViewFlipperActivity
    var mStrList = mutableListOf<String>()

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

        initCommonVariables()
        initViewFlipper()
        initAdapterViewFlipper()
    }

    private fun initCommonVariables() {
        mActivity = this

        for (i in 1..15) {
            mStrList.add("这是第" + i + "个元素")
        }
    }

    private fun initViewFlipper() {

        //CnPeng 2018/11/30 2:21 PM 添加view数据
        for ((index, str) in mStrList.withIndex()) {
            val textView = TextView(mActivity)
            textView.text = str

            if (0 == index % 2) {
                textView.setTextColor(Color.parseColor("#FFFF0000"))
                //CnPeng 2018/11/30 1:38 PM 这种方式也可以
                //textView.setTextColor(Color.RED)
            } else {
                textView.setTextColor(ContextCompat.getColor(mActivity, R.color.c_666666))
            }
            viewFlipper.addView(textView)
        }

        //setViewFlipperAnimation()
    }

    /**
     * CnPeng 2018/11/30 2:22 PM
     * 功用:设置ViewFlipper的切换动画
     * 说明:这是代码设置的方式,也可以从布局文件中通过 inanimation 、outanimation 属性设置
     * -1、在ViewFlipper中 DEFAULT_INTERVAL 为 3000,即 3秒。
     * -2、在执行inAnimation 和 outAnimation 时,如果两者的duration之和等于我们设置的 interval,则动画能正常展示;否则,动画不能正常展示
     * -3、可以使用动画集合,因为在设置动画时接收的对象为 Animation,而 AnimationSet 是 Animation的子类
     */
    private fun setViewFlipperAnimation() {
        val inAnimation = TranslateAnimation(0f, 0f, 100f, 0f)
        inAnimation.duration = 1500
        //CnPeng 2018/11/30 4:20 PM 此处不需要调用start。viewFlipper在展示child的时候会主动触发动画。但是,调用了在界面上也看不出特殊
        //        inAnimation.start()

        val outAnimation = TranslateAnimation(0f, 0f, 0f, -100f)
        outAnimation.duration = 1500
        //        outAnimation.start()

        viewFlipper.inAnimation = inAnimation
        viewFlipper.outAnimation = outAnimation
    }

    private fun initAdapterViewFlipper() {
        val flipperAdapter = AdapterFlipperViewAdapter(mStrList, mActivity)
        adapterViewFlipper.adapter = flipperAdapter

        // setAdapterViewFlipperAnimator()
//        setAdapterViewFlipperAnimator2()
    }


    /**
     * CnPeng 2018/11/30 4:31 PM
     * 功用:
     * 说明:
     * -1、在AdapterViewFlipper中 DEFAULT_INTERVAL 为 10000,即 10秒。
     * -2、在执行inAnimation 和 outAnimation 时,如果两者的duration之和 小于等于 我们设置的 interval,则动画能正常展示;否则,动画不能正常展示
     * -3、不能使用动画集合,因为设置动画时只接收 ObjectAnimator及其子类,而 AnimatorSet 是其叔叔
     * -4、如果是从xml中设置动画的话,初始化view的时候,第一条也会执行一个渐入的动画;如果是通过代码设置,则第一条直接显示在视图中
     */
    private fun setAdapterViewFlipperAnimator() {
        //CnPeng 2018/11/30 4:14 PM 注意,下面这一行是从Java代码转义过来的,在Java环境下,最后一个参数我们会手动构造一个float[] ,但在kotlin中会报错
        // val animator = ObjectAnimator.ofFloat(adapterViewFlipper, "translationY", arrayOf(100f, 0f))

        //CnPeng 2018/11/30 4:15 PM 最后一个参数是可变数组,我们不需要构造array,直接写array的值即可。
        val inAnimator = ObjectAnimator.ofFloat(adapterViewFlipper, "translationY", 100f, 0f)
        inAnimator.duration = 1500

        //CnPeng 2018/11/30 4:20 PM 此处不需要调用start。viewFlipper在展示child的时候会主动触发动画。如果调用了,执行动画的将是flipper本身
        //        inAnimator.start()

        val outAnimator = ObjectAnimator.ofFloat(adapterViewFlipper, "translationY", 0f, -100f)
        outAnimator.duration = 1500
        //        outAnimator.start()

        adapterViewFlipper.inAnimation = inAnimator
        adapterViewFlipper.outAnimation = outAnimator
    }

    /**
     * CnPeng 2018/11/30 4:31 PM
     * 功用:
     * 说明:
     * -1、在AdapterViewFlipper中 DEFAULT_INTERVAL 为 10000,即 10秒。
     * -2、在执行inAnimation 和 outAnimation 时,如果两者的duration之和 小于等于 我们设置的 interval,则动画能正常展示;否则,动画不能正常展示
     * -3、不能使用动画集合,因为设置动画时只接收 ObjectAnimator及其子类,而 AnimatorSet 是其叔叔
     * -4、如果是从xml中设置动画的话,初始化view的时候,第一条也会执行一个渐入的动画;如果是通过代码设置,则第一条直接显示在视图中
     */
    private fun setAdapterViewFlipperAnimator2() {

        val inAnimator = AnimatorInflater.loadAnimator(mActivity, R.animator.inanimator_flipper)
        val outAnimator = AnimatorInflater.loadAnimator(mActivity, R.animator.outanimator_flipper)
        adapterViewFlipper.inAnimation = inAnimator as ObjectAnimator?
        adapterViewFlipper.outAnimation = outAnimator as ObjectAnimator?
    }
}

  • activity_view_flipper.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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"
    android:padding="@dimen/dp10"
    tools:context=".b_work.b03_view_flipper.ViewFlipperActivity">

    <TextView
        android:id="@+id/tv_title1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="5dp"
        android:text="ViewFlipper实现方式"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tv_taobao1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="淘宝头条 | "
        android:textColor="@color/colorAccent"
        android:textStyle="bold"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tv_title1" />

    <ViewFlipper
        android:id="@+id/viewFlipper"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:animateFirstView="false"
        android:autoStart="true"
        android:inAnimation="@anim/inanimation_viewflipper"
        android:outAnimation="@anim/outanimation_viewflipper"
        app:layout_constraintLeft_toRightOf="@id/tv_taobao1"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tv_title1" />

    <TextView
        android:id="@+id/tv_title2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:padding="5dp"
        android:text="AdapterViewFlipper实现方式"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tv_taobao1" />


    <TextView
        android:id="@+id/tv_taobao2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="淘宝头条 | "
        android:textColor="@color/colorAccent"
        android:textStyle="bold"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tv_title2" />

    <!--        android:flipInterval="1500" -->
    <!-- android:inAnimation="@animator/inanimator_flipper"
        android:outAnimation="@animator/outanimator_flipper"-->
    <AdapterViewFlipper
        android:id="@+id/adapterViewFlipper"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:autoStart="true"
        android:flipInterval="3000"
        android:inAnimation="@animator/inanimator_flipper"
        android:outAnimation="@animator/outanimator_flipper"
        app:layout_constraintLeft_toRightOf="@id/tv_taobao2"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tv_title2" />


</androidx.constraintlayout.widget.ConstraintLayout>
  • AdapterViewFlipperAdapter.kt

/**
 * 作者:CnPeng
 * 时间:2018/11/30
 * 功用:AdapterFlipperView使用的适配器
 * 其他:
 */
class AdapterFlipperViewAdapter (strList: MutableList<String>, mActivity: ViewFlipperActivity) : BaseAdapter() {
    var mStrList = strList
    var mContext = mActivity

    override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
        val itemHolder: ItemHolder
        //CnPeng 2018/11/30 3:50 PM 这里略微有点怪,不能直接给 convertView复制,一直提示convertView是常量
        var itemView = convertView
        if (null == itemView) {
            itemView = TextView(mContext)
            itemHolder = ItemHolder()
            itemHolder.textView = itemView;
        } else {
            itemHolder = itemView.tag as ItemHolder
        }

        itemHolder.textView.text = mStrList[position]
        if (0 == position % 2) {
            itemHolder.textView.setTextColor(Color.BLACK)
        } else {
            itemHolder.textView.setTextColor(Color.parseColor("#ff0000"))
        }
        return itemView
    }

    override fun getItem(position: Int): Any {
        return mStrList[position]
    }

    override fun getItemId(position: Int): Long {
        return position.toLong()
    }

    override fun getCount(): Int {
        return mStrList.size
    }

    class ItemHolder {
        lateinit var textView: TextView
    }
}
  • res/anim/inanimation_viewflipper.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="1500"
        android:fromXDelta="0"
        android:fromYDelta="100"
        android:toXDelta="0"
        android:toYDelta="0" />

    <alpha
        android:duration="1000"
        android:fromAlpha="0"
        android:toAlpha="1" />

</set>
  • res/anim/outanimation_viewflipper.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="1500"
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:toXDelta="0"
        android:toYDelta="-100" />

    <alpha
        android:duration="1000"
        android:fromAlpha="1"
        android:toAlpha="0" />
</set>
  • res/animator/inanimator_flipper.xml
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1500"
    android:propertyName="translationY"
    android:valueFrom="100"
    android:valueTo="0">

</objectAnimator>
  • res/animator/outanimator_flipper.xml
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1500"
    android:propertyName="translationY"
    android:valueFrom="0"
    android:valueTo="-100">

</objectAnimator>

4、附录

    原文作者:CnPeng
    原文地址: https://www.jianshu.com/p/407d92907681
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞