个人纯手写纯原生的安卓贪吃蛇,原理是recycview的局部刷新和通过变量判断移动方向,运用到了一些算法原理和技巧,感兴趣的可以查看源码
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.SimpleItemAnimator;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.GestureDetector;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.Timer;
public class MainActivity extends AppCompatActivity {
private RecyclerView recycler;
private TextView score;
private TextView duration;
private SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
private int forward; // 下0、左1、上2、右3
private int dx[] = {0, -1, 0, 1};
private int dy[] = {1, 0, -1, 0};
public static int width = 32;
private List<Site> path = new ArrayList<>();
private Site food;
private GestureDetector.OnGestureListener gesture;
private GestureDetector detector;
private boolean gameOver = false;
private Handler handler;
Random random = new Random();
private GameAdapter gameAdapter;
int time = 0, fenshu = 0;
private Handler timer;
private Site changes[] = new Site[5];
private int speed;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initGesture();
initGame();
initTime();
startMove(); /*核心*/
}
private void initTime() {
timer = new Handler(Looper.myLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message message) {
if(!gameOver) {
addTime();
timer.sendEmptyMessageDelayed(1, 1000);
}
return false;
}
});
timer.sendEmptyMessageDelayed(1, 1000);
}
private void startMove() {
handler = new Handler(Looper.myLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message message) {
if(!gameOver){
/*新走到的位置*/
Site end = path.get(path.size() - 1);
Site site = new Site(end.x + dx[forward], end.y + dy[forward]);
site = offset(site);
if(path.contains(site)){ // 是否咬到自己
path.add(site);
gameAdapter.notifyDataSetChanged();
stopGame();
return false;
}
changes[0] = site;
changes[1] = end;
changes[2] = path.get(0);
changes[3] = food;
path.add(site); // 重载食物,去掉尾巴
if(!site.equals(food)){
path.remove(0);
}else {
fenshu ++;
score.setText("Score:"+fenshu);
while(path.contains(food)) food = new Site(random.nextInt(width * width));
}
for (int i = 0; i < 5; i++) {
gameAdapter.notifyItemChanged(changes[i].getPosition(), null);
}
// gameAdapter.notifyDataSetChanged(); // 只需要更新旧头,新头site、尾path[0]、旧的食物food(既新头)、新的食物while+food
handler.sendEmptyMessageDelayed(0, speed);
}
return false;
}
});
gameAdapter = new GameAdapter();
recycler.setAdapter(gameAdapter);
handler.sendEmptyMessageDelayed(0, 1000);
}
private void addTime() {
time ++;
Date date = new Date();
date.setTime(16*60*60*1000L + time * 1000);
String format = sdf.format(date);
duration.setText("Duration:"+format);
}
private void stopGame() {
gameOver=true;
saveScore();
new AlertDialog.Builder(this).setTitle("Game Over").setMessage("请选择重新开始或返回首页")
.setPositiveButton("Play again", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
startActivity(new Intent(MainActivity.this, MainActivity.class));
finish();
}
}).setNegativeButton("Back to home", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
finish();
}
}).setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialogInterface) {
startActivity(new Intent(MainActivity.this, MainActivity.class));
finish();
}
}).show();
}
private void saveScore() {
SharedPreferences sp = getSharedPreferences("user", MODE_PRIVATE);
try {
JSONArray jsonArray = new JSONArray(sp.getString("data", ""));
JSONObject obj = new JSONObject();
obj.put("score", fenshu);
obj.put("duration", time);
jsonArray.put(obj);
sp.edit().putString("data", jsonArray.toString()).apply();
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
private void initGame() {
SharedPreferences sp = getSharedPreferences("user", MODE_PRIVATE);
int wid = Integer.parseInt(sp.getString("width", "4"));
path.add(new Site(random.nextInt(width * width)));
forward = random.nextInt(4);
for (int i = 0; i < wid; i++) {
Site end = path.get(path.size() - 1);
Site site = new Site(end.x + dx[forward], end.y + dy[forward]);
site = offset(site);
path.add(site);
}
food = new Site(random.nextInt(width * width));
while(path.contains(food)) food = new Site(random.nextInt(width * width));
}
private Site offset(Site site) {
site.x = site.x % width;
site.y = site.y % width;
if (site.x < 0) {
site.x += width;
}
if (site.y < 0) {
site.y += width;
}
return site;
};
private void initView() {
recycler = findViewById(R.id.recyc);
((SimpleItemAnimator)recycler.getItemAnimator()).setSupportsChangeAnimations(false); // 不显示itemChange的闪烁动画
recycler.setLayoutManager(new GridLayoutManager(this, width){
@Override
public boolean canScrollVertically() {
return false;
}
});
score = findViewById(R.id.score);
duration = findViewById(R.id.duration);
SharedPreferences sp = getSharedPreferences("user", MODE_PRIVATE);
speed = Integer.parseInt(sp.getString("speed", 600+""));
}
private void initGesture() {
gesture = new GestureDetector.SimpleOnGestureListener(){
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
float offset = getResources().getDisplayMetrics().density * 50; // 50dp
// float offset = 30;
if(e1.getX() - e2.getX() > offset){ // 左滑
// "不能往反方向滑动",
if(forward != 3){ forward = 1; }
}else if(e2.getX() - e1.getX() > offset){ // 右滑
if(forward != 1){ forward = 3; }
}else if(e2.getY() - e1.getY() > offset){ // 下滑
if(forward != 2){ forward = 0; }
}else if(e1.getY() - e2.getY() > offset){ // 上滑
if(forward != 0){ forward = 2; }
}
return super.onFling(e1, e2, velocityX, velocityY);
}
};
detector = new GestureDetector(this, gesture);
recycler.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
detector.onTouchEvent(motionEvent);
return false;
}
});
}
public void back(View view) {
finish();
}
public void move(View view) {
switch (view.getId()){
case R.id.up:
if(forward != 0){ forward = 2; }
break;
case R.id.left:
if(forward != 3){ forward = 1; }
break;
case R.id.right:
if(forward != 1){ forward = 3; }
break;
case R.id.down:
if(forward != 2){ forward = 0; }
break;
}
}
class GameAdapter extends RecyclerView.Adapter<GameAdapter.MyViewHolder>{
private ColorDrawable colors[] = {
new ColorDrawable(getResources().getColor(R.color.body)),
new ColorDrawable(Color.TRANSPARENT)
};
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new MyViewHolder(LayoutInflater.from(MainActivity.this).inflate(R.layout.item_game, parent, false));
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
Site site = new Site(position);
if(path.contains(site)){
holder.itemView.setBackground(colors[0]);
if(path.get(path.size()-1).equals(site)) holder.itemView.setBackground(getResources().getDrawable(R.drawable.baseline_face_24));
}else if(food.equals(site)){
holder.itemView.setBackground(getResources().getDrawable(R.drawable.baseline_fastfood_24));
}else{
holder.itemView.setBackground(colors[1]);
}
}
@Override
public int getItemCount() {
return width * width;
}
class MyViewHolder extends RecyclerView.ViewHolder{
public MyViewHolder(@NonNull View itemView) {
super(itemView);
}
}
}
}