从源码角度上探索AdapterViewFlipper怎么实现广告栏的垂直自动滚动
一,从源码上认识AdapterViewFlipper
我不知道大家有没有跟我一样,看到别人的app一些比较好的交互时,总是好奇别人是怎么实现的,如果是换做自己,我哦该怎么实现。最近在做一个横向轮播的视频展示需求,我们知道viewpager是可以很容易实现横向滚动的,那么很多app上的一些广告栏是垂直滚动的,比如京东,淘宝,一些招聘的app都有。那么别人是在怎么实现的呢。方式有很多中,如果只是简单的文本展示,那么TextView就完全可以搞定,这里有篇文章把TextView的用法都简单介绍 android高仿京东快报(垂直循环滚动新闻栏) 当然再复杂的都可以用自定义view来实现,但是写app的,我相信很多人跟我一样,不太喜欢写自定义控件。这时AdapterViewFlipper就闪亮登场了。我个人比较喜欢从源码上来研究一个控件的用法,下面是我自己的一些学习积累,希望对跟我之前一样不是很懂AdapterViewFlipper的人有一些帮助。
那么AdapterViewFlipper到底是神马呢?官方是这么介绍它的:
/*** Simple {@link ViewAnimator} that will animate between two or more views* that have been added to it. Only one child is shown at a time. If* requested, can automatically flip between each child at a regular interval.* * @attr ref android.R.styleable#AdapterViewFlipper_flipInterval* @attr ref android.R.styleable#AdapterViewFlipper_autoStart*/
上面的英文都比较简单啦,大家都能看懂。其实AdapterViewFlipper继承了AdapterViewAnimator,它会显示一个View组件,可以通过showPrevious()和showNext()方法控制组件显示上一个、下一个组件。从上面可以看到AdapterViewFlipper有两个比较重要的属性。
* flipInterval :这个属性是动画持续的时间,也就是设置自动播放的时间间隔
* autoStart :这个属性从单词上都可以知道是啥意思,即设置显示该组件是否是自动播放
public class AdapterViewFlipper extends AdapterViewAnimator {private static final int DEFAULT_INTERVAL = 10000;private int mFlipInterval = DEFAULT_INTERVAL;private boolean mAutoStart = false;public AdapterViewFlipper(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes);final TypedArray a = context.obtainStyledAttributes(attrs,com.android.internal.R.styleable.AdapterViewFlipper, defStyleAttr, defStyleRes);mFlipInterval = a.getInt(com.android.internal.R.styleable.AdapterViewFlipper_flipInterval, DEFAULT_INTERVAL);mAutoStart = a.getBoolean(com.android.internal.R.styleable.AdapterViewFlipper_autoStart, false);// A view flipper should cycle through the viewsmLoopViews = true;a.recycle();}
看上面的代码可以知道,flipInterval默认是10000,autoStart默认是false的,所以要想自动播放则需要把这个属性设为true。
那么AdapterViewFlipper是怎么来自动播放的呢,其实呢,View万变不离其中,View的“自动”一般都离不开View.java提供的这个方法:
/*** <p>Causes the Runnable to be added to the message queue, to be run* after the specified amount of time elapses.* The runnable will be run on the user interface thread.</p>** @param action The Runnable that will be executed.* @param delayMillis The delay (in milliseconds) until the Runnable* will be executed.** @return true if the Runnable was successfully placed in to the* message queue. Returns false on failure, usually because the* looper processing the message queue is exiting. Note that a* result of true does not mean the Runnable will be processed --* if the looper is quit before the delivery time of the message* occurs then the message will be dropped.** @see #post* @see #removeCallbacks*/public boolean postDelayed(Runnable action, long delayMillis) {final AttachInfo attachInfo = mAttachInfo;if (attachInfo != null) {return attachInfo.mHandler.postDelayed(action, delayMillis);}// Postpone the runnable until we know on which thread it needs to run.// Assume that the runnable will be successfully placed after attach.getRunQueue().postDelayed(action, delayMillis);return true;}
postDelayed(Runnable action, long delayMillis)这个方法是什么鬼呢?其实这是Android 控件View自带的定时器 。这个定时器的用法,我们在开发中经常用的:例如 有一个需求是这样的,点击加关注按钮后,执行关注操作,成功后按钮文字变为“已关注”,保持3秒,三秒后按钮文字便问“取消关注”,点击后执行取消关注的操作。对于这样子的需求用postDelayed做是最方便的。
mButton.setText("已关注");
/*3秒内设置不可点击*/
mButton.setClickable(false);
mButton.postDelayed(new Runnable() {@Overridepublic void run() {/*3秒后可以点击*/mButton.setClickable(true);mButton.setText("取消关注");}
},3*1000);
回到刚刚的话题,AdapterViewFlipper是怎么自动播放的,我跟踪到源码里面发现:
@Overrideprotected void onAttachedToWindow() {super.onAttachedToWindow();// Listen for broadcasts related to user-presencefinal IntentFilter filter = new IntentFilter();filter.addAction(Intent.ACTION_SCREEN_OFF);filter.addAction(Intent.ACTION_USER_PRESENT);// OK, this is gross but needed. This class is supported by the// remote views machanism and as a part of that the remote views// can be inflated by a context for another user without the app// having interact users permission - just for loading resources.// For exmaple, when adding widgets from a user profile to the// home screen. Therefore, we register the receiver as the current// user not the one the context is for.getContext().registerReceiverAsUser(mReceiver, android.os.Process.myUserHandle(),filter, null, getHandler());if (mAutoStart) {// Automatically start when requestedstartFlipping();}}
这个方法我相信对View有一定基础的人都知道是啥。我们先看看官方怎么介绍这个方法:
protected void onAttachedToWindow()This is called when the view is attached to a window. At this point it has a Surface and will start drawing. Note that this function is guaranteed to be called beforeonDraw(android.graphics.Canvas), however it may be called any time before the first onDraw -- including before or afteronMeasure(int, int).
从开发文档中我们可以看出,onAttachedToWindow是在第一次onDraw前调用的。也就是我们写的View在没有绘制出来时调用的,但只会调用一次。比如,我们写状态栏中的时钟的View,在onAttachedToWindow这方法中做初始化工作,比如注册一些广播等等……也就是AdapterViewFlipper在onAttachedToWindow的时候会判断mAutoStart是否是true,如果是,则会走startFlipping()开启循环播放view
/*** Start a timer to cycle through child views*/public void startFlipping() {mStarted = true;updateRunning();}/*** Internal method to start or stop dispatching flip {@link Message} based* on {@link #mRunning} and {@link #mVisible} state.*/private void updateRunning() {// by default when we update running, we want the// current view to animate inupdateRunning(true);}/*** Internal method to start or stop dispatching flip {@link Message} based* on {@link #mRunning} and {@link #mVisible} state.** @param flipNow Determines whether or not to execute the animation now, in* addition to queuing future flips. If omitted, defaults to* true.*/private void updateRunning(boolean flipNow) {boolean running = !mAdvancedByHost && mVisible && mStarted && mUserPresent&& mAdapter != null;if (running != mRunning) {if (running) {showOnly(mWhichChild, flipNow);postDelayed(mFlipRunnable, mFlipInterval);//看到了吧,祖宗在这呢} else {removeCallbacks(mFlipRunnable);}mRunning = running;}if (LOGD) {Log.d(TAG, "updateRunning() mVisible=" + mVisible + ", mStarted=" + mStarted+ ", mUserPresent=" + mUserPresent + ", mRunning=" + mRunning);}}
广播接收器:
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {final String action = intent.getAction();if (Intent.ACTION_SCREEN_OFF.equals(action)) {mUserPresent = false;updateRunning();} else if (Intent.ACTION_USER_PRESENT.equals(action)) {mUserPresent = true;updateRunning(false);}}};private final Runnable mFlipRunnable = new Runnable() {@Overridepublic void run() {if (mRunning) {showNext();}}};
当然在摧毁view的时候就会停止自动播放:
@Overrideprotected void onDetachedFromWindow() {super.onDetachedFromWindow();mVisible = false;getContext().unregisterReceiver(mReceiver);updateRunning();}
知道原理后,使用就简单多了,AdapterViewFlipper提供一个方法给我们设置自动播放功能:
/*** Set if this view automatically calls {@link #startFlipping()} when it* becomes attached to a window.*/public void setAutoStart(boolean autoStart) {mAutoStart = autoStart;}
我们只要调用这个方法就可以自动播放了。
二、怎么利用AdapterViewFlipper来进行垂直自动滚动广告栏
下面让我用自己写的demo告诉你怎么做。
1、先看布局文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context="top.hellowoodes.adapterviewflipper.MainActivity"><AdapterViewFlipper
android:layout_width="wrap_content"android:layout_height="wrap_content"android:id="@+id/adapterViewFlipper"android:flipInterval="3000" //自动轮播时间间隔android:layout_centerInParent="true" /><Button
android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="自动播放"android:id="@+id/button3"android:onClick="auto"android:layout_alignParentBottom="true" />
</RelativeLayout>
2、Java代码怎么写
AdapterViewFlipper肯定是可以让我们自定义适配器布局的,请看:
(1)先定义一个Adapter
private class MyAdapter extends BaseAdapter{int[] imageIds = new int[]{R.drawable.a2,R.drawable.a3,R.drawable.a4,R.drawable.a2,R.drawable.a3,R.drawable.a4};@Overridepublic int getCount() {return imageIds.length;}@Overridepublic Object getItem(int position) {return position;}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ImageView imageView = new ImageView(AdapterViewFlipperActivity.this); //这里你定义多复杂的布局文件都可以啦imageView.setImageResource(imageIds[position]);imageView.setScaleType(ImageView.ScaleType.FIT_XY);imageView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));return imageView;}}
(2)调用自动播放
/*** Created by wuchunmei on 6/27/17.*/
public class AdapterViewFlipperActivity extends AppCompatActivity {AdapterViewFlipper adapterViewFlipper;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.flipper_layout);adapterViewFlipper = (AdapterViewFlipper) findViewById(R.id.adapterViewFlipper);MyAdapter adapter = new MyAdapter();adapterViewFlipper.setAdapter(adapter);}public void auto(View source) {//开始自动播放adapterViewFlipper.setInAnimation(this, R.animator.flipper_in);adapterViewFlipper.setOutAnimation(this, R.animator.flipper_out);adapterViewFlipper.startFlipping();}
}
在auto(View source)这个方法里面,你会看到setInAnimation跟setOutAnimation这两个方法,这是干什么用的呢。
/*** Specifies the animation used to animate a View that enters the screen.** @param context The application's environment.* @param resourceID The resource id of the animation.** @see #getInAnimation()* @see #setInAnimation(android.animation.ObjectAnimator)*/public void setInAnimation(Context context, int resourceID) {setInAnimation((ObjectAnimator) AnimatorInflater.loadAnimator(context, resourceID));}
这个是控制view进入屏幕的动画
/*** Specifies the animation used to animate a View that exit the screen.** @param outAnimation The animation started when a View exit the screen.** @see #getOutAnimation()* @see #setOutAnimation(android.content.Context, int)*/public void setOutAnimation(ObjectAnimator outAnimation) {mOutAnimation = outAnimation;}
这个是view退出屏幕的动画。
所以到这里你懂了么,其实这两个进出的动画就是控制view是垂直滚动的还是横向滚动的啦。
但是这里需要特别注意:AdapterViewFlipper这个控件,setInAnimation跟setOutAnimation只能出入属性动画,出入tween动画无效,亲身试过了
看我写的动画,在res下新建一个animator的文件夹:
(1)进入动画:
<?xml version="1.0" encoding="utf-8"?><objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"android:duration="500"android:interpolator="@android:anim/accelerate_decelerate_interpolator"android:propertyName="y"android:valueFrom="1000"android:valueTo="0"android:valueType="floatType"/>
(2)退出动画
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"android:duration="500"android:interpolator="@android:anim/accelerate_decelerate_interpolator"android:propertyName="y"android:valueFrom="0"android:valueTo="-1000"android:valueType="floatType"/>
从这两个动画,相信你更直观地看出怎么个垂直滚动法了。
从源码角度上探索AdapterViewFlipper怎么实现广告栏的垂直自动滚动相关推荐
- 从源码角度解析Android中APK安装过程
从源码角度解析Android中APK的安装过程 1. Android中APK简介 Android应用Apk的安装有如下四种方式: 1.1 系统应用安装 没有安装界面,在开机时自动完成 1.2 网络下载 ...
- Mybatis底层原理学习(二):从源码角度分析一次查询操作过程
在阅读这篇文章之前,建议先阅读一下我之前写的两篇文章,对理解这篇文章很有帮助,特别是Mybatis新手: 写给mybatis小白的入门指南 mybatis底层原理学习(一):SqlSessionFac ...
- 从源码角度来读Handler
最近打算从源码角度来读一下Handler,MessageQueue,Message,Looper,这四个面试必考项.所以今天先从Handler开始. 一.Handler的作用 源码定义 There a ...
- 从源码角度分析MapReduce的map-output流程
文章目录 前言 流程图 源码分析 1 runNewMapper方法 2.NewOutputCollector方法 2.1 createSortingCollector方法 2.1.1 collecto ...
- 从JDK源码角度看Long
概况 Java的Long类主要的作用就是对基本类型long进行封装,提供了一些处理long类型的方法,比如long到String类型的转换方法或String类型到long类型的转换方法,当然也包含与其 ...
- 从源码角度看Android系统SystemServer进程启动过程
SystemServer进程是由Zygote进程fork生成,进程名为system_server,主要用于创建系统服务. 备注:本文将结合Android8.0的源码看SystemServer进程的启动 ...
- 从源码角度看Android系统Zygote进程启动过程
在Android系统中,DVM.ART.应用程序进程和SystemServer进程都是由Zygote进程创建的,因此Zygote又称为"孵化器".它是通过fork的形式来创建应用程 ...
- Android -- 带你从源码角度领悟Dagger2入门到放弃(一)
1,以前的博客也写了两篇关于Dagger2,但是感觉自己使用的时候还是云里雾里的,更不谈各位来看博客的同学了,所以今天打算和大家再一次的入坑试试,最后一次了,保证最后一次了. 2,接入项目 在项目的G ...
- 从shiro源码角度学习工厂方法设计模式
绪论 shiro是一个简单易用,功能强大的Java安全框架,学习其源码设计思想对我们的编码水平的提高大有裨益.现在,就从源码角度带大家学习一下shiro里面的工厂方法模式. 这里的前提是读者有过使用s ...
最新文章
- php 返回一个json对象,PHP给前端返回一个JSON对象的实例讲解
- django 组件架构图
- Android 常用开源框架源码解析 系列 (四)Glide
- bundle传递对象与Serializable、Parcelable接口理解和思考
- 使用 SAP Fiori Tools 部署 SAP UI5 应用到 ABAP 服务器时遇到的各种错误和解决办法
- 信号回勾产生的原因_燃气减压阀振动的原因及处理方案
- wireshark最新版本for Ubuntu18.04(六)
- Python并发编程之线程中的信息隔离(五)
- h5项目解决苹果手机iOS系统字体放大问题
- steam安裝位置linux,「Linux」- 安装 Steam 客户端 @20210219
- 阿里云备案服务号是什么怎么用?
- windows 服务器cpu使占用高的原因分析与解决办法
- python资产负债表_用Python清理雅虎财务资产负债表
- 『WIN11』出现问题,你的PIN不可用,请重置PIN,无限循环解决
- html—table(房屋楼层显示以及根据不同类型进行背景颜色区分)
- python文本txt词频统计_python实例:三国演义TXT文本词频分析
- keil c支持汇编语言吗,keil中用汇编实现hello.c的功能
- C语言—指针数组与数组指针
- 朱洪教育c语言课程,诚恳呼吁多给学科教育类研究生攻读教育学博士的机会
- 常见的开源许可证比较BSDamp;Ap…