四、射击(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