VerticalBannerView仿淘宝头条实现垂直轮播广告

VerticalBannerView是一个仿淘宝APP首页轮播头条的自定义控件。

特性:

1.可自由定义展示的内容。
2.使用方式类似ListView/RecyclerView。
3.可为当前显示的内容添加各种事件,比如点击打开某个页面等。

VerticalBannerView开源项目地址

运行效果图:

一、项目使用

(1).添加项目依赖。

dependencies {
  compile 'com.github.Rowandjj:VerticalBannerView:1.0'
}

(2).添加布局。

<LinearLayout
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:gravity="center_vertical"
  android:orientation="horizontal">

  <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:paddingLeft="5dp"
    android:text="淘宝头条"
    android:textStyle="bold"/>

  <View
    android:layout_width="1dp"
    android:layout_height="40dp"
    android:layout_marginLeft="5dp"
    android:layout_marginRight="5dp"
    android:background="#cccccc"/>

  <com.taobao.library.VerticalBannerView
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/banner"
    android:layout_width="wrap_content"
    android:layout_height="36dp"
    app:animDuration="900"
    app:gap="2000"/>
</LinearLayout>

(3).实现Adapter。

public class SampleAdapter extends BaseBannerAdapter<Model> {
  private List<Model> mDatas;

  public SampleAdapter01(List<Model> datas) {
    super(datas);
  }

  @Override
  public View getView(VerticalBannerView parent) {
    return LayoutInflater.from(parent.getContext()).inflate(R.layout.your_item,null);
  }

  @Override
  public void setItem(final View view, final Model data) {
    TextView textView = (TextView) view.findViewById(R.id.text);
    textView.setText(data.title);
    // 你可以增加点击事件
    view.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        // TODO handle click event
      }
    });
  }
}

(4).为VerticalBannerView设置Adapter,并启动动画。

List<Model> datas = new ArrayList<>();
datas.add(new Model01("Note7发布了"));
datas.add(new Model01("Note7被召回了"));
SampleAdapter adapter = new SampleAdapter(datas);
VerticalBannerView banner = (VerticalBannerView) findViewById(R.id.banner);
banner.setAdapter(adapter);
banner.start();

二、源码分析

实现原理:

VerticalBannerView本质上是一个垂直的LinearLayout。定义一个Adapter类,向LinearLayout提供子View。初始状态下往LinearLayout中添加两个子View,子View的高度同LinearLayout的高度一致,这样一来只有第1个子View显示出来,第2个子View在底部不显示。然后使用属性动画ObjectAnimator同时修改两个子View的translationY属性,动画执行过程中translationY从默认值0渐变到负的LinearLayout的高度,显示出来的效果就是第1个子View逐渐向上退出,第2个子View从底部向上逐渐显示。动画执行完毕后,移除第1个子View,这样第2个子View的索引变成0,并完全显示出来占据LinearLayout的高度。再将已经移除的第1个子View,添加到索引为1的位置,此时该子View超出父视图之外完全不显示。一轮动画执行完毕,再调用postDelay()方法重复上述动画,一直循环下去。

下面进入代码部分,主要是两个类BaseBannerAdapter和VerticalBannerView。

(1).BaseBannerAdapter类

BaseBannerAdapter类负责为广告栏提供数据。我们在使用时,需要写一个Adapter类继承BaseBannerAdapter,实现getView()和setItem()方法。在getView()方法中,我们需要把要添加到广告栏中的item view创建出来并返回,setItem()方法则负责为创建的item view绑定数据。

public abstract class BaseBannerAdapter<T> {
  private List<T> mDatas;
  private OnDataChangedListener mOnDataChangedListener;

  public BaseBannerAdapter(List<T> datas) {
    mDatas = datas;
    if (datas == null || datas.isEmpty()) {
      throw new RuntimeException("nothing to show");
    }
  }

  public BaseBannerAdapter(T[] datas) {
    mDatas = new ArrayList<>(Arrays.asList(datas));
  }

  // 设置banner填充的数据
  public void setData(List<T> datas) {
    this.mDatas = datas;
    notifyDataChanged();
  }

  void setOnDataChangedListener(OnDataChangedListener listener) {
    mOnDataChangedListener = listener;
  }

  // 获取banner总数
  public int getCount() {
    return mDatas == null ? 0 : mDatas.size();
  }

  // 通知数据改变
  void notifyDataChanged() {
    mOnDataChangedListener.onChanged();
  }

  // 获取数据
  public T getItem(int position) {
    return mDatas.get(position);
  }

  // 设置banner的ItemView
  public abstract View getView(VerticalBannerView parent);

  // 设置banner的数据
  public abstract void setItem(View view, T data);

  // 数据变化的监听
  interface OnDataChangedListener {
    void onChanged();
  }
}

(2).VerticalBannerView类

VerticalBannerView类继承自LinearLayout,并在构造方法中设定方向为垂直。同时VerticalBannerView类实现了OnDataChangedListener接口,实现onChanged()方法,这样当改变数据后调用BaseBannerAdapter的notifyDataChanged()时,VerticalBannerView的onChanged()方法被回调,执行setupAdapter()重新初始化数据。

public class VerticalBannerView extends LinearLayout implements BaseBannerAdapter.OnDataChangedListener {
  public VerticalBannerView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init(context, attrs, defStyleAttr);
  }

  private void init(Context context, AttributeSet attrs, int defStyleAttr) {
    setOrientation(VERTICAL);
 ......
  }

  ......

  @Override
  public void onChanged() {
    setupAdapter();
  }

  ......
}

为VerticalBannerView添加item数据,需要调用setAdapter()方法,关键在于其中执行的setupAdapter()方法。

public void setAdapter(BaseBannerAdapter adapter) {
  if (adapter == null) {
    throw new RuntimeException("adapter must not be null");
  }
  if (mAdapter != null) {
    throw new RuntimeException("you have already set an Adapter");
  }
  this.mAdapter = adapter;
  mAdapter.setOnDataChangedListener(this);
  setupAdapter();
}

在setupAdapter()方法中,先移除所有的子View,然后调用Adapter的getView()方法创建两个子View,分别赋值给成员变量mFirstView和mSecondView,并为这两个子View绑定数据,最后再调用addView()添加进来。

// 初始化Child View
private void setupAdapter() {
  // 先移除所有的子View
  removeAllViews();

  if (mAdapter.getCount() == 1) {
    mFirstView = mAdapter.getView(this);
    mAdapter.setItem(mFirstView, mAdapter.getItem(0));
    addView(mFirstView);
  } else {
    // 调用Adapter的getView()方法,创建两个子View,分别赋值给mFirstView和mSecondView
    mFirstView = mAdapter.getView(this);
    mSecondView = mAdapter.getView(this);
    // 使用索引0和1的数据,为mFirstView和mSecondView设置数据
    mAdapter.setItem(mFirstView, mAdapter.getItem(0));
    mAdapter.setItem(mSecondView, mAdapter.getItem(1));
    // 将mFirstView和mSecondView添加到当前View
    addView(mFirstView);
    addView(mSecondView);

    mPosition = 1;
    isStarted = false;
  }
  setBackgroundDrawable(mFirstView.getBackground());
}

为了实现子View之间的切换,需要把上面添加进来的子View的高度修改为与VerticalBannerView高度一致。这个操作在onMeasure()方法中完成。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  // 成员变量mBannerHeight有一个默认值
  if (LayoutParams.WRAP_CONTENT == getLayoutParams().height) {
    // 如果当前View的高度设置为wrap_content,使用默认值
    getLayoutParams().height = (int) mBannerHeight;
  } else {
    // 如果当前View设定了固定高度,则使用设定的高度
    mBannerHeight = getHeight();
  }
  // 修改mFirstView和mSecondView的高度为其父视图的高度
  if (mFirstView != null) {
    mFirstView.getLayoutParams().height = (int) mBannerHeight;
  }
  if (mSecondView != null) {
    mSecondView.getLayoutParams().height = (int) mBannerHeight;
  }
}

上面的准备工作完成后,就可以进入动画的执行了。VerticalBannerView通过调用start()方法启动切换动画。在start()方法中,调用postDelayed()执行AnimRunnable任务,在AnimRunnable的run()方法中,先调用performSwitch(),然后再次调用postDelayed()使AnimRunnable任务一直循环执行下去。两个子View之间的切换工作由performSwitch()负责执行。

public void start() {
  if (mAdapter == null) {
    throw new RuntimeException("you must call setAdapter() before start");
  }

  if (!isStarted && mAdapter.getCount() > 1) {
    isStarted = true;
    postDelayed(mRunnable, mGap);
  }
}

private AnimRunnable mRunnable = new AnimRunnable();

private class AnimRunnable implements Runnable {
  @Override
  public void run() {
    performSwitch();
    // 调用postDelayed()延时再执行,一直循环下去
    postDelayed(this, mGap);
  }
}

下面再进入performSwitch()方法,该方法是Item View之间产生切换效果的核心。

// 执行切换
private void performSwitch() {
  // 动画在执行过程中,View的translationY属性从0一直减小到-mBannerHeight
  // 因为translationY属性一直是负值,所以View向上移动
  ObjectAnimator animator1 = ObjectAnimator.ofFloat(mFirstView, "translationY", -mBannerHeight);
  ObjectAnimator animator2 = ObjectAnimator.ofFloat(mSecondView, "translationY", -mBannerHeight);
  AnimatorSet set = new AnimatorSet();
  set.playTogether(animator1, animator2);
  set.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
      // 动画执行完成,把mFirstView和mSecondView的translationY恢复到默认状态
      mFirstView.setTranslationY(0);
      mSecondView.setTranslationY(0);
      // 使用下一条数据,设置到第一个子View
      View removedView = getChildAt(0);
      mPosition++;
      mAdapter.setItem(removedView, mAdapter.getItem(mPosition % mAdapter.getCount()));
      // 移除第一个子View,此时当前LinearLayout的childCount==1
      removeView(removedView);
      // 移除的View,再添加到第2个位置
      addView(removedView, 1);
    }
  });
  set.setDuration(mAnimDuration);
  set.start();
}

在performSwitch()方法中,使用属性动画ObjectAnimator同时修改两个mFirstView和mSecondView的translationY属性,动画执行中translationY从默认值0一直减小到-mBannerHeight,在这个过程中mFirstView逐渐向上退出,mSecondView从底部逐渐显现。动画执行完毕后,移除mFirstView,mSecondView变成第1个子View并完全显示出来填充父视图的高度。再将移除的mFirstView添加到第2个位置,此时mFirstView未显示出来。由于performSwitch()方法一直循环被调用,mFirstView和mSecondView就这样一直循环切换。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Flutter banner_view 轮播图的使用及实现代码

    1.前言 实现轮播图,效果如下: 2.实现 将采用 banner_view 实现:资源库地址 2.1.yaml 引入依赖 在 pubspec.yaml 声明需要引用的库,执行命令 flutter packages get 进行拉取即可使用. banner_view: "^1.1.2" 2.2.代码中引入依赖 在资源库地址下方,作者提供了 banner_view 的几种展示方式. import 'package:flutter/material.dart'; import 'packag

  • Android ViewPager实现无限循环轮播广告位Banner效果

    现在一些app通常会在头部放一个广告位,底部放置一行小圆圈指示器,指示广告位当前的页码,轮播展示一些图片,这些图片来自于网络.这个广告位banner是典型的android ViewPager实现,但是如果自己实现这样的ViewPager,要解决一系列琐碎的问题,比如: (1)这个广告位ViewPager要支持无限循环轮播,例如,有3张图片,A,B,C,当用户滑到最后C时候再滑就要滑到A,反之亦然. (2)ViewPager要实现自动播放,比如每个若干秒如2秒,自动切换播放到下一张图片. (3)通

  • ViewPager打造轮播图Banner/引导页Guide

    前言 去年7月时,在Github发布了一个开源的Banner库,虽然Star不多,但还是有少部分人使用. Banner效果: Github链接地址:https://github.com/Allure0/LMBanners 昨天,有使用此库的同学提出需求,想在引导页的时候用这个库并且最后一页有进入按钮如何实现,为满足他的需求,也方便更多开发者是快速实现.进行了简单的扩展支持Guide模式的使用. Guide效果图: OK,效果如图所以,咱们此库满足了既可在Banner上使用也可以快速在第一次安装应

  • 手把手教你用ViewPager自定义实现Banner轮播

    欢迎大家关注Android开源网络框架NoHttp:https://github.com/yanzhenjie/NoHttp 我们在实际开发中,很多App都会在做一个广告轮播器(可能是图片,可能是其他View),很多同学都是使用别人封装好的或者直接使用ViewPager自己来改,但是有人可能并不理解里面的原理,或者有人遇到了手势滑动冲突.我们今天就用150行代码实现一自定义的广告轮播器并不干扰原来View滑动事件. 本例代码源码及Demo传送门 效果演示 需求分析.解决方案 轮播器最重要的几个特

  • VerticalBannerView仿淘宝头条实现垂直轮播广告

    VerticalBannerView是一个仿淘宝APP首页轮播头条的自定义控件. 特性: 1.可自由定义展示的内容. 2.使用方式类似ListView/RecyclerView. 3.可为当前显示的内容添加各种事件,比如点击打开某个页面等. VerticalBannerView开源项目地址 运行效果图: 一.项目使用 (1).添加项目依赖. dependencies { compile 'com.github.Rowandjj:VerticalBannerView:1.0' } (2).添加布局

  • Android控件ViewFlipper仿淘宝头条垂直滚动广告条

    ViewFlipper的使用,仿淘宝头条垂直滚动广告条,供大家参考,具体内容如下 学习,学习,学以致用 ViewFlipper是安卓自带的控件,很多人可能很少知道这个控件,这个控件很简单,也很好理解,能不能用上实战就看你们的本事了.下面是淘宝头条广告的原效果 下面是我们今天要实现的效果,图片是Gif,运行效果是很流畅的,由于这个图片反应有点慢,会浪费大家点时间,所以我把它调快了,大家可以掏出手机打开淘宝看,一模一样的 从源码可以看出,其实ViewFlipper间接的继承了FrameLayout,

  • Android仿淘宝头条向上滚动广告条ViewFlipper

    所谓前人栽树,后人乘凉,在此感谢博主的贡献. 参考博文: 仿淘宝首页的淘宝头条View垂直滚动 欢迎关注我的微信公众号 不只是原创技术文章,更多的是对生活的思考总结 我在博主的基础上做了如下工作: 修复了滚动条第二条点击事件无法触发这个bug: 充分简化了自定义ViewFlipper类的代码: 添加了直接使用ViewFlipper控件实现同样效果: 先上效果图: 这里使用了一个比较少用的控件:ViewFlipper 学习一个未知的东西,第一步就是要搞懂what:学的这个东西是什么以及能够实现什么

  • Android仿京东淘宝自动无限循环轮播控件思路详解

    在App的开发中,很多的时候都需要实现类似京东淘宝一样的自动无限轮播的广告栏,所以就自己写了一个,下面是我自定义控件的思路和过程. 一.自定义控件属性 新建自定义控件SliderLayout继承于RelativeLayout,首先要考虑的就是自定义的控件需要扩展那些属性,把这些属性列出来.在这里是要实现类似于京东淘宝的无限轮播广告栏,那么首先想到的就是轮播的时长.轮播指示器的样式等等.我在这里列举了一些并且结合到了代码中. 1.扩展属性 (1)是否开启自动轮播的功能. (2)指示器的图形样式,一

  • Android仿淘宝头条基于TextView实现上下滚动通知效果

    最近有个项目需要实现通知栏的上下滚动效果,仿淘宝头条的那种. 我从网上看了一些代码,把完整的效果做了出来.如图所示: 具体代码片段如下: 1.在res文件夹下新建anmin文件夹,在这个文件夹里创建两个文件 (1).anim_marquee_in.xml进入时动画 <?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/ap

  • Android仿淘宝首页头条View垂直滚动效果

    之前本来是打算做TextView垂直向上滚动的,后来发现一位大神做得很好,https://github.com/sfsheng0322/MarqueeView 孙福生大神,然后自己要用到多个View向上滚动,也就是类似淘宝首页头条的那种滚动,所以就按照那个思路想了系啊,可以把View拿来滚动,这样可以自己随意的修改View里面的内容,还比较简单一些.所以这个整个思路就是把View就行循环滚动. 看一下循环滚动View的内容咋写的吧,非常简单. package com.dreamlive.upma

  • 微信小程序仿淘宝热搜词在搜索框中轮播功能

    摘要 逛淘宝的时候,发现淘宝搜索框中一直在垂直方向上轮播热搜提示词,觉得这是个不错的设计,除了能让空间更充分使用,也能让页面更有动感,最重要的是能够增加搜索框的使用频率.就在小程序中试着实现实现. 效果 体验 实现思路 思路比较简单,主要是两点, 1:input处于热搜提示词上层,用z-index实现 2:热搜词轮播用swiper实现,方向为vertical 3:在input聚焦时获取swiper当前值,设置为placeholder 4:将swiper隐藏 代码 已封装成组件 组件代码: wxs

  • JS实现的仿东京商城菜单、仿Win右键菜单及仿淘宝TAB特效合集

    本文实例讲述了JS实现的仿东京商城菜单.仿Win右键菜单及仿淘宝TAB特效.分享给大家供大家参考.具体如下: 这是一个非常好的实用菜单整合特效,有多级下拉菜单.仿东京商城的拉出式菜单.仿Windows的右键菜单,仿淘宝的标签Tab菜单,每一个都是精彩,代码中附有丰富的注释,便于你的学习和修改. 运行效果截图如下: 在线演示地址如下: http://demo.jb51.net/js/2015/js-f-jd-taobao-win-rbutton-tab-demo/ 具体代码如下: <!DOCTYP

  • vue mint-ui 实现省市区街道4级联动示例(仿淘宝京东收货地址4级联动)

    本文介绍了vue mint-ui 实现省市区街道4级联动示例(仿淘宝京东收货地址4级联动) 先去下载一个"省份.城市.区县.乡镇" 四级联动数据,然后 引入 import { Picker } from 'mint-ui'; //前提是npm install mint-ui -S Vue.component(Picker.name, Picker); 组件使用 <mt-picker :slots="addressSlots" class="picke

  • jQuery仿淘宝网产品品牌隐藏与显示效果

    本文实例讲述了jQuery仿淘宝网产品品牌隐藏与显示效果.分享给大家供大家参考.具体如下: 这里演示jQuery实现产品品牌隐藏与显示效果,仿淘宝网商品列表的品牌显示与折叠功能,很实用的代码,看了就知道了. 运行效果截图如下: 在线演示地址如下: http://demo.jb51.net/js/2015/jquery-f-taobao-product-hidden-show-codes/ 具体代码如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.

随机推荐