自定义View之文字游乐场(二)

四、射击(Shooting)

在这四个项目中,射击是最复杂的一个。
我们来数算下其中涉及到的各个元素,先看下屏幕上可见的几个部分:
1,气球;
2,子弹;
3,炮台;
4,计时;
5,积分。
其实还有些看不见的工作,下面来详细说说:

1,气球

首先说气球,屏幕上显示了5个彩色的气球,要进行移动,并且,移动到屏幕顶部之后,要再次在底部出现;
这里是用了一个椭圆来表示气球,这样来实现:

path.addOval(rect[i], Direction.CCW);

其中rect[i]是某个椭圆的外接矩形,至于Direction.CCW,是逆时针方向绘制(前面摩天轮中出现过一次哦),目的是为了后面沿path写的文字的方向是朝向圆心的。
为了呈现画面的多彩,所以使用了5种颜色。每个气球上要写个字,至少要让玩家一眼就看明白这都是些啥东西嘛,所以写的文字组合起来就是“彩色的气球”。(别说我俗气,你要说这是多么的通俗易懂啊!)
文字的写法,是附着在路径上的,这样实现的:

canvas.drawTextOnPath(str[i], path, 189, -35, mPaint);

说明下,str[i]是指文字啦,为了让文字正向居中显示,要精心调整hOffset与vOffset。
看到这里,读者朋友应该也会感受到5个气球带来的工作量,就是所有气球相关的都需要5份,比如说外接矩形就要5个,颜色要5种,写个字分5份,画椭圆要画5次,等等……

2,子弹

然后是子弹,我使用了一个文字“呯”来表示,它又有几个属性,一个是路线,从炮台指向目标点,这个路线需要存在,但是不能显示出来,我们可以使用透明的画笔颜色来实现;说到目标点,其实就是玩家手指点击的位置,这个就是需要添加一个触摸事件:

boolean onTouchEvent(MotionEvent event)

其中主要的工作就是获取手指触摸点的坐标。
子弹的另一个属性是大小,随着距离的增加,子弹要越来越小;我们可以通过设置画笔的文字尺寸属性来实现:

mPaint.setTextSize(textSizeUse);

其中,textSizeUse值随着距离的增加而变小。
还有一个是速度,不能开枪了就击中,我们要让子弹飞一会。这个就是通过动态调整
Canvas.drawTextOnPath()的第三个参数hOffset来实现了。

3,炮台

再然后是炮台与炮筒,炮台比较简单,在屏幕底部中间位置画出来就行,可是这个炮筒就需要注意了,别看就那么点黑不溜丢的东西,它的方向是要与子弹的线路保持一致的。
那么,怎么来实现方向一致,但是长度不一致的画法呢?
对于炮筒,这里只是简单的实现,就是一根粗的线条即可,它的一端是炮台的中心,另一端应该是在子弹行进的路线上。我们希望的是获取到该路线上指定长度对应位置的坐标值,这样,我们就可以画出一段直线了。好在系统已经提供好了函数,不需要我们自己来进行计算了。获取path上某点的坐标:

    float[] pos= new float[2];
    float[] tan= new float[2];  
    PathMeasure pathMeasure = new PathMeasure(path, false);
    pathMeasure.getPosTan(len, pos, tan);//根据沿path的长度求坐标

getPosTan()的三个参数的说明:
len:沿path的长度,由我们指定;
pos:返回的坐标值,即x坐标(pos[0])与y坐标(pos[1]),正是我们需要的;
tan:返回的切线值,这里我们没有用到;
获取到path上的点坐标后,炮筒也就搞定了。

4,计时

为了游戏的可玩性,我添加了一个游戏计时功能,在指定时间内,看谁击中的气球多。有了比较与竞争,玩游戏也更有动力哟。
这里的游戏时间计时,采用了一个自定义的倒计时工具类:CountDownTimerUtil,其工作机制基本与 CountDownTimer 一致,其优点是可以动态设置倒计时总时间以及回调的间隔时间。其详细介绍,参见我前面的博文可以动态修改时间的CountDownTimer
那么计时是从什么时候开始呢?
我最初是在界面一出现的时候就开始计时,在实际操作时,发现这种方式不太友好,因为刚切换过来界面,还没弄明白是怎么回事呢,就开始计时了,前面几秒钟就浪费掉了。这对于本来就短的游戏时间而言(例如20秒),是很大的损失,玩家不会喜欢的。所以我换了一种方式,就是切换过来时并不开始计时,玩家点击的时候,才是游戏开始的时间。
然而,这样改动之后,启动计时是很自然了,却带来一个新问题,就是一次计时完成之后,玩家正专心玩着,还没有注意到结束了,再次点击,就开始了第二次游戏,之前的计分可能还没有看见呢。这样,我又添加了一个游戏结束后的计分暂停时间,这样就是设定了两个时间间隔,如下:

private static final int GAMETIME = 20;//游戏计时时间20s
private static final int GAMEPAUSETIME = 5;//游戏结束后有5s的停止时间,用于显示计分

5,计分

目前的计分机制非常简单,就是击中一个气球就计1分。本来还有些想法,例如一发子弹击中多个气球,有奖励计分,由于时间关系没有实现,以后再说吧。感兴趣的同学,也可以自己来实现哟。

6,击中事件

前面其实都提到了一个重要的事件,就是“击中”。
什么叫击中呢?
从一个程序员的角度来看,就是子弹的坐标与气球的位置发生了相交。更仔细点说,气球有大小,子弹也有大小,是两个物体相遇,首先是外边缘接触到。
这里没有做到这么精细,文字就是使用了点坐标,气球使用的是外接矩形,若点坐标进入外接矩形的区域中,就判断是击中了。
还有一个要考虑的事情就是击中后,气球的爆炸效果。
分析下气球击中后的场景,就是气球不再继续上升了,同时气球的外形变大,并且断裂开,然后消失掉。
这样分解下来,我们也就可以在画布上来实现了:
首先是记录击中时刻的气球位置(外接矩形的坐标),然后扩大,此时不能画一个椭圆了,要分段画出弧线;外接矩形继续扩大,分段弧形变得更短,最后消失(不画了)。

7,待完善

做到这里,射击已经是一个初具可玩性的游戏了,不过还有一些细节可以进一步完善:
(1)前面说到的,一发子弹击中多个气球,有奖励计分;另外可以存一个最高分记录,激励玩家去刷新记录。
(2)发射的间隔控制:目前子弹的发射不是使用间隔控制,是在子弹发射后消失前,不能进行下一次发射;这与枪炮的工作原理是不相符的,应该有一个最短的发射间隔时间。
(3)子弹的终点,目前就是手指点击的位置,若考虑子弹的直线运动,应该是该直线之上的气球都能被击中,而不是到点击的位置结束。
(4)击中的判断,像前面说的,可以做得更精确些;
(5)气球初始出现位置,可以不那么规律,可以使用某个范围内的随机值;气球上升的速度,以及上升的路线,都可以做些小范围的调整;
(6)系气球的线:本来想用贝塞尔曲线画出来的,结果画出来后,发现导致气球的上升不太规律了,有时存在某个气球突然停滞然后跳动的情况(可见贝塞尔曲线是多么的消耗cpu啊)。
(7)…哎呀呀,不说了不说了,再说下去,没人来玩了。

下面是具体实现:

package com.customview.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Cap;
import android.graphics.Paint.FontMetrics;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.Path.Direction;
import android.graphics.PathMeasure;
import android.graphics.PointF;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import com.customview.LogUtil;


public class ShootingView extends View
{
    private int[] mColors = { Color.RED, Color.GREEN, Color.MAGENTA, 0xffffa500, Color.BLUE };//气球的颜色

    private Paint mPaint;//画笔
    private int progressValue=0;//进度值
    private int progressValueShootRecord=0;//子弹发射时刻的进度值 

    private static final int NUM_BALLOON=5;//气球个数
    Path path ;
    PointF startPoint ;//发射点
    private RectF rect[];//气球的位置
    private int shootIn[];//是否击中
    private RectF rectRecord[];//子弹击中时刻的气球位置
    private int progressValueRecord[];//击中时刻的进度值    

    private int screenWidth;
    private int screenHeight;

    private PointF targetPoint;//用户选择的目标点
    private boolean flagShoot=false;//发射子弹的标志
    private PointF pointBarrel;//炮管的终点坐标
    private int score=0;//计分

    private int textSize=16;//文字大小

    //时间相关
    private static final int GAMETIME = 20;//游戏计时时间20s
    private static final int GAMEPAUSETIME = 5;//游戏结束后有5s的停止时间,用于显示计分
    private CountDownTimerUtil timethis = null;//倒计时定时器
    private int timerStatus = 0;//定时器状态:0,未工作;1,游戏计时;2,游戏暂停时间计时
    private String strTimeRest;//剩余时间   


    public ShootingView(Context context, AttributeSet attrs)
    {       
        this(context, attrs, 0);
    }

    public ShootingView(Context context)
    {
        this(context, null);
    }

    public ShootingView(Context context, AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);

        mPaint = new Paint();
        progressValue=0;
        targetPoint = new PointF(0,0);

        WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
        screenWidth = wm.getDefaultDisplay().getWidth();
        screenHeight = wm.getDefaultDisplay().getHeight();

        rect = new RectF[NUM_BALLOON];
        rectRecord = new RectF[NUM_BALLOON];
        shootIn = new int[NUM_BALLOON];
        progressValueRecord = new int[NUM_BALLOON];
        pointBarrel = new PointF();
        path = new Path();
        startPoint = new PointF();//发射点

        resetShootingStatus();      

        timethis = new CountDownTimerUtil((GAMETIME + GAMEPAUSETIME) * 1000, 1000){

            @Override
            public void onTick(long time1) {
                long time2 = time1 / 1000;              
                long seconds = time2 % 60;
                long minutes = (time2 - seconds) / 60;
                if(seconds>=GAMEPAUSETIME){
                    strTimeRest = "剩余:"+(seconds-GAMEPAUSETIME) + "秒";
                } else {
                    strTimeRest = "时间到了";
                    timerStatus = 2;
                }
            }

            @Override
            public void onFinish() {

                strTimeRest = "点击开始";
                timerStatus = 0;
            }           
        };

    }

    public void setProgressValue(int progressValue){

        this.progressValue = progressValue;
        if(progressValue==0){
            for(int i=0;i<NUM_BALLOON;i++){
                progressValueRecord[i]=0;
            }
            progressValueShootRecord=0;
        }

        postInvalidate();
    }

    public void setColors(int[] colors){
        mColors = colors;   
        postInvalidate();
    }

    public void startCountTimer(){

        timethis.setCountdownInterval(1000);
        timethis.setMillisInFuture((GAMETIME + GAMEPAUSETIME) * 1000);
        timethis.start();   
        timerStatus = 1;
        score = 0;                      
    }

    public void resetShootingStatus(){

        for(int i=0;i<NUM_BALLOON;i++){
            setBalloonRectF(i,-1,-1);
            rectRecord[i] = new RectF();
            shootIn[i]=-1;
            progressValueRecord[i]=progressValue;
        }
        progressValueShootRecord = progressValue;
        LogUtil.logWithMethod(new Exception(),"progressValue="+progressValue);

        pointBarrel.set(0,0);

        if(timethis!=null){
            timethis.cancel();
        }
        strTimeRest="点击开始";
        timerStatus = 0;
        score = 0;

        flagShoot=false;    
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        int mWidth = getMeasuredWidth();
        int mHeight = getMeasuredHeight();

        //按比例计算View各部分的值
        float unit = Math.min(((float)mWidth)/300, ((float)mHeight)/300);
        float lineWidth = 3*unit;//线粗

        String strShoot = "呯";          
        String strBalloon="彩色的气球";
        String[] str = new String[strBalloon.length()];
        for(int i=0;i<strBalloon.length();i++){
            str[i]=strBalloon.substring(i,i+1);
        }

        startPoint.set(mWidth/2, mHeight );//发射点

        for(int i=0;i<NUM_BALLOON;i++){

            //如果气球移动到顶部了,重新设置到底部,以做再次上升
            if(rect[i].bottom<0){
                setBalloonRectF(i,mWidth,mHeight);
            }           

            //移动一次
            rect[i].set(rect[i].left,rect[i].top-3,rect[i].right,rect[i].bottom-3);

            //画椭圆
            if(shootIn[i]<0){
                mPaint.setAntiAlias(true);
                mPaint.setStrokeWidth((float) lineWidth );
                mPaint.setStyle(Style.STROKE);
                mPaint.setStrokeCap(Cap.ROUND);
                mPaint.setColor(mColors[i]); 

                path.reset();
                canvas.drawOval(rect[i], mPaint);
                path.addOval(rect[i], Direction.CCW);

                //写字
                mPaint.setTextSize(16*unit);
                mPaint.setStrokeWidth(2*unit );
                mPaint.setStyle(Paint.Style.FILL);
                canvas.drawTextOnPath(str[i], path, 189, -35, mPaint);
            }


            //写字,要移动,且字体逐渐变小
            if(flagShoot==true){

                if(timerStatus==0){
                    startCountTimer();
                }

                //画出子弹的路线,透明的
                path.reset();
                path.moveTo(startPoint.x, startPoint.y);
                path.lineTo(targetPoint.x, targetPoint.y);
                mPaint.setColor(Color.TRANSPARENT);
                canvas.drawPath(path, mPaint);

                PathMeasure pathMeasure = new PathMeasure(path, false);
                float length = pathMeasure.getLength();


                //LogUtil.logWithMethod(new Exception(),"progressValue="+progressValue+" progressValueShootRecord="+progressValueShootRecord);
                float hOffset=(progressValue-progressValueShootRecord)*26;
                int textSizeUse=(int)((textSize-(progressValue-progressValueShootRecord)/3)*unit);

                if(hOffset>=length){
                    flagShoot = false;
                }

                mPaint.setColor(Color.RED);
                mPaint.setStrokeWidth(2*unit );
                mPaint.setStyle(Paint.Style.FILL);              
                mPaint.setTextSize(textSizeUse);
                canvas.drawTextOnPath(strShoot, path, hOffset, textSizeUse*2/5, mPaint);//调整子弹与炮筒的位置匹配


                FontMetrics fm = mPaint.getFontMetrics();
                float len=hOffset+(fm.bottom-fm.top);

                float[] pos= new float[2];
                float[] tan= new float[2];  
                pathMeasure.getPosTan(len, pos, tan);//根据沿path的长度求坐标

                if((pos[1]>=rect[i].top)&&(pos[1]<=rect[i].bottom)){
//                      LogUtil.logWithMethod(new Exception(),"y is in rect1");
                    if((pos[0]>=rect[i].left)&&(pos[0]<=rect[i].right)){
                        LogUtil.logWithMethod(new Exception(),"and x is in rect1");
                        shootIn[i]=i;
                        //记录击中时刻的rect值
                        rectRecord[i].set(rect[i]);
                        progressValueRecord[i] = progressValue;

                        if(timerStatus==1){
                            score++;
                        }

                        rect[i].set(0,screenHeight+200,0,screenHeight+300);//避免再次被击中,尽管看不见了
                    }
                }                   

                //计算炮筒的方向,要与子弹路线一致
                pathMeasure.getPosTan(50, pos, tan);//根据沿path的长度求坐标
                pointBarrel.x=pos[0];
                pointBarrel.y=pos[1];               
            }


            //显示击中的效果               
            if(shootIn[i]>=0){
                //LogUtil.logWithMethod(new Exception(),"progressValueRecord[i]="+progressValueRecord[i]+" progressValue="+progressValue);

                mPaint.setColor(mColors[i]);//(Color.RED);
                mPaint.setStrokeWidth(3*unit );
                mPaint.setStyle(Style.STROKE);              

                if((progressValue-progressValueRecord[i])<3){
                    //画椭圆:暂时停留一下
                    canvas.drawOval(rectRecord[i], mPaint);
                    path.addOval(rectRecord[i], Direction.CCW);
                }
                else if( (progressValue-progressValueRecord[i])<30){
                    //画椭圆的分段的圆弧,半径逐渐变大,模拟爆炸的效果
                    RectF rect2=new RectF();
                    float delta=(progressValue-progressValueRecord[i])/1;
                    rect2.set(rectRecord[i].left-delta, rectRecord[i].top-delta, rectRecord[i].right+delta, rectRecord[i].bottom+delta);
                    for(int j=0;j<12;j++){
                        float startAngle=j*30;
                        float sweepAngle = 20-delta;
                        if(sweepAngle>0){
                            canvas.drawArc(rect2, startAngle, sweepAngle, false, mPaint);                           
                        }
//                          LogUtil.logWithMethod(new Exception(),"drawArc startAngle="+startAngle+" sweepAngle="+sweepAngle);
                    }
                }else if( (progressValue-progressValueRecord[i])>40){
                    setBalloonRectF(i,mWidth,mHeight);
                    LogUtil.logWithMethod(new Exception(),"i="+i+" mHeight="+mHeight+" screenHeight="+screenHeight);
                    shootIn[i]=-1;
                    progressValueRecord[i]=0;

                }               
            }               
        }


        //画出炮筒,要与子弹路线一致
        mPaint.setColor(Color.BLACK);//(Color.RED);
        mPaint.setStrokeWidth(8*unit );
        if(pointBarrel.x==0){//若还没有射击过,设置默认炮筒方向
            pointBarrel.x=mWidth/2;
            pointBarrel.y=mHeight-50;
        }           
        canvas.drawLine(mWidth/2, mHeight, pointBarrel.x, pointBarrel.y, mPaint);


        //画出炮台,画一次就行了
        mPaint.setAntiAlias(true);
        mPaint.setStrokeWidth((float) lineWidth );
        mPaint.setStrokeCap(Cap.ROUND);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(Color.BLACK);
        RectF rectBattery = new RectF(mWidth/2-40,mHeight-40,mWidth/2+40,mHeight+40);
        canvas.drawArc(rectBattery, 180, 180, true, mPaint);


        //显示倒计时时间及分数:
        mPaint.setTextSize(16*unit);
        mPaint.setColor(Color.BLACK);//(Color.RED);
        mPaint.setStrokeWidth(8*unit );
        canvas.drawText(strTimeRest, 20, mHeight-90, mPaint);
        mPaint.setColor(Color.RED);
        canvas.drawText("得分:"+score, 20, mHeight-40, mPaint);

    }

    //设置气球外接矩形的位置
    private void setBalloonRectF(int i, float mWidth, float mHeight) {      
        rect[i]=new RectF(mWidth*(i+(float)0.5)/(NUM_BALLOON+1), mHeight-300, 
                          mWidth*(i+(float)0.5)/(NUM_BALLOON+1)+75, mHeight-200);
    }

    //手指触摸屏幕
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // 获取手指的位置
        float x = event.getX();
        float y = event.getY();

        if (flagShoot == false) {

            targetPoint.set(x, y);// 把点击的位置记录下来
            textSize = 16;
            progressValueShootRecord = progressValue;
            flagShoot = true;
        }

        // 通知视图重绘制、
        invalidate();
        return true;
    }

}

五、主Activity

前面介绍了4个自定义View,如何展示呢?这里使用了ViewPager来进行管理。具体ViewPager的使用,本文就不进行说明了,请参考:
http://blog.youkuaiyun.com/harvic880925/article/details/38557517

主Activity中除了ViewPager,主要就是一个定时器的使用,来推进各个View中画面的变化。
具体实现如下:

package com.customview;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Parcelable;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import com.customview.view.FerrisWheelView;
import com.customview.view.ShootingView;
import com.customview.view.SlidingView;
import com.customview.view.SurfingView;
import android.app.Activity;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.Matrix;

public class MainActivity extends Activity
{

    FerrisWheelView ferrisWheelView;//摩天轮的View
    SlidingView slidingView;//滑梯的View
    SurfingView surfingView;//冲浪的View
    ShootingView shootingView;//射击的View

    int progressValue=0;//进度推进值 

    private ViewPager viewPager;  //对应的viewPager 
    LayoutInflater lf;
    View        view1,view2,view3,view4;
    ArrayList<View>       viewList;

    private ImageView cursor;// 动画图片
    private TextView t1, t2, t3, t4;// 页卡头标
    private int offset = 0;// 动画图片偏移量
    private int currIndex = 0;// 当前页卡编号
    private int bmpW;// 动画图片宽度


    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState); 
        this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);//去掉信息栏
        setContentView(R.layout.activity_main);

        InitImageView();
        InitTextView();
        InitViewPager();

        timer.schedule(task, 1000, 50); // 1s后执行task,经过50ms再次执行    

    }

    Handler handler = new Handler() {  
        public void handleMessage(Message msg) {  
            if (msg.what == 1) {  
//              LogUtil.logWithMethod(new Exception(),"handler : progressValue="+progressValue+" index="+index);

                //通知view,进度值有变化
                if(currIndex==0){
                    ferrisWheelView.setProgressValue(progressValue);
                    ferrisWheelView.postInvalidate();                   
                }
                if(currIndex==1){
                    slidingView.setProgressValue(progressValue);
                    slidingView.postInvalidate();
                }
                if(currIndex==2){
                    surfingView.setProgressValue(progressValue);
                    surfingView.postInvalidate();
                }
                if(currIndex==3){
                    shootingView.setProgressValue(progressValue);
                    shootingView.postInvalidate();
                }

                progressValue+=1;

            }  
            super.handleMessage(msg);              
        };  
    };  

    Timer timer = new Timer();  
    TimerTask task = new TimerTask() {  

        @Override  
        public void run() {  
            // 需要做的事:发送消息  
            Message message = new Message();  
            message.what = 1;  
            handler.sendMessage(message);  
        }  
    };  


    /**
     * 初始化头标
     */
    private void InitTextView() {
        t1 = (TextView) findViewById(R.id.text1);
        t2 = (TextView) findViewById(R.id.text2);
        t3 = (TextView) findViewById(R.id.text3);
        t4 = (TextView) findViewById(R.id.text4);

        t1.setOnClickListener(new MyOnClickListener(0));
        t2.setOnClickListener(new MyOnClickListener(1));
        t3.setOnClickListener(new MyOnClickListener(2));
        t4.setOnClickListener(new MyOnClickListener(3));
    }

    /**
     * 初始化ViewPager
     */
    private void InitViewPager() {


        viewPager = (ViewPager) findViewById(R.id.vp_customView); 
        lf = getLayoutInflater().from(this);

        view1 = lf.inflate(R.layout.ferris_wheel_view, null);
        view2 = lf.inflate(R.layout.sliding_view, null);  
        view3 = lf.inflate(R.layout.surfing_view, null);  
        view4 = lf.inflate(R.layout.shooting_view, null);

        viewList = new ArrayList<View>();// 将要分页显示的View装入数组中

        viewList.add(view1);  
        viewList.add(view2);  
        viewList.add(view3);  
        viewList.add(view4);

        viewPager.setAdapter(new MyPagerAdapter(viewList));
        viewPager.setCurrentItem(0);
        viewPager.setOnPageChangeListener(new MyOnPageChangeListener());


        ferrisWheelView = (FerrisWheelView)(view1.findViewById(R.id.ferrisWheelView));      
        slidingView = (SlidingView)(view2.findViewById(R.id.slidingView));
        surfingView = (SurfingView)(view3.findViewById(R.id.surfingView));
        shootingView = (ShootingView)(view4.findViewById(R.id.shootingView));

    }

    /**
     * 初始化动画
     */
    private void InitImageView() {
        cursor = (ImageView) findViewById(R.id.cursor);
        bmpW = BitmapFactory.decodeResource(getResources(), R.drawable.b)
                .getWidth();// 获取图片宽度
        DisplayMetrics dm = new DisplayMetrics();
        LogUtil.logWithMethod(new Exception(),"bmpW="+bmpW);
        getWindowManager().getDefaultDisplay().getMetrics(dm);

        int screenW = dm.widthPixels;// 获取分辨率宽度
//      LogUtil.logWithMethod(new Exception(),"bmpW="+bmpW+" viewList.size()="+viewList.size());
        offset = (screenW / 4 - bmpW) / 2;// 计算偏移量
        LogUtil.logWithMethod(new Exception(),"offset="+offset);

        Matrix matrix = new Matrix();
        matrix.postTranslate(offset, 0);
        cursor.setImageMatrix(matrix);// 设置动画初始位置

    }

    /**
     * ViewPager适配器
     */
    public class MyPagerAdapter extends PagerAdapter {
        public List<View> mListViews;

        public MyPagerAdapter(List<View> mListViews) {
            this.mListViews = mListViews;
        }

        @Override
        public void destroyItem(View arg0, int arg1, Object arg2) {
            ((ViewPager) arg0).removeView(mListViews.get(arg1));
        }

        @Override
        public void finishUpdate(View arg0) {
        }

        @Override
        public int getCount() {
            return mListViews.size();
        }

        @Override
        public Object instantiateItem(View arg0, int arg1) {
            ((ViewPager) arg0).addView(mListViews.get(arg1), 0);
            return mListViews.get(arg1);
        }

        @Override
        public boolean isViewFromObject(View arg0, Object arg1) {
            return arg0 == (arg1);
        }

        @Override
        public void restoreState(Parcelable arg0, ClassLoader arg1) {
        }

        @Override
        public Parcelable saveState() {
            return null;
        }

        @Override
        public void startUpdate(View arg0) {
        }
    }


    /**
     * 头标点击监听
     */
    public class MyOnClickListener implements View.OnClickListener {

        private int index = 0;

        public MyOnClickListener(int i) {
            index = i;
//          LogUtil.logWithMethod(new Exception(),"index="+i);
        }

        @Override
        public void onClick(View v) {
            viewPager.setCurrentItem(index);
        }
    };

    /**
     * 页卡切换监听
     */
    public class MyOnPageChangeListener implements OnPageChangeListener {

        int width = offset * 2 + bmpW;
        int one = width ;// 页卡1 -> 页卡2 偏移量
        int two = width * 2;// 页卡1 -> 页卡3 偏移量
        int three = width * 3;// 页卡1 -> 页卡3 偏移量

        @Override
        public void onPageSelected(int arg0) {
            Animation animation = null;
            LogUtil.logWithMethod(new Exception(),"arg0="+arg0);
            switch (arg0) {
            case 0:
                if (currIndex == 1) {
                    animation = new TranslateAnimation(one, 0, 0, 0);
                } else if (currIndex == 2) {
                    animation = new TranslateAnimation(two, 0, 0, 0);
                } else if (currIndex == 3) {
                    animation = new TranslateAnimation(three, 0, 0, 0);
                }
                break;
            case 1:
                if (currIndex == 0) {
                    animation = new TranslateAnimation(0, one, 0, 0);
                } else if (currIndex == 2) {
                    animation = new TranslateAnimation(two, one, 0, 0);
                } else if (currIndex == 3) {
                    animation = new TranslateAnimation(three, one, 0, 0);
                }
                break;
            case 2:
                if (currIndex == 0) {
                    animation = new TranslateAnimation(0, two, 0, 0);
                } else if (currIndex == 1) {
                    animation = new TranslateAnimation(one, two, 0, 0);
                } else if (currIndex == 3) {
                    animation = new TranslateAnimation(three, two, 0, 0);
                }
                break;
            case 3:
                if (currIndex == 0) {
                    animation = new TranslateAnimation(0, three, 0, 0);
                } else if (currIndex == 1) {
                    animation = new TranslateAnimation(one, three, 0, 0);
                } else if (currIndex == 2) {
                    animation = new TranslateAnimation(two, three, 0, 0);
                }       
                break;
            }
            currIndex = arg0;           
            animation.setFillAfter(true);// True:图片停在动画结束位置
            animation.setDuration(200);
            cursor.startAnimation(animation);


            //重置进度值
            progressValue=0;

            if(currIndex==3){
                shootingView.resetShootingStatus();//切换到射击时,要多做一些初始化工作
            }

        }

        @Override
        public void onPageScrolled(int arg0, float arg1, int arg2) {
        }

        @Override
        public void onPageScrollStateChanged(int arg0) {
        }
    }

}

主Activity对应的布局文件如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:custom="http://schemas.android.com/apk/res/com.customview"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >


    <LinearLayout
        android:id="@+id/linearLayout1"
        android:layout_width="fill_parent"
        android:layout_height="40.0dip"
        android:background="#ffffffff" >

        <TextView
            android:id="@+id/text1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1.0"
            android:gravity="center"
            android:text="摩天轮"
            android:textColor="#ff0000ff"
            android:textSize="24.0dip" />

        <TextView
            android:id="@+id/text2"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1.0"
            android:gravity="center"
            android:text="滑梯"
            android:textColor="#ffff0000"
            android:textSize="24.0dip" />

        <TextView
            android:id="@+id/text3"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1.0"
            android:gravity="center"
            android:text="冲浪"
            android:textColor="#ff00ff00"
            android:textSize="24.0dip" />
        <TextView
            android:id="@+id/text4"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1.0"
            android:gravity="center"
            android:text="射击"
            android:textColor="#ff000000"
            android:textSize="24.0dip" />
    </LinearLayout>

    <ImageView
        android:id="@+id/cursor"
        android:layout_width="fill_parent"
        android:layout_height="5dp"
        android:scaleType="matrix"
        android:src="@drawable/b" />

    <android.support.v4.view.ViewPager
        android:id="@+id/vp_customView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_weight="1.0"
        android:background="#cffccc"
        android:flipInterval="30"
        android:persistentDrawingCache="animation" >
    </android.support.v4.view.ViewPager>

</LinearLayout>

源代码地址:

http://download.youkuaiyun.com/detail/lintax/9646000

参考:

http://blog.youkuaiyun.com/u013831257/article/details/50784565
http://blog.youkuaiyun.com/lovejjfg/article/details/51707577
http://blog.youkuaiyun.com/harvic880925/article/details/38557517

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值