Android中ViewFlipper实现动态加载view
前面已经讲过ImageSwitcher和TextSwitcher。ImageSwitcher用来切换ImageView的,TextSwitcher是用来切换TextView的。
但是我们现在要切换自定义View怎么办?
ImageSwitcher和TextSwitcher已经不能满足我们的需求。ViewFlipper可以在任意View之间切换。下面我们就来讲解它。
先看一下结构图
可以看到ViewSwitcher和ViewFlipper都是继承自ViewAnimator。
下面通过一个Demo了解一下ViewFlipper的用法
main.xml
Html代码- <?xmlversion="1.0"encoding="utf-8"?>
- <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical">
- <ViewFlipper
- android:id="@+id/viewFlipper"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
- <include
- android:id="@+id/layout01"
- layout="@layout/layout01"/>
- <include
- android:id="@+id/layout02"
- layout="@layout/layout02"/>
- </ViewFlipper>
- </LinearLayout>
layout01.xml Html代码
- <?xmlversion="1.0"encoding="utf-8"?>
- <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical">
- <TextView
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:gravity="center"
- android:text="一个TextView"
- android:textSize="40dip"/>
- </LinearLayout>
layout02.xml Html代码
- <?xmlversion="1.0"encoding="utf-8"?>
- <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical">
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:gravity="center"
- android:orientation="vertical">
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/ic_launcher"/>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="一个TextView+一个ImageView"
- android:textSize="20dip"/>
- </LinearLayout>
- </LinearLayout>
ViewFlipperDemoActivity.java Java代码
- packagecom.tianjf;
- importandroid.app.Activity;
- importandroid.os.Bundle;
- importandroid.view.MotionEvent;
- importandroid.view.View;
- importandroid.view.View.OnTouchListener;
- importandroid.view.animation.AnimationUtils;
- importandroid.widget.ViewFlipper;
- publicclassViewFlipperDemoActivityextendsActivityimplements
- OnTouchListener{
- privateViewFlipperviewFlipper;
- //左右滑动时手指按下的X坐标
- privatefloattouchDownX;
- //左右滑动时手指松开的X坐标
- privatefloattouchUpX;
- @Override
- publicvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- viewFlipper=(ViewFlipper)findViewById(R.id.viewFlipper);
- viewFlipper.setOnTouchListener(this);
- }
- @Override
- publicbooleanonTouch(Viewv,MotionEventevent){
- if(event.getAction()==MotionEvent.ACTION_DOWN){
- //取得左右滑动时手指按下的X坐标
- touchDownX=event.getX();
- returntrue;
- }elseif(event.getAction()==MotionEvent.ACTION_UP){
- //取得左右滑动时手指松开的X坐标
- touchUpX=event.getX();
- //从左往右,看前一个View
- if(touchUpX-touchDownX>100){
- //设置View切换的动画
- viewFlipper.setInAnimation(AnimationUtils.loadAnimation(this,
- android.R.anim.slide_in_left));
- viewFlipper.setOutAnimation(AnimationUtils.loadAnimation(this,
- android.R.anim.slide_out_right));
- //显示下一个View
- viewFlipper.showPrevious();
- //从右往左,看后一个View
- }elseif(touchDownX-touchUpX>100){
- //设置View切换的动画
- //由于Android没有提供slide_out_left和slide_in_right,所以仿照slide_in_left和slide_out_right编写了slide_out_left和slide_in_right
- viewFlipper.setInAnimation(AnimationUtils.loadAnimation(this,
- R.anim.slide_in_right));
- viewFlipper.setOutAnimation(AnimationUtils.loadAnimation(this,
- R.anim.slide_out_left));
- //显示前一个View
- viewFlipper.showNext();
- }
- returntrue;
- }
- returnfalse;
- }
- }
slide_in_right.xml Html代码
- <?xmlversion="1.0"encoding="utf-8"?>
- <setxmlns:android="http://schemas.android.com/apk/res/android">
- <translateandroid:fromXDelta="50%p"android:toXDelta="0"android:duration="300"/>
- <alphaandroid:fromAlpha="0.0"android:toAlpha="1.0"android:duration="300"/>
- </set>
slide_out_left.xml Html代码
- <?xmlversion="1.0"encoding="utf-8"?>
- <setxmlns:android="http://schemas.android.com/apk/res/android">
- <translateandroid:fromXDelta="0"android:toXDelta="-50%p"android:duration="300"/>
- <alphaandroid:fromAlpha="1.0"android:toAlpha="0.0"android:duration="300"/>
- </set>
上面的例子是在布局文件中为ViewFlipper固定添加了两个View,如果现在有N个View怎么办呢?那么我们就需要在Java代码里面动态的添加View
先上代码再讲解
main.xml
Html代码- <?xmlversion="1.0"encoding="utf-8"?>
- <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical">
- <com.tianjf.MyViewFlipper
- android:id="@+id/myViewFlipper"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:background="@android:color/white"
- android:gravity="center">
- </com.tianjf.MyViewFlipper>
- </LinearLayout>
flipper_view.xml Html代码
- <?xmlversion="1.0"encoding="utf-8"?>
- <ScrollViewxmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:scrollbars="none">
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:orientation="vertical">
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/ic_launcher"/>
- <TextView
- android:id="@+id/textView"
- android:textSize="100dip"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
- </LinearLayout>
- </ScrollView>
MyGestureListener.java
Java代码- packagecom.tianjf;
- importandroid.view.GestureDetector.SimpleOnGestureListener;
- importandroid.view.MotionEvent;
- publicclassMyGestureListenerextendsSimpleOnGestureListener{
- privateOnFlingListenermOnFlingListener;
- publicOnFlingListenergetOnFlingListener(){
- returnmOnFlingListener;
- }
- publicvoidsetOnFlingListener(OnFlingListenermOnFlingListener){
- this.mOnFlingListener=mOnFlingListener;
- }
- @Override
- publicfinalbooleanonFling(finalMotionEvente1,finalMotionEvente2,
- finalfloatspeedX,finalfloatspeedY){
- if(mOnFlingListener==null){
- returnsuper.onFling(e1,e2,speedX,speedY);
- }
- floatXFrom=e1.getX();
- floatXTo=e2.getX();
- floatYFrom=e1.getY();
- floatYTo=e2.getY();
- //左右滑动的X轴幅度大于100,并且X轴方向的速度大于100
- if(Math.abs(XFrom-XTo)>100.0f&&Math.abs(speedX)>100.0f){
- //X轴幅度大于Y轴的幅度
- if(Math.abs(XFrom-XTo)>=Math.abs(YFrom-YTo)){
- if(XFrom>XTo){
- //下一个
- mOnFlingListener.flingToNext();
- }else{
- //上一个
- mOnFlingListener.flingToPrevious();
- }
- }
- }else{
- returnfalse;
- }
- returntrue;
- }
- publicinterfaceOnFlingListener{
- voidflingToNext();
- voidflingToPrevious();
- }
- }
MyViewFlipper.java
Java代码- packagecom.tianjf;
- importcom.tianjf.MyGestureListener.OnFlingListener;
- importandroid.content.Context;
- importandroid.util.AttributeSet;
- importandroid.view.GestureDetector;
- importandroid.view.MotionEvent;
- importandroid.view.View;
- importandroid.widget.ViewFlipper;
- publicclassMyViewFlipperextendsViewFlipperimplementsOnFlingListener{
- privateGestureDetectormGestureDetector=null;
- privateOnViewFlipperListenermOnViewFlipperListener=null;
- publicMyViewFlipper(Contextcontext){
- super(context);
- }
- publicMyViewFlipper(Contextcontext,AttributeSetattrs){
- super(context,attrs);
- }
- publicvoidsetOnViewFlipperListener(OnViewFlipperListenermOnViewFlipperListener){
- this.mOnViewFlipperListener=mOnViewFlipperListener;
- MyGestureListenermyGestureListener=newMyGestureListener();
- myGestureListener.setOnFlingListener(this);
- mGestureDetector=newGestureDetector(myGestureListener);
- }
- @Override
- publicbooleanonInterceptTouchEvent(MotionEventev){
- if(null!=mGestureDetector){
- returnmGestureDetector.onTouchEvent(ev);
- }else{
- returnsuper.onInterceptTouchEvent(ev);
- }
- }
- @Override
- publicvoidflingToNext(){
- if(null!=mOnViewFlipperListener){
- intchildCnt=getChildCount();
- if(childCnt==2){
- removeViewAt(1);
- }
- addView(mOnViewFlipperListener.getNextView(),0);
- if(0!=childCnt){
- setInAnimation(getContext(),R.anim.left_slip_in);
- setOutAnimation(getContext(),R.anim.left_slip_out);
- setDisplayedChild(0);
- }
- }
- }
- @Override
- publicvoidflingToPrevious(){
- if(null!=mOnViewFlipperListener){
- intchildCnt=getChildCount();
- if(childCnt==2){
- removeViewAt(1);
- }
- addView(mOnViewFlipperListener.getPreviousView(),0);
- if(0!=childCnt){
- setInAnimation(getContext(),R.anim.right_slip_in);
- setOutAnimation(getContext(),R.anim.right_slip_out);
- setDisplayedChild(0);
- }
- }
- }
- publicinterfaceOnViewFlipperListener{
- ViewgetNextView();
- ViewgetPreviousView();
- }
- }
ViewFlipperDemoActivity.java Java代码
- packagecom.tianjf;
- importcom.tianjf.MyViewFlipper.OnViewFlipperListener;
- importandroid.app.Activity;
- importandroid.os.Bundle;
- importandroid.view.LayoutInflater;
- importandroid.view.View;
- importandroid.widget.ScrollView;
- importandroid.widget.TextView;
- publicclassViewFlipperDemoActivityextendsActivityimplementsOnViewFlipperListener{
- privateMyViewFlippermyViewFlipper;
- privateintcurrentNumber;
- @Override
- publicvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- currentNumber=1;
- myViewFlipper=(MyViewFlipper)findViewById(R.id.myViewFlipper);
- myViewFlipper.setOnViewFlipperListener(this);
- myViewFlipper.addView(creatView(currentNumber));
- }
- @Override
- publicViewgetNextView(){
- currentNumber=currentNumber==10?1:currentNumber+1;
- returncreatView(currentNumber);
- }
- @Override
- publicViewgetPreviousView(){
- currentNumber=currentNumber==1?10:currentNumber-1;
- returncreatView(currentNumber);
- }
- privateViewcreatView(intcurrentNumber){
- LayoutInflaterlayoutInflater=LayoutInflater.from(this);
- ScrollViewresultView=(ScrollView)layoutInflater.inflate(R.layout.flipper_view,null);
- ((TextView)resultView.findViewById(R.id.textView)).setText(currentNumber+"");
- returnresultView;
- }
- }
好了,代码上完了,开始讲解!
ViewFilpper的showPrevious()方法和showNext()方法是用来显示已经在布局文件中定义好了的View,现在我们没有在布局文件中为ViewFlipper添加View,那么showPrevious()方法和showNext()方法就不能用了。但是我们怎么实现滑动来切换View呢?用什么方法呢?
这时候,我们就要自定义一个MyViewFlipper来监听滑动事件,并做切换视图的处理。
你可以让MyViewFlipper实现OnTouchListener接口,然后实现onTouch方法,然后根据MotionEvent.ACTION_DOWN和MotionEvent.ACTION_UP的坐标判断是不是滑动事件,就像ImageSwitcher中讲解的那样(http://blog.csdn.net/tianjf0514/article/details/7556487)
除了自己判断是不是滑动事件,那么Android有没有直接提供哪个方法作为滑动事件的回调函数呢?答案是:提供了。OnGestureListener中的onFling方法就是滑动事件的回调函数。这时候你也许会毫不犹豫的让MyViewFlipper实现OnGestureListener接口,并复写onFling方法。这样做当然可以,不过实现OnGestureListener接口不仅仅要复写onFling方法,还要复写其他的方法(onDown()、onShowPress()、onSingleTapUp()、onScroll()、onLongPress()),但是这些回调函数我们不需要,这就造成了垃圾代码。
为了避免垃圾代码,Android提供了一个类SimpleOnGestureListener已经实现了OnGestureListener接口和OnDoubleTapListener接口,并复写了所有方法。那么我们只要新建一个自己的MyGestureListener.java来继承SimpleOnGestureListener,并有选择性的复写需要的方法(我们在此只复写onFling方法)。
这时,我们就自定义了一个手势类,并且这个手势类会监听滑动事件来做一些处理。但是我们怎么利用这个手势类呢?怎么利用到MyViewFlipper类中去呢?
关于onFling方法,有一点要注意:不是每个View都能有onFling回调函数,一开始,我的flipper_view.xml布局文件最外层是一个LinearLayout,死活都走不到onFling方法,后来在外层又套了一个ScrollView,就能正常走到OnFling方法里面了。
可以看到flingToNext方法和flingToPrevious方法里面会判断childCnt,如果为2,就removeViewAt(1);,然后再addView(mOnViewFlipperListener.getNextView(), 0);。这就要回顾一下ImageSwitcher的原理,ViewFlipper的原理和ImageSwitcher一样,有且仅有2个子View,滑动时候就在这两个子View上来回切换。index为0的就是当前看到的,index为1的就是看不见的。上面代码的意思就是:当滑动时,必然要新添加一个View,那么子View的个数有可能大于2,随意要先判断一下如果childCnt == 2,那么就把index == 1的那个View(即看不见的View)给Remove调,然后把新添加的View添加到index == 0处。这样可以减少内存消耗。
OK,这个例子的基本的注意点已经讲完了。下面在系统的回顾一下这个例子的具体流程。
在我们滑动手机屏幕的时候(假设我们从右往左滑动),那么应该显示下一个View。
- 调用onFling方法中的mOnFlingListener.flingToNext();
- flingToNext方法的是实现在MyViewFlipper类中,调用flingToNext方法的addView(mOnViewFlipperListener.getNextView(), 0);
- getNextView的实现在ViewFlipperDemoActivity类中
好的,讲完了,要想完全理解透彻,跑跑例子,理解理解。
更多相关文章
- Android控件之Spinner用法详解
- Android(第三种动画)属性动画完全解析(上),初识属性动画的基本用法
- Android(安卓)focus search returned a view that wasn't able t
- 探究Android(安卓)View 绘制流程,Canvas 的由来。
- Android一年工作经验应掌握的知识点
- Android(安卓)ContentProvider学习
- android中selector改变界面状态用法小结
- android关闭其他app
- 在Android(安卓)Jar包使用图片资源的解决方法