自定義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.csdn.net/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.csdn.net/detail/lintax/9646000

參考:

http://blog.csdn.net/u013831257/article/details/50784565
http://blog.csdn.net/lovejjfg/article/details/51707577
http://blog.csdn.net/harvic880925/article/details/38557517

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章