写这个Dome只是想看一下ViewSwitcher的视图切换滑动效果,以及实现一些分屏显示的效果,测试一下与ViewPager+fragment处理效果的区别。
这边是demo源码工程的CSDN下载地址:http://download.csdn.net/detail/gjr9596/7825889,
demo里面先显示测试了一下TextSwitcher与ImageSwitcher的效果,我先介绍一下ViewSwitcher的子类,然后在介绍自定义ViewSwitcher的用法。
一、TextSwitcher与ImageSwitcher的用法及视图切换动画效果
ImageSwitcher和TextSwitcher的继承关系是一样的。
有两个重要的父类:ViewSwitcher和ViewAnimator,继承于ViewSwitcher,说明具备了切换功能,继承于ViewAnimator,说明具备了动画功能。
他们的使用都分为3个步骤:
1.实例化一个变量
ImageSwitcher imageSwitcher = (ImageSwitcher)findViewById(R.id.imageSwitcher);
TextSwitcher textSwitcher = (TextSwitcher)findViewById(R.id.textSwitcher);
2.通过设置工厂来实现切换时的生成视图
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>
3.给组建设置初始显示文字或图片,设置onTouch滑动事件监听。
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设置背景的时候进行显示视图切换动画效果
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分屏显示效果。
二,自定义SlideMenuSwitcher是ViewSwitcher的重载,用该类实现两个屏的切换和切换的动画实现
同样也是3步。
1、首先继承ViewSwitcher写一个自定义的SlideMenuSwitcher 视图切换类,实例化变量,塞入数据。
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;
}
2、设置setFactory(new SlideViewFactory(context)); 这里是在自定义SlideMenuSwitcher 中进行。
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);
}
}
SlideViewFactory 继承 ViewFactory 同样重写其中的makeview()返回屏幕显示视图,然后在 SlideMenuSwitcher 中调用ViewSwitcher. setFactory(new SlideViewFactory(context)); 来设置,下面介绍一下SlideMenuSwitcher 中的几个重要函数。
3.给组建设置初始显示文字或图片,设置onTouch滑动事件监听,都在SlideMenuSwitcher 中实现。
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;
}
}
}
三、关于GridView的onTouch与onItemClick事件冲突的解决方案
我们首先重写一个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;
}
}
四、关于GestureDetector类及其用法
当用户触摸屏幕的时候,会产生许多手势,例如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动作发生时就会触发