• 首页
  • 博客
  • 学院
  • 下载
  • GitChat
  • TinyMind
  • 论坛
  • 问答
  • 商城
  • VIP
  • 活动
  • 招聘
  • ITeye
  • CSTO
  • 写博客
  • 发Chat

newhope1106的博客

RSS订阅

自实现仿淘宝头条消息滚动的ViewFlipper

2018年05月25日 10:21:14

阅读数:149

最近需要实现一个需求,在列表中,每个item支持显示仿淘宝的上下消息滚动的功能,最开始的考虑是直接使用android自带的ViewFlipper,添加自定的动画来完成,在测试的时候,发现如果每个item的消息太长的时候,列表滑动起来会非常卡,经过分析应该是每个item的消息列表加入到ViewFlipper的时候,都会创建一个新的View,并加入到布局中,从而在滑动的时候,该操作执行太频繁,导致卡顿。因此考虑自己实现一个ViewFlipper,最多通过两个View之间轮换即可,同时对View进行复用,不需要创建那么多的View。

效果图

github:https://github.com/newhope1106/ViewFlipper

一、分析问题关键点

1.每个item的消息上下滚动的效果?其本质是两个控件通过动画上下滚动,来实现轮播,最少需要两个控件,考虑到性能问题,可以对控件进行复用。

2.消息控件的布局未知,动画持续时间和消息滚动间隔未知?可以通过适配器提供消息控件,并且在适配器中用户自己设置消息值,而自实现的ViewFlipper只需要考虑自己的职责,也就是消息滚动效果即可。

二、实现

1.控件实现代码

public class TextViewFlipper extends FrameLayout {
    /**
     * 只保留2个控件,进行动画轮播
     * */
    private View mView1;
    private View mView2;

    private int mIndex = 0;

    private boolean mStarted;
    private boolean mRunning;

    private ValueAnimator mAnimator;

    private BaseFlipperAdapter mAdapter;

    private int mFlipInterval = BaseFlipperAdapter.DEFAULT_FLIP_INTERVAL;

    private int mAnimDuration = BaseFlipperAdapter.DEFAULT_DURATION;

    private final Runnable mFlipRunnable = new Runnable() {
        @Override
        public void run() {
            if (mRunning) {
                showNext();
                postDelayed(mFlipRunnable, mFlipInterval);
            }
        }
    };

    public TextViewFlipper(Context context) {
        this(context, null);
    }

    public TextViewFlipper(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    /**
     * 设置适配器,并且开始动画
     * */
    public void setFlipperAdapter(BaseFlipperAdapter adapter){
        mAdapter = adapter;

        if(adapter != null){
            initData();
        } else {
            stopFlipping();
        }
    }

    /**
     * 获取适配器
     * */
    public BaseFlipperAdapter getAdapter() {
        return mAdapter;
    }

    private void initData(){
        mFlipInterval = mAdapter.getFlipInterval();
        mAnimDuration = mAdapter.getAnimDuration();

        int count = mAdapter.getCount();

        if(count >= 1){
            mView1 = mAdapter.getView(mView1, 0);
        }

        if(count > 1){
            mView2 = mAdapter.getView(mView2, 1);
        }

        reset();

        if(count >= 1 && mView1 != null){
            mView1.setVisibility(VISIBLE);
        }
    }

    /**
     * 重置所有状态
     * */
    private void reset(){
        mStarted = false;
        mRunning = false;
        mIndex = 0;

        removeCallbacks(mFlipRunnable);

        if(mAnimator != null){
            mAnimator.cancel();
        }

        removeAllViews();

        if(mView1 != null){
            mView1.setVisibility(GONE);
            mView1.setTranslationY(0);
            mView1.setAlpha(1.0f);
            addView(mView1);
        }

        if(mView2 != null){
            mView2.setVisibility(GONE);
            mView2.setTranslationY(0);
            mView2.setAlpha(1.0f);
            addView(mView2);
        }
    }

    /**
     * 开始轮播
     * */
    public void startFlipping() {
        if(mAdapter==null || mAdapter.getCount() == 0){
            return;
        }

        if(mAdapter.getCount() == 1 && !mAdapter.startWhenOnlyOne()){
            return;
        }
        mStarted = true;
        updateRunning();
    }

    /**
     * 结束轮播
     * */
    public void stopFlipping() {
        mStarted = false;
        updateRunning();
    }

    private void updateRunning() {
        boolean running = mStarted;
        if (running != mRunning) {
            if (running) {
                showOnly();
                postDelayed(mFlipRunnable, mFlipInterval);
            } else {
                removeCallbacks(mFlipRunnable);
                if(mAnimator != null){
                    mAnimator.cancel();
                }
            }
            mRunning = running;
        }
    }

    /**
     * 开始动画
     * */
    private void showOnly() {
        if(mAnimator == null){
            mAnimator = ValueAnimator.ofFloat(0f, 1.0f);
            mAnimator.setDuration(mAnimDuration);
            mAnimator.setRepeatCount(0);
            mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator valueAnimator) {
                    float value = (float)valueAnimator.getAnimatedValue();

                    int count = getChildCount();
                    int childIndex = mIndex % 2;
                    for (int i = 0; i < count; i++) {
                        final View child = getChildAt(i);
                        if (i == childIndex) {
                            child.setAlpha(value);
                            child.setTranslationY(getHeight()*(1-value));
                            child.setVisibility(View.VISIBLE);
                        } else {
                            if(child.getVisibility() == VISIBLE){
                                child.setAlpha(1 - value);
                                child.setTranslationY(- getHeight()*value);
                            }
                        }
                    }
                }
            });
        }

        mAnimator.start();
    }

    public void showNext() {
        setDisplayedChild(mIndex + 1);
    }

    /**
     * 设置特定View的值
     * */
    private void setDisplayedChild(int whichChild) {
        mIndex = whichChild;
        if (whichChild >= mAdapter.getCount()) {
            mIndex = 0;
        } else if (whichChild < 0) {
            mIndex = mAdapter.getCount() - 1;
        }

        if(mIndex % 2 == 0){
            View tempView = mAdapter.getView(mView1, mIndex);
            //如果属于重新创建的控件,则需要重新添加
            if(tempView.getParent() != this){
                removeView(mView1);
                addView(tempView, 0);
            }

            mView1 = tempView;
        } else {
            View tempView = mAdapter.getView(mView2, mIndex);
            //如果属于重新创建的控件,则需要重新添加
            if(tempView.getParent() != this){
                removeView(mView2);
                addView(tempView, 1);
            }

            mView2 = tempView;
        }

        boolean hasFocus = getFocusedChild() != null;
        // This will clear old focus if we had it
        showOnly();
        if (hasFocus) {
            // Try to retake focus if we had it
            requestFocus(FOCUS_FORWARD);
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        reset();
    }
}

以上通过两个View来实现对View进行复用,而无需重新创建,也无需创建多个View

2.适配器实现代码

public abstract class BaseFlipperAdapter {
    /**
     * 两次item切换的间隔
     * */
    public static final int DEFAULT_FLIP_INTERVAL = 3000;

    /**
     * 获取动画持续时间
     * */
    public static final int DEFAULT_DURATION = 500;

    /**
     * 获取数据个数
     * @return 返回数据个数
     * */
    public abstract int getCount();

    /**
     * 获取当前要显示的控件
     * */
    public abstract View getView(View convertView, int position);

    /**
     * 获取item切换的间隔
     * */
    public int getFlipInterval(){
        return DEFAULT_FLIP_INTERVAL;
    }

    /**
     * 获取动画持续时间
     * */
    public int getAnimDuration(){
        return DEFAULT_DURATION;
    }

    /**
     * 只有一个item的时候,是否开启动画,默认false
     * */
    public boolean startWhenOnlyOne(){
        return false;
    }
}

以上适配器提供需要的接口数据

3.使用

public class DemoActivity extends Activity{

    /**模拟数据*/
    private List<List<String>> mockDatas = new ArrayList<>();

    @Override
    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);

        initMockData();

        setContentView(R.layout.activity_demo);

        ListView listView = findViewById(R.id.list_view);
        listView.setAdapter(new ListAdapter());
    }

    /**
     * 生成模拟数据
     * */
    private void initMockData(){
        for(int i=0; i<100; i++){
            List<String> data = new ArrayList<>();
            data.add(i + "家人给2岁孩子喝这个,孩子智力倒退10岁!!!");
            data.add(i + "iPhone8最感人变化成真,必须买买买买!!!!");
            data.add(i + "简直是白菜价!日本玩家33万甩卖15万张游戏王卡");

            mockDatas.add(data);
        }
    }

    /**
     * 实现适配器
     * */
    private class FlipperAdapter extends BaseFlipperAdapter{
        private List<String> mData;

        public void setData(List<String> data){
            mData = data;
        }

        @Override
        public int getCount() {
            return mData == null ? 0 : mData.size();
        }

        @Override
        public View getView(View convertView, int position) {
            if(convertView == null){
                convertView = View.inflate(DemoActivity.this, R.layout.layout_flipper_item, null);
            }

            TextView textView = (TextView) convertView;
            textView.setText(mData.get(position));

            return convertView;
        }
    }

    private class ListAdapter extends BaseAdapter {

        @Override
        public int getCount() {
            return mockDatas.size();
        }

        @Override
        public Object getItem(int i) {
            return mockDatas.get(i);
        }

        @Override
        public long getItemId(int i) {
            return i;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup viewGroup) {
            if(convertView == null){
                convertView = getLayoutInflater().inflate(R.layout.list_view_item, null);
            }

            TextViewFlipper textViewFlipper = (TextViewFlipper)convertView.findViewById(R.id.text_view_flipper);
            FlipperAdapter adapter = (FlipperAdapter)textViewFlipper.getAdapter();
            if(adapter == null){
                adapter = new FlipperAdapter();
            }
            adapter.setData(mockDatas.get(position));
            textViewFlipper.setFlipperAdapter(adapter);
            textViewFlipper.startFlipping();

            return convertView;
        }
    }
}

以上demo是在listview中实现上下滚动功能,如果只是单个控件实现上下滚动功能也是支持的。

三、改进点

1. 以上没有把动画部分抽取出来,给用户自定义,依赖了特定的业务,并不完善。

2. 使用方面步骤有点多,成本略高,可以再加一层封装,来实现一些领域的业务,对外提供简单的接口

文章标签:  android ViewFlipper 仿淘宝 滚动
个人分类:  控件

Android基础控件——ViewFlipper的使用,仿淘宝头条垂直滚动广告条

ViewFlipper的使用,仿淘宝头条垂直广告条 学习,学习,学以致用 ViewFlipper是安卓自带的控件,很多人可能很少知道这个控件,这个控件很简单,也很好理解,能不能用上实战就看你们的本...

 qq_30379689

2017-01-07 18:53:43

阅读数:6381

仿淘宝首页的淘宝头条View垂直滚动

之前本来是打算做TextView垂直向上滚动的,后来发现一位大神做得很好,https://github.com/sfsheng0322/MarqueeView 孙福生大神,然后自己要用到多个View向...

 dreamlivemeng

2016-07-21 09:43:19

阅读数:6700

海参的功效,北京人不知道海参有这些功效!沈阳盛鼎 · 顶新

Android仿淘宝头条垂直滚动广告条效果

2017年02月07日 27.88MB 下载

RecyclerViewHeader+ViewFlipper仿淘宝头条滚动效果

1、需要导入的包: //RecyclerView compile 'com.android.support:recyclerview-v7:23.1.0' //RecyclerV...

 wapchief

2017-06-14 10:34:39

阅读数:613

安卓仿淘宝头条数据上下自动滚动

其实实现逻辑就是一个自定义的view,在加上指定的动画就可以; 布局文件: ...

 u014388322

2016-09-30 10:59:22

阅读数:2293

Android【垂直滚动广告条】仿淘宝头条1号店京东—垂直滚动广告条

淘宝头条是淘宝App中很经典的一个功能显示,主要用于显示最近的热评新闻,显示主要方式为文字竖直滚动效果,下面简单阐述一下本demo所涉及到的技术点以及功能展示 1.主要用到的控件为Andro...

 Sophie237

2017-02-07 16:17:51

阅读数:2718

“人喝茶三年,茶养人一辈子”已被科学证实!花青堂茶叶 · 顶新

iOS开发之仿淘宝头条

之前总是使用别人封装好的类,封装好的功能,但是从来没有过多的考虑别人是如何想的.但是当自己想去封装一个控件或者类的时候,当自己也要反客为主成为类的设计者的时候,却发现自己很多都想不到,自我感觉是自己平...

 James_JohnSon

2015-03-18 20:31:02

阅读数:3115

仿淘宝首页的淘宝头条垂直滚动

https://github.com/dreamlivemeng/UpMarqueeTextView-master

 qq_26916671

2017-06-27 14:44:29

阅读数:230

Android中仿淘宝头条,自定义控件,向上滚动

1效果图 2项目结构 3代码 自定义滑动控件UpDownTextView package android.zhh.com.myfangtaobao2; import android.a...

 zhaihaohao1

2017-03-15 13:33:50

阅读数:1157

ViewFlipper的使用,仿淘宝头条垂直滚动广告条

2017年01月17日 1.98MB 下载

自定义控件实现(淘宝头条/京东快报)垂直循环滚动栏目

1、通过继承LinearLayout的方式 ①自定义属性 xml version="1.0" encoding="utf-8"?> name="JDAdverView"> ...

 zhaodecang

2016-11-26 01:13:25

阅读数:16343

类似淘宝头条的view滚动第二种实现方式

上一次自己做了两套布局的平移动画来实现类似淘宝头条的滚动效果,后来又在github上找到了孙福大神写的自定义控件 地址:https://github.com/sfsheng0322/MarqueeVi...

 engineer_zh

2017-02-16 17:44:14

阅读数:220

Android仿淘宝头条垂直滚动,垂直走马灯,公告

今天看了淘宝头条的的滚动,感觉用户体验非常好,然后在就github上找到了一个, github:https://github.com/gongwen/MarqueeViewLibrary 效果图如...

 Afanbaby

2017-06-14 18:31:33

阅读数:2168

仿淘宝头条,走马灯上下滚动

2016年01月18日 1.99MB 下载

Android仿淘宝头条滚动广告条 ViewFlipper

2018年03月15日 15.51MB 下载

仿天猫热点,淘宝头条向上自动滚动的textview

仿天猫热点,淘宝头条向上自动滚动的textview

 Jeff169

2016-05-21 11:33:31

阅读数:2185

Android仿淘宝滚动的头条

http://www.jianshu.com/p/73c04d93cc56 1. 先看效果图 marquee_view.gif 感谢鸿洋大神,我是从他的博客上看到...

 qq_26916671

2017-06-15 10:43:26

阅读数:335

仿淘宝首页的淘宝头条垂直滚动,因为循环滚动的是布局,所以很多向上的情况都可以用,可以先收藏起。

https://github.com/dreamlivemeng/UpMarqueeTextView-master

 u014045181

2016-07-21 09:43:06

阅读数:1188

android 实现淘宝消息滚动条

因为最近有项目需求,需要实现一个类似淘宝的消息滚动条,如下图所示。 比如上面图上的这个糯米头条功能,通过Ui automator view 发现糯米是通过一个listview的方式实现的,但是...

 mjn19920902

2016-08-10 10:35:26

阅读数:1842

仿淘宝头条

2016年06月27日 81KB 下载

个人资料

newhope1106

关注

原创
28
粉丝
5
喜欢
7
评论
6
等级:
访问:
3万+
积分:
613
排名:
8万+
勋章:

最新文章

  • 基于事件触发的开源框架EventTrigger
  • Android性能优化总结
  • 再谈Activity启动流程(2)
  • 再谈Activity启动流程(1)
  • java虚拟机知识点简要梳理

个人分类

  • android25篇
  • 控件2篇
  • 网络2篇
  • 框架8篇
  • 架构1篇
  • java5篇

展开

归档

  • 2018年5月1篇
  • 2017年5月1篇
  • 2017年4月3篇
  • 2017年3月6篇
  • 2017年2月5篇
  • 2017年1月5篇
  • 2016年12月9篇
  • 2016年11月3篇

展开

热门文章

  • google的GCM推送使用简介

    阅读量:15765

  • android四大组件启动流程-BroadcastReceiver启动流程(基于android 6.0)

    阅读量:1798

  • Activity启动流程,界面绘制到事件处理的整个流程(基于Android6.0源码)(2)

    阅读量:1296

  • 上拉加载更多,下拉刷新的弹性ListView的实现

    阅读量:1295

  • OkHttp 3.x框架简要分析

    阅读量:920

最新评论

  • google的GCM推送使用简介

    newhope1106:[reply]u011429034[/reply] GCM是系统里面集成的推送服务,不是我们一般用...

  • google的GCM推送使用简介

    gaga_king:[reply]u011429034[/reply] 这个数系统级的推送,像小米推送在miui系统上...

  • google的GCM推送使用简介

    u011429034:你确信,如果程序的进程被kill掉了,还能在接收到推送的么?

  • android四大组件启动流程 -...

    newhope1106:以上ActivityManagerService.publishService会调用到Activi...

  • Activity启动流程,界面绘制...

    newhope1106:以上需要注意一定,子View可以通过调用父View的requestDisallowIntercep...

联系我们

请扫描二维码联系客服

webmaster@csdn.net

400-660-0108

QQ客服 客服论坛

关于招聘广告服务  百度

©1999-2018 CSDN版权所有

京ICP证09002463号

经营性网站备案信息

网络110报警服务

中国互联网举报中心

北京互联网违法和不良信息举报中心

  • 0

  • 收藏
  • 评论
  • 微信
  • 微博
  • QQ
关闭

滚动的ViewFlipper——滚动的大标题相关推荐

  1. 记录一次iOS11大标题不滚动的问题

    iOS11出来之后,有了大标题这种UI,设计师也跟上潮流.所以项目中使用到了大标题,设置很简单只需要以下两行代码: if #available(iOS 11.0, *) {self.navigatio ...

  2. html 字幕飘动效果,html 滚动字幕 制作滚动字幕效果 参数

    制作滚动字幕效果:marquee标签 如下:<MARQUEE direction=up height=146 οnmοuseοut=start() οnmοuseοver=stop() scro ...

  3. Appium使用swipe定位滚动列表和滚动屏幕元素

    app自动化测试时,会碰到下图这样的元素,点击商品类型,弹出的选择框是滚动列表 使用uiautomatorviewer截图:滚动框为一个整体,无法定位到商品类型中的每一个元素,所以使用id,name无 ...

  4. 移动端滚动穿透与滚动溢出解决方案

    滚动穿透 滚动穿透.gif 问题描述 在移动端 WEB 开发的时候(小程序也雷同),如上录屏所示,如果页面超过一屏高度出现滚动条时,在 fixed 定位的弹窗遮罩层上进行滑动,它下面的内容也会跟着一起 ...

  5. 鼠标滚动时判断向下滚动还是向上滚动

    有时候需要用到,判断页面是向上还是向下滚动了,兼容比较低版本的浏览器如IE6.7等. 原理:拿当前的scrollTop和之前的scrollTop对比  如果变大了,表示向下滚动(scrollTop值变 ...

  6. msclass 文字滚动_MSClass 图片/文字不间断滚动\间歇滚动\翻屏滚动类

    /*MSClass (Class Of Marquee Scroll通用不间断滚动JS封装类) Ver 1.65*\ 制作时间:2006-08-29 (Ver 0.5) 发布时间:2006-08-31 ...

  7. php 滚动彩色文字,滚动文字特效大全 -★用彩色生成器加文字,图片加文字,带背景的链接滚动文字...

    霍斯 的 滚动文字特效大全http://www.sudu8.cn/sudu8_article/87/471.html 带背景的链接滚动文字http://font.itbulo.com/ 代码如下: 对 ...

  8. vue 轮播组件 vue-seamless-scroll简单用法/上下左右无缝滚动,单步滚动,以及支持水平方向的手动切换功能

    一.vue-seamless-scroll是什么? vue-seamless-scroll是一个基于Vue.js的简单无缝滚动组件, 基于requestAnimationFrame实现,配置多满足多样 ...

  9. pandas使用groupby函数计算dataframe数据中每个分组的滚动统计值(rolling statistics)的语法:例如分组的N天滚动平均值、滚动中位数、滚动最大最小值、滚动加和等

    pandas使用groupby函数计算dataframe数据中每个分组的滚动统计值(rolling statistics)的语法:例如分组的N天滚动平均值.滚动中位数.滚动最大最小值.滚动加和等 目录

最新文章

  1. 菜鸟学Java(十九)——WEB项目测试好帮手,Maven+Jetty
  2. 【深度学习】医学图像分割的集成与后处理
  3. 《编译与反编译技术实战》——第2章编译器实践概述
  4. 三菱plc编程实例3000_三菱PLC十字路口的红绿灯编程实例
  5. 改mysql修改界定符_dbvisualizer参数设置
  6. centos linux编译c,紧急提醒!Linux是如何编译C语言程序文件的?CentOS 8的gcc使用方法介绍...
  7. 存储器的保护(一)——《x86汇编语言:从实模式到保护模式》读书笔记18
  8. 面向全球用户的Teams app之Culture数字篇
  9. 使用Servlet上传多张图片——访问提示
  10. pmml_再访PMML
  11. 自定义文件系统下的磁盘访问次数计算
  12. kosbie的python课程视频_Python视频教程
  13. 群晖套件中心没有docker_非Docker方法安装qBittorrent,舒心玩转PT
  14. 打开fiddler部分网页打不开
  15. vulhub nginx insecure-configuration
  16. 8086微处理器的寄存器
  17. Spark的任务调度
  18. 软件测试工资一般是多少?
  19. microsoft WINDOWS 系统错误代码
  20. 2021-11-16日完成任务:中医体质辨识设计与实现

热门文章

  1. uniapp 打包安卓定位失败 高德key错误
  2. Fluent案例01 欧拉多相流——水箱注水
  3. html桌面天干地支,天干地支
  4. 服务器重装系统后风扇声音大,服务器,你安静一点好不好?!噪音大令人头疼,一招便可解决!...
  5. 烟雨黑帽seo程序演示 批量建站养站提升权重建站程序
  6. 数据质量在自动化测试中的领导力与策略
  7. 观点 | 回顾以太坊近期及中期扩容路线图,展望 rollup 作为中心的以太坊路线图...
  8. mybatis判断字符串为空或者空字符串
  9. 生物信息--连锁不平衡(Linkage Disequilibrium)
  10. PCLint选项详解