写这个Dome只是想看一下ViewSwitcher的视图切换滑动效果,以及实现一些分屏显示的效果,测试一下与ViewPager+fragment处理效果的区别。
这边是demo源码工程的CSDN下载地址:http://download.csdn.net/detail/gjr9596/7825889,
demo里面先显示测试了一下TextSwitcher与ImageSwitcher的效果,我先介绍一下ViewSwitcher的子类,然后在介绍自定义ViewSwitcher的用法。
ImageSwitcher和TextSwitcher的继承关系是一样的。
有两个重要的父类:ViewSwitcher和ViewAnimator,继承于ViewSwitcher,说明具备了切换功能,继承于ViewAnimator,说明具备了动画功能。
他们的使用都分为3个步骤:
ImageSwitcher imageSwitcher = (ImageSwitcher)findViewById(R.id.imageSwitcher); TextSwitcher textSwitcher = (TextSwitcher)findViewById(R.id.textSwitcher);
textSwitcher.setFactory(new MyTextFacotry());
imageSwitcher.setFactory(new MyImageFacotry());
其中自定义MyTextFacotry与MyImageFacotry同样都继承ViewFactory:
private class MyImageFacotry implements ViewFactory { public View makeView() { ImageView imageView = (ImageView)LayoutInflater.from(getApplicationContext()) .inflate(R.layout.image_switcher_view, imageSwitcher, false); return imageView; } } private class MyTextFacotry implements ViewFactory { public View makeView() { TextView textView = (TextView)LayoutInflater.from(getApplicationContext()). inflate(R.layout.text_switcher_view, textSwitcher, false); return textView; } }
实现继承了ViewFactory的自定义工厂类中的makeView()方法,makeView()方法就是负责给ImageSwitcher或textSwitcher创建显示视图的。
这个函数就是得到我们要生成的View,这里实际上直接从布局得到
注意这里的text_switcher_view和image_switcher_view的布局文件中都只包括一个TextView与ImageView
R.layout.text_switcher_view
<?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:textSize="26dp" android:textColor="#00ff00" android:text="这是TextSwitcher" > </TextView>
R.layout.image_switcher_view
<?xml version="1.0" encoding="utf-8"?> <ImageView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="fitCenter" > </ImageView>
TextSwitcher在每次setText的时候显示视图切换效果
textSwitcher.setText(""+indexText); textSwitcher.setOnTouchListener(new OnTouchListener() { public boolean onTouch(View arg0, MotionEvent arg1) { if (arg1.getAction() == MotionEvent.ACTION_DOWN) { touchDownX = arg1.getX(); return true; } else if(arg1.getAction() == MotionEvent.ACTION_UP) { touchUpX = arg1.getX(); if (touchDownX - touchUpX > 100)//左滑 { if (indexText <3 ) { textSwitcher.setInAnimation(getApplicationContext(), R.anim.switcher_in_right); textSwitcher.setOutAnimation(getApplicationContext(), R.anim.switcher_out_right); indexText++; textSwitcher.setText(""+indexText); } } else { if (indexText >0) { textSwitcher.setInAnimation(getApplicationContext(), R.anim.switcher_in_left); textSwitcher.setOutAnimation(getApplicationContext(), R.anim.switcher_out_left); indexText--; textSwitcher.setText(""+indexText); } } return true; } return false; } });
imageSwitcher.setImageResource(arrayImage[indexImage]); imageSwitcher.setOnTouchListener(new OnTouchListener() { public boolean onTouch(View arg0, MotionEvent arg1) { if (arg1.getAction() == MotionEvent.ACTION_DOWN) { touchDownX = arg1.getX(); return true; } else if(arg1.getAction() == MotionEvent.ACTION_UP) { touchUpX = arg1.getX(); if (touchDownX - touchUpX > 100)//左滑 { if (indexImage <3 ) { imageSwitcher.setInAnimation(getApplicationContext(), R.anim.switcher_in_right); imageSwitcher.setOutAnimation(getApplicationContext(), R.anim.switcher_out_right); imageSwitcher.setImageResource(arrayImage[indexImage]); indexImage++; } } else { if (indexImage >0) { imageSwitcher.setInAnimation(getApplicationContext(), R.anim.switcher_in_left); imageSwitcher.setOutAnimation(getApplicationContext(), R.anim.switcher_out_left); imageSwitcher.setImageResource(arrayImage[indexImage]); indexImage--; } } return true; } return false; } });
在这里写的是一个向左滑动时候,消失的view左移消除加载的view左移加载,向右滑动时候,消失的view右移消除加载的view右移加载,很常见的动画效果。有4个动画文件,分别是switcher_in_right,switcher_out_right,switcher_in_left,witcher_out_left
switcher_in_left.xml
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android"> <scale android:fromXScale="0.0" android:toXScale="1.0" android:fromYScale="1.0" android:toYScale="1.0" android:pivotX="1%" android:pivotY="1%" android:duration="500" /> <alpha android:interpolator="@android:anim/linear_interpolator" android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="500"/> </set>
switcher_in_right.xml
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android"> <scale android:fromXScale="0.0" android:toXScale="1.0" android:fromYScale="1.0" android:toYScale="1.0" android:pivotX="99%" android:pivotY="99%" android:duration="500" /> <alpha android:interpolator="@android:anim/linear_interpolator" android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="500"/> </set>
witcher_out_left.xml
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android"> <scale android:fromXScale="1.0" android:toXScale="0.0" android:fromYScale="1.0" android:toYScale="1.0" android:pivotX="99%" android:pivotY="99%" android:duration="500"/> <alpha android:interpolator="@android:anim/linear_interpolator" android:fromAlpha="1.0" android:toAlpha="0.0" android:duration="500"/> </set>
switcher_out_right.xml
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android"> <scale android:fromXScale="1.0" android:toXScale="0.0" android:fromYScale="1.0" android:toYScale="1.0" android:pivotX="1%" android:pivotY="1%" android:duration="500"/> <alpha android:interpolator="@android:anim/linear_interpolator" android:fromAlpha="1.0" android:toAlpha="0.0" android:duration="500"/> </set>
这里先介绍下继承了ViewSwitcher类的ImageSwitcher和TextSwitcher的使用,待会再介绍自定义ViewSwitcher实现GridView分屏显示效果。
switcher = (SlideMenuSwitcher) findViewById(R.id.slide_view); switcher.setData(makeItems()); //将36个图标赋值到swithcer中。 /**模拟36个应用程序*/ private ArrayList<DataItem> makeItems() { ArrayList<DataItem> items = new ArrayList<DataItem>(); for (int i = 0; i < 36; i++) { String label = "应用-" + i; Drawable drawable = getResources().getDrawable(R.drawable.ic_launcher); DataItem item = new DataItem(); item.dataName = label; item.drawable = drawable; items.add(item); } return items; }
public class SlideViewFactory implements ViewFactory{ LayoutInflater mInflater; public SlideViewFactory(Context context) { mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); } /**这个函数就是得到我们要生成的View,这里实际上直接从布局得到, ,一个view用于显示一屏的应用程序*/ public View makeView() { return mInflater.inflate(R.layout.slidelistview, null); } }
public class SlideMenuSwitcher extends ViewSwitcher{ private MenuData mMenuData; private int mCurrentScreen; private Context mContext; private float touchDownX , touchUpX; int index = 0; private OnTouchListener mOnTouchListener; GestureDetector gestureDetector; //构造函数,初始化 public SlideMenuSwitcher(Context context, AttributeSet attrs) { super(context, attrs); setFactory(new SlideViewFactory(context)); // setAnimateFirstView(false); mContext = context; //初始化一个GestureDetector(手势识别类)变量,事件监听来判断滑动切换 this.gestureDetector =new GestureDetector(context, new MyOnGestureListener()); } /**通过该方法将数据赋值进去,并且将初始的屏显示出来*/ public void setData(ArrayList<DataItem> dataItems) { // MenuData是一个自定义类,用来给gridview填充图标数据的 mMenuData = new MenuData(); mMenuData.setMenuItems(dataItems); // mCurrentScreen = mMenuData.getScreenNumber() / 2; mCurrentScreen=0; // mCurrentScreen保存当前显示的屏的状态 // 调用ViewSwitcher.getCurrentView()其实得到的就是其工厂类SlideViewFactory中的MakeView()函数返回的view。 LinearLayout view=(LinearLayout) getCurrentView(); final MyGridView listView = (MyGridView)view.findViewById(R.id.slidelistview_gridview); TextView textview = (TextView)view.findViewById(R.id.slidelistview_title); textview.setText("第"+mCurrentScreen+"页"); listView.setGestureDetector(gestureDetector); OneScreenListAdapter adapter = new OneScreenListAdapter(mContext); adapter.setScreenData(mMenuData.getScreen(mCurrentScreen)); listView.setAdapter(adapter); } /**该方法用于显示下一屏*/ public void showNextScreen() { if (mCurrentScreen < mMenuData.getScreenNumber() - 1) { //每次切换下一屏的时候判断当前屏幕状态,设置切换动画,动画效果与前面的一样。 mCurrentScreen++; setInAnimation(mContext, R.anim.switcher_in_right); setOutAnimation(mContext, R.anim.switcher_out_right); } else { return; } //每次切换下一屏的时候更新数据 setViewData(mCurrentScreen); showNext(); } /**该方法用于显示上一屏*/ public void showPreviousScreen() { if (mCurrentScreen > 0) { mCurrentScreen--; setInAnimation(mContext, R.anim.switcher_in_left); setOutAnimation(mContext, R.anim.switcher_out_left); } else { return; } setViewData(mCurrentScreen); showPrevious(); } private void setViewData(int index) { // GridView listView = (GridView) getNextView(); View view=getNextView(); MyGridView listView = (MyGridView)view.findViewById(R.id.slidelistview_gridview); TextView textview = (TextView)view.findViewById(R.id.slidelistview_title); textview.setText("第"+mCurrentScreen+"页"); // listView.setOnTouchListener(vOnTouchListener); listView.setGestureDetector(gestureDetector); OneScreenListAdapter adapter = new OneScreenListAdapter(mContext); adapter.setScreenData(mMenuData.getScreen(index)); listView.setAdapter(adapter); } /** * 创建一个Listener来实时监听当前面板操作手势。 */ class MyOnGestureListener extends SimpleOnGestureListener { @Override public boolean onSingleTapUp(MotionEvent e) { Log.i(getClass().getName(), "onSingleTapUp-----" + getActionName(e.getAction())); return false; } @Override public void onLongPress(MotionEvent e) { Log.i(getClass().getName(), "onLongPress-----" + getActionName(e.getAction())); } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { Log.i(getClass().getName(), "onScroll-----" + getActionName(e2.getAction()) + ",(" + e1.getX() + "," + e1.getY() + ") ,(" + e2.getX() + "," + e2.getY() + ")"); return false; } //关键是重写了onFling()函数,判断一个手势操作结束后的水平移动距离来确定是否滑动事件, @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { Log.i(getClass().getName(), "onFling-----" + getActionName(e2.getAction()) + ",(" + e1.getX() + "," + e1.getY() + ") ,(" + e2.getX() + "," + e2.getY() + ")"); if (e1.getX() -e2.getX() > 100) {//左滑 if (index < 4 ) { showNextScreen();//切换下一屏 index++; } } else if(e2.getX() - e1.getX() > 100) {//右滑 if (index >0) { showPreviousScreen();//切换上一屏 index--; } } return false; } @Override public void onShowPress(MotionEvent e) { Log.i(getClass().getName(), "onShowPress-----" + getActionName(e.getAction())); } @Override public boolean onDown(MotionEvent e) { Log.i(getClass().getName(), "onDown-----" + getActionName(e.getAction())); return false; } @Override public boolean onDoubleTap(MotionEvent e) { Log.i(getClass().getName(), "onDoubleTap-----" + getActionName(e.getAction())); return false; } @Override public boolean onDoubleTapEvent(MotionEvent e) { Log.i(getClass().getName(), "onDoubleTapEvent-----" + getActionName(e.getAction())); return false; } @Override public boolean onSingleTapConfirmed(MotionEvent e) { Log.i(getClass().getName(), "onSingleTapConfirmed-----" + getActionName(e.getAction())); return false; } } }
我们首先重写一个MyGridView继承GridView,重点是重写一下onTouchEvent()与dispatchTouchEvent()
目的是onTouch事件不屏蔽继续向下传递,并且在GestureDetector手势识别类中处理相关逻辑,
通过setGestureDetector()来设置gestureDetector变量,关于gestureDetector变量的实例化是在SlideMenuSwitcher 类中完成的,
创建一个Listener来实时监听当前面板操作手势。
class MyOnGestureListener extends SimpleOnGestureListener {}重写了其中的onFling()来完成切换逻辑判断。
gestureDetector =new GestureDetector(context, new MyOnGestureListener());来实例化一个GestureDetector变量
public class MyGridView extends GridView { GestureDetector gestureDetector; public MyGridView(Context context) { super(context); } public MyGridView(Context context, AttributeSet attrs) { super(context, attrs); } public void setGestureDetector(GestureDetector gestureDetector) { this.gestureDetector = gestureDetector; } public GestureDetector getGestureDetector() { return this.gestureDetector; } @Override public boolean onTouchEvent(MotionEvent ev) { super.onTouchEvent(ev); return gestureDetector.onTouchEvent(ev); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { gestureDetector.onTouchEvent(ev); super.dispatchTouchEvent(ev); return true; } }
当用户触摸屏幕的时候,会产生许多手势,例如down,up,scroll,filing等等。
一般情况下,我们知道View类有个View.OnTouchListener内部接口,通过重写他的onTouch(View v, MotionEvent event)方法,我们可以处理一些touch事件,但是这个方法太过简单,如果需要处理一些复杂的手势,用这个接口就会很麻烦(因为我们要自己根据用户触摸的轨迹,一些用户习惯去判断是什么样的手势,执行什么样的操作)。
Android sdk给我们提供了GestureDetector(Gesture:手势Detector:识别)类,通过这个类我们可以识别很多的手势,主要是通过他的onTouchEvent(event)方法完成了不同手势的识别。虽然他能识别手势,但是不同的手势要怎么处理,应该是提供给我们在程序中实现的。
GestureDetector.OnGestureListener接口:用来通知普通的手势事件,该接口有如下六个回调函数:
1. onDown(MotionEvent e):down事件,按下事件,onDown只要Touch down则一定立刻触发;
2. onSingleTapUp(MotionEvent e):一次点击up事件,在touch down后又没有滑动(onScroll),也没有长按(onLongPress),然后直接松开(Touchup)时触发。
3. onShowPress(MotionEvent e):down事件发生而move或则up还没发生前触发该 事件;而Touchdown后过一会没有滑动先触发onShowPress再是onLongPress。所以Touchdown后一直不滑动按照onDown->onShowPress->onLongPress这个顺序触发。
4. onLongPress(MotionEvent e):长按事件;Touch了不移动一直Touch down时触发
5. onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY):滑动手势事件;Touch了滑动一点距离后,在ACTION_UP时才会触发一次,并且一次事件只触发一次。 参数:e1 第1个ACTION_DOWN MotionEvent 第一个按下的点并且只有一个;e2 最后一个ACTION_MOVE MotionEvent,最后一个移动的点 ;velocityX X轴上的移动速度,像素/秒 ;velocityY Y轴上的移动速度,像素/秒.触发条件:X轴的坐标位移大于FLING_MIN_DISTANCE,且移动速度大于FLING_MIN_VELOCITY个像素/秒
6. onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY):在屏幕上
拖动事件。无论是用手拖动view,或者是以抛的动作滚动,都会多次触发,这个方法在ACTION_MOVE动作发生时就会触发