天天看点

自定义ViewSwitcher实现分屏及切换动画效果一、TextSwitcher与ImageSwitcher的用法及视图切换动画效果二,自定义SlideMenuSwitcher是ViewSwitcher的重载,用该类实现两个屏的切换和切换的动画实现三、关于GridView的onTouch与onItemClick事件冲突的解决方案四、关于GestureDetector类及其用法

写这个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,说明具备了动画功能。

自定义ViewSwitcher实现分屏及切换动画效果一、TextSwitcher与ImageSwitcher的用法及视图切换动画效果二,自定义SlideMenuSwitcher是ViewSwitcher的重载,用该类实现两个屏的切换和切换的动画实现三、关于GridView的onTouch与onItemClick事件冲突的解决方案四、关于GestureDetector类及其用法

他们的使用都分为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动作发生时就会触发