package com.my2048;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;
/**
2048简单实现的小游戏 —> 在布局里的引用下就可以了
*/
public class My2048View extends View {
//TODO 先画出一个整体的视图出来 --> 整体的一个图案画出来
//TODO 创建画笔,缓存一个数组,记录数字以便下一次计算
//TODO 创建四个方向的计算算法 上下左右不同的移动的计算(最核心的) ---> 关联手势
//TODO 绘制
//TODO 这里主要使用了一个二维数组进行对方格的数字管理,进行一个变化
//TODO 例如:进行一个右移,我对二维数组的第一个以为数组进行处理 ---> 即判断数是否要相加
//这里没有做回退上一步的功能,你可以用一个ArrayList储存 每一步的 数组arr就可以
Paint strokePaint; //画线的笔
Paint textPaint; //画数字的笔
int width; //记录屏幕容器给我分配的宽
int height; //记录屏幕容器分配的高
int gridWidth; //格子的宽高 --->一般为屏幕宽度的四分之一
int[][] arr = new int[4][4]; //对格子的数进行对应
int textSize = 50; //数字的大小,这里没采用sp,最好可以采用sp单位
float textHeight; //记录数字大小文本的高度
public My2048View(Context context) {
this(context, null);
}
public My2048View(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public My2048View(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(); //初始化一些配置
}
private void init() { //初始化一些配置
strokePaint = new Paint();
strokePaint.setAntiAlias(true); //设置是否使用抗锯齿功能,会消耗较大资源,绘制图形速度会变慢
strokePaint.setColor(Color.GRAY); //设置颜色为灰色
strokePaint.setStrokeCap(Paint.Cap.ROUND);//设置线开始和结束点为圆弧样式
strokePaint.setStyle(Paint.Style.STROKE); //设置为线条样式
textPaint = new Paint();
textPaint.setAntiAlias(true); //设置是否使用抗锯齿功能,会消耗较大资源,绘制图形速度会变慢
textPaint.setColor(Color.BLUE); //设置颜色为蓝色
textPaint.setStyle(Paint.Style.STROKE); //设置线条样式
textPaint.setTextSize(textSize); //最好可以转成sp
textPaint.setFakeBoldText(true);//设置文本为粗体
textPaint.setTextAlign(Paint.Align.CENTER);//设置为画文字的x轴为中心点,y轴向下扩展 所以后面y轴向下点才可以
Rect rect=new Rect();
textPaint.getTextBounds(arr[0][0]+"", 0, 1, rect);
textHeight = rect.height();//文本的高度
randomNumber();//随机位置生成数
}
@Override//布局
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
}
@Override//测量
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
int startX;
int startY;
int endX;
int endY;
@Override//测量之后调用的方法 ----> 一般在这里得到view的宽高
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
width = w; //屏幕分配给我的width
height = h; //屏幕分配给我的height
gridWidth = w / 4; //设置为宽度的四分之一,可以自己更改
startX = (width - gridWidth * 4) / 2; //记录我开始画框的坐标点 -->X轴
startY = 0; //记录我开始画框的坐标点 -->Y轴
endX = w - startX; //记录我结束画框的坐标点 -->X轴
endY = 4 * gridWidth; //记录我结束画框的坐标点 -->Y轴
}
@Override//绘制
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.parseColor("#FFFFFFAA")); //绘制背景
drawStroke(canvas); //绘制线条,整体的一个框架
drawText(canvas); //绘制格子中间的数字变化
}
private void drawStroke(Canvas canvas) {
for (int i = 0; i < 5; i++) {
canvas.drawLine(startX + i * gridWidth, startY, startX + i * gridWidth, endY, strokePaint);//画横线
canvas.drawLine(startX, startY + i * gridWidth, endX, startY + i * gridWidth, strokePaint);//画竖线
}
}
private void drawText(Canvas canvas) { //画数字
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (arr[i][j]!=0)
canvas.drawText(arr[i][j]+"", startX +gridWidth/2+gridWidth*j, startY +gridWidth/2+gridWidth*i+textHeight/2,textPaint);
}
}
}
float downX,downY,moveX,moveY; // 记录手势点下/移动的坐标
@Override
public boolean onTouchEvent(MotionEvent event) { //这里的处理不怎么好 ---> 你可能点动一下就有反应了
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX =event.getX();
downY =event.getY();
break;
case MotionEvent.ACTION_MOVE:
moveX=event.getX();
moveY =event.getY();
break;
case MotionEvent.ACTION_UP:
float v = Math.abs(downX - moveX) - Math.abs(downY - moveY); //手势判断用户向哪里移动
if(v > 0) {
if(downX > moveX) {
left(); //左移
}else {
right();//右移
}
}else{
if(downY > moveY) {
up(); //向上移动
}else {
down();//向下移动
}
}
downX=0;downY=0;moveX=0;moveY=0;
randomNumber(); //随机位置生数
break;
}
invalidate(); //重绘制
return true;
}
Random r = new Random(); //生成随机数的类
int[] value = new int[2];
HashMap hashMap = new HashMap<>();
private void randomNumber() { //随机位置生成一个2
hashMap.clear();
int count = 0;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (0==arr[i][j]) {
value=new int[2];//一定要不同的对象,不然会有问题
value[0]=i;
value[1]=j;
hashMap.put(count,value); //记录可以随机生成的坐标点 的集合
count++;
}
}
}
if (hashMap.size() > 0) { //随机选一个坐标点生成数
int i = r.nextInt(hashMap.size());//生成的数包头不包尾
int[] ints = hashMap.get(i);
arr[ints[0]][ints[1]]=2; //生成一个数为2
} else { //做了个简单的提示
//判断是不是输了 --- 可以自己做的更好点 --->可以缓存上一步数,看是否可以移动
Toast.makeText(getContext(),"你要输了哦",Toast.LENGTH_SHORT).show();
}
}
ArrayList myData = new ArrayList<>(); //缓存的集合
Integer filed;
private void down() { //向下移动实现
//返回一个装满不是0数字的集合
for (int i = 0; i < 4; i++) {
myData.clear(); //清除以前的数据
for (int j = 0; j < 4; j++) { //遍历竖的格子里的数
if (arr[j][i]!=0) { //如果不是0,在这竖的格子里要进行一个重新赋值
myData.add(arr[j][i]);
arr[j][i]=0; //把以前记录的数给清除,然后后面 进行一个重新赋值
}
}
getFinalNumber2(myData); //对数据是否要相加要处理
setNumberDown(i, myData); //数据处理之后进行一个重新赋值
}
}
private void up() { //向上移动实现
for (int i = 0; i < 4; i++) {
myData.clear();
for (int j = 0; j < 4; j++) {
if (arr[j][i]!=0) {
myData.add(arr[j][i]);
arr[j][i]=0;
}
}
getFinalNumber(myData);
setNumberUp(i, myData);
}
}
private void right() { //向右滑
for (int i = 0; i < 4; i++) {
myData.clear();
for (int j = 0; j < 4; j++) {
if (arr[i][j]!=0) {
myData.add(arr[i][j]);
arr[i][j]=0;
}
}
getFinalNumber2(myData);//
setNumberRight(i, myData);
}
}
private void left() { //向左滑
for (int i = 0; i < 4; i++) {
myData.clear();
for (int j = 0; j < 4; j++) {
if (arr[i][j]!=0) {
myData.add(arr[i][j]);
arr[i][j]=0;
}
}
getFinalNumber(myData);
setNumberLeft(i,myData);
}
}
private void setNumberDown(int i, ArrayList myData) {
// 重新赋值
int size = myData.size();
for (int j = 0; j < size; j++) {
arr[4-1-j][i] = myData.get(size-1-j);
}
}
private void setNumberUp(int i, ArrayList myData) {
// 重新赋值
int size = myData.size();
for (int j = 0; j < size; j++) {
arr[j][i]=myData.get(j);
}
}
private void setNumberRight(int i, ArrayList myData) {
int size = myData.size(); //重新赋值
for (int j = 0; j < size; j++) {
arr[i][4-j-1] = myData.get(size-j-1);
}
}
private void setNumberLeft(int i, ArrayList myData) {
// 重新赋值
int size = myData.size();
for (int j = 0; j < size; j++) {
arr[i][j] = myData.get(j);
}
}
private void getFinalNumber(ArrayList myData) {
for (int i = 0; i < myData.size() - 1; i++) {
filed = myData.get(i);
if (filed.equals(myData.get(i + 1))) {//是Integer对象不能用==相比较,当integer对象小于128可以用==号相比较,因为小于128是取常量池里的
myData.set(i, filed * 2);
myData.remove(i + 1);
getFinalNumber(myData);
}
}
}
private void getFinalNumber2(ArrayList myData) {
int mySize = myData.size() - 1;//求出集合的size
for (int i = mySize; i > 0; --i) {
filed = myData.get(i);
if (filed.equals(myData.get(i - 1))) {//是Integer对象不能用==相比较,当integer对象小于128可以用==号相比较,因为小于128是取常量池里的
myData.set(i - 1, filed * 2);
myData.remove(i);
getFinalNumber2(myData);
break;
}
}
}
}
//这是一个自定义的View,你直接在layout文件中直接引用就可以了,也可修改这个view的代码实现自己的要求,这个只是简单实现功能