一,从源码上认识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怎么实现广告栏的垂直自动滚动相关推荐

  1. 从源码角度解析Android中APK安装过程

    从源码角度解析Android中APK的安装过程 1. Android中APK简介 Android应用Apk的安装有如下四种方式: 1.1 系统应用安装 没有安装界面,在开机时自动完成 1.2 网络下载 ...

  2. Mybatis底层原理学习(二):从源码角度分析一次查询操作过程

    在阅读这篇文章之前,建议先阅读一下我之前写的两篇文章,对理解这篇文章很有帮助,特别是Mybatis新手: 写给mybatis小白的入门指南 mybatis底层原理学习(一):SqlSessionFac ...

  3. 从源码角度来读Handler

    最近打算从源码角度来读一下Handler,MessageQueue,Message,Looper,这四个面试必考项.所以今天先从Handler开始. 一.Handler的作用 源码定义 There a ...

  4. 从源码角度分析MapReduce的map-output流程

    文章目录 前言 流程图 源码分析 1 runNewMapper方法 2.NewOutputCollector方法 2.1 createSortingCollector方法 2.1.1 collecto ...

  5. 从JDK源码角度看Long

    概况 Java的Long类主要的作用就是对基本类型long进行封装,提供了一些处理long类型的方法,比如long到String类型的转换方法或String类型到long类型的转换方法,当然也包含与其 ...

  6. 从源码角度看Android系统SystemServer进程启动过程

    SystemServer进程是由Zygote进程fork生成,进程名为system_server,主要用于创建系统服务. 备注:本文将结合Android8.0的源码看SystemServer进程的启动 ...

  7. 从源码角度看Android系统Zygote进程启动过程

    在Android系统中,DVM.ART.应用程序进程和SystemServer进程都是由Zygote进程创建的,因此Zygote又称为"孵化器".它是通过fork的形式来创建应用程 ...

  8. Android -- 带你从源码角度领悟Dagger2入门到放弃(一)

    1,以前的博客也写了两篇关于Dagger2,但是感觉自己使用的时候还是云里雾里的,更不谈各位来看博客的同学了,所以今天打算和大家再一次的入坑试试,最后一次了,保证最后一次了. 2,接入项目 在项目的G ...

  9. 从shiro源码角度学习工厂方法设计模式

    绪论 shiro是一个简单易用,功能强大的Java安全框架,学习其源码设计思想对我们的编码水平的提高大有裨益.现在,就从源码角度带大家学习一下shiro里面的工厂方法模式. 这里的前提是读者有过使用s ...

最新文章

  1. php 返回一个json对象,PHP给前端返回一个JSON对象的实例讲解
  2. django 组件架构图
  3. Android 常用开源框架源码解析 系列 (四)Glide
  4. bundle传递对象与Serializable、Parcelable接口理解和思考
  5. 使用 SAP Fiori Tools 部署 SAP UI5 应用到 ABAP 服务器时遇到的各种错误和解决办法
  6. 信号回勾产生的原因_燃气减压阀振动的原因及处理方案
  7. wireshark最新版本for Ubuntu18.04(六)
  8. Python并发编程之线程中的信息隔离(五)
  9. h5项目解决苹果手机iOS系统字体放大问题
  10. steam安裝位置linux,「Linux」- 安装 Steam 客户端 @20210219
  11. 阿里云备案服务号是什么怎么用?
  12. windows 服务器cpu使占用高的原因分析与解决办法
  13. python资产负债表_用Python清理雅虎财务资产负债表
  14. 『WIN11』出现问题,你的PIN不可用,请重置PIN,无限循环解决
  15. html—table(房屋楼层显示以及根据不同类型进行背景颜色区分)
  16. python文本txt词频统计_python实例:三国演义TXT文本词频分析
  17. keil c支持汇编语言吗,keil中用汇编实现hello.c的功能
  18. C语言—指针数组与数组指针
  19. 朱洪教育c语言课程,诚恳呼吁多给学科教育类研究生攻读教育学博士的机会
  20. 常见的开源许可证比较BSDamp;Ap…

热门文章

  1. AE生成高清视频设置
  2. OpenStack私有云安装配置虚拟机
  3. Kubernetes哪一点最打动你?或者,它发布过的哪一项特性让你认为最厉害?
  4. Turbo产品系列回来了!
  5. zoj 2576 Queen Collisions
  6. 从 git 的历史记录中彻底删除文件或文件夹
  7. 怎么在苹果手机上进行时间管理?
  8. Vuex基本使用的总结
  9. 高端蓝牙耳机哪个牌子好?四款高音质不错的蓝牙耳机推荐
  10. 张国荣 - 当爱已成往事