Android实例——拼图游戏

拼图游戏

  • 项目简介
  • 权限
  • adapter
    • PictureListAdapter
    • PuzzleAdapter
  • bean
    • ItemBean
  • Presenter
    • IPuzzlePresenter
    • PuzzlePresenterImpl
  • ui
    • IGameCallback
  • utils
    • Constant
    • ImagesUtils
    • ScreenUtils
  • View
    • MainActivity
    • PuzzleActivity
  • 布局
    • activity_main.xml
    • activity_puzzle.xml

项目简介

选择图片,生成拼图,通过移动拼图还原图片通关游戏,选择界面如下

在这里插入图片描述

游戏界面如下

在这里插入图片描述

采用MVP架构,项目结构如下

在这里插入图片描述

权限

需要读取相册中的图片

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

adapter

PictureListAdapter

public class PictureListAdapter extends BaseAdapter {

    private Context mContext;
    private List<Bitmap> mBitmapList;

    public PictureListAdapter(Context context, List<Bitmap> bitmapList) {
        mContext = context;
        mBitmapList = bitmapList;
    }

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

    @Override
    public Object getItem(int position) {
        return mBitmapList.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ImageView item = new ImageView(mContext);
        item.setImageBitmap(mBitmapList.get(position));
        return item;
    }
}

PuzzleAdapter

public class PuzzleAdapter extends BaseAdapter {

    private List<ItemBean> mItemBeanList;

    public PuzzleAdapter() {
        mItemBeanList = new ArrayList<>();
    }

    public void setData(List<ItemBean> itemBeanList) {
        mItemBeanList.clear();
        for (ItemBean itemBean : itemBeanList) {
            try {
                mItemBeanList.add((ItemBean) itemBean.clone());
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
        }
    }

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

    @Override
    public Object getItem(int position) {
        return mItemBeanList.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ImageView item = new ImageView(parent.getContext());
        /*item.setLayoutParams(new GridView.LayoutParams(GridView.LayoutParams.MATCH_PARENT, GridView.LayoutParams.MATCH_PARENT));
        item.setScaleType(ImageView.ScaleType.FIT_XY);*/
        item.setImageBitmap(mItemBeanList.get(position).getBitmap());
        return item;
    }
}

bean

ItemBean

public class ItemBean implements Cloneable {
    private int mOriID;	//表示拼图原始顺序
    private int mPuzzleID;	//表示拼图打乱后的顺序
    private Bitmap mBitmap;

    public ItemBean() {

    }

    public ItemBean(int oriID, int puzzleID, Bitmap bitmap) {
        mOriID = oriID;
        mPuzzleID = puzzleID;
        mBitmap = bitmap;
    }

    public int getOriID() {
        return mOriID;
    }

    public void setOriID(int oriID) {
        mOriID = oriID;
    }

    public int getPuzzleID() {
        return mPuzzleID;
    }

    public void setPuzzleID(int puzzleID) {
        mPuzzleID = puzzleID;
    }

    public Bitmap getBitmap() {
        return mBitmap;
    }

    public void setBitmap(Bitmap bitmap) {
        mBitmap = bitmap;
    }

    @Override
    public String toString() {
        return "ItemBean{" +
                "mOriID=" + mOriID +
                ", mPuzzleID=" + mPuzzleID +
                ", mBitmap=" + mBitmap +
                '}';
    }

    @NonNull
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

Presenter

IPuzzlePresenter

public interface IPuzzlePresenter {

    enum STATE {
        SUCCESS, FAILED_STEP, FAILED_TIME
    }

    void registerGameCallback(IGameCallback gameControl);

    void unRegisterGameCallback();

	//生成拼图数据
    void loadPuzzleData(Context context, Bitmap oriBitmap);
	
	//复位拼图
    void resetGame();

	//交换拼图
    void swapPuzzle(int position);

	//生成新的游戏
    void restartGame();

}

PuzzlePresenterImpl

public class PuzzlePresenterImpl implements IPuzzlePresenter {

    private static final String TAG = "PuzzlePresenterImpl";
    private IGameCallback mCallback;
    private ItemBean mLastBitmap;
    private Bitmap mOriBitmap;
    private List<ItemBean> mPuzzleList;
    private List<ItemBean> mResetList;
    private int mType;

    private Map<Integer, Integer> mCountDownMap;
    private Map<Integer, Integer> mStepCountMap;

    private int mCurrentStep;
    private int mCurrentTime;
    private Timer mTimer;
    private TimerTask mTimerTask;
    
    public PuzzlePresenterImpl(int type) {
        mPuzzleList = new ArrayList<>();
        mResetList = new ArrayList<>();
        mType = type;

        mCountDownMap = new HashMap<>();
        mCountDownMap.put(2, 180);
        mCountDownMap.put(3, 300);
        mCountDownMap.put(4, 600);

        mStepCountMap = new HashMap<>();
        mStepCountMap.put(2, 10);
        mStepCountMap.put(3, 150);
        mStepCountMap.put(4, 500);
    }

    @Override
    public void registerGameCallback(IGameCallback callback) {
        this.mCallback = callback;
    }

    @Override
    public void unRegisterGameCallback() {
        mTimer.cancel();
        /*mPuzzleList.clear();
        mResetList.clear();*/
        this.mCallback = null;
    }

    @Override
    public void loadPuzzleData(Context context, Bitmap oriBitmap) {
        Log.d(TAG, "loadPuzzleData: ");
        initPuzzle(context, oriBitmap);
        startGame();
    }

    private void startGame() {
        shufflePuzzle();
        cloneItemBeanList(mPuzzleList, mResetList);
        initCountDown();
        if (mCallback != null) {
            mCallback.onLoadPuzzleData(mOriBitmap, mPuzzleList);
        }
        startCountDown();
    }

    private void cloneItemBeanList(List<ItemBean> from, List<ItemBean> to) {
        if (to != null && to.size() != 0) {
            to.clear();
        }
        for (ItemBean itemBean : from) {
            try {
                to.add((ItemBean) itemBean.clone());
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
        }
    }

    private void initCountDown() {
        mCurrentTime = mCountDownMap.get(mType);
        mCurrentStep = mStepCountMap.get(mType);
    }

    private void initPuzzle(Context context, Bitmap oriBitmap) {
        mOriBitmap = ImagesUtils.resizeBitmap(oriBitmap, ScreenUtils.getScreenWidthPixels(context), ScreenUtils.getScreenWidthPixels(context));
        int itemWidth = mOriBitmap.getWidth() / mType;
        int itemHeight = mOriBitmap.getHeight() / mType;
        for (int i = 1; i <= mType; i++) {
            for (int j = 1; j <= mType; j++) {
                Bitmap bitmap = Bitmap.createBitmap(
                        mOriBitmap,
                        (j - 1) * itemWidth,
                        (i - 1) * itemHeight,
                        itemWidth,
                        itemHeight);
                mPuzzleList.add(new ItemBean((i - 1) * mType + j, (i - 1) * mType + j, bitmap));
            }
        }
        //取出最后一个图片,当拼图完成时补充
        mLastBitmap = mPuzzleList.remove(mType * mType - 1);
        //将空的图片插入到最后
        mPuzzleList.add(new ItemBean(mType * mType, 0,
                ImagesUtils.createBitmap(
                        mOriBitmap.getWidth() / mType,
                        mOriBitmap.getHeight() / mType,
                        "空白格")));
    }

    private void startCountDown() {
        mTimer = new Timer(true);
        mTimerTask = new TimerTask() {
            @Override
            public void run() {
                Log.d(TAG, "run: Thread = " + Thread.currentThread().getId() + ", mCurrentTime = " + mCurrentTime);
                mCurrentTime--;
                if (mCallback != null) {
                    mCallback.onCountDown(mCurrentTime, mCurrentStep);
                    if (mCurrentTime == 0) {
                        finishGame(STATE.FAILED_TIME);
                    }
                }
            }
        };
        mTimer.schedule(mTimerTask, 0, 1000);
    }

    @Override
    public void resetGame() {
        for (ItemBean itemBean : mPuzzleList) {
            Log.d(TAG, "resetGame: mPuzzleList = " + itemBean);
        }
        for (ItemBean itemBean : mResetList) {
            Log.d(TAG, "resetGame: mResetList = " + itemBean);
        }
        Log.d(TAG, "resetGame: --------------");

        cloneItemBeanList(mResetList, mPuzzleList);
        initCountDown();
        if (mCallback != null) {
            mCallback.onResetGame(mPuzzleList);
        }

        for (ItemBean itemBean : mPuzzleList) {
            Log.d(TAG, "resetGame: mPuzzleList = " + itemBean);
        }
        for (ItemBean itemBean : mResetList) {
            Log.d(TAG, "resetGame: mResetList = " + itemBean);
        }
        Log.d(TAG, "resetGame: --------------");
    }

    @Override
    public void swapPuzzle(int position) {
        if (mCallback != null) {
            mCallback.onSwapPuzzle(isMovable(position), mPuzzleList);
            mCurrentStep--;
            mCallback.onCountDown(mCurrentTime, mCurrentStep);
            if (isSuccess()) {
                finishGame(STATE.SUCCESS);
            } else if (mCurrentStep == 0) {
                finishGame(STATE.FAILED_STEP);
            }
        }
    }

    private void finishGame(STATE state) {
        if (mTimer != null) {
            mTimer.cancel();
            mTimer = null;
            mTimerTask = null;
        }
        mCallback.onFinishGame(state);
    }

    @Override
    public void restartGame() {
        startGame();
    }

    private ItemBean getBlankBitmap() {
        for (ItemBean itemBean : mPuzzleList) {
            if (itemBean.getPuzzleID() == 0) {
                return itemBean;
            }
        }
        return null;
    }

    private boolean isMovable(int position) {
        int blankPosition = getBlankBitmap().getOriID() - 1;
        if ((Math.abs(blankPosition - position) == mType       //判断上下
                || ((blankPosition / mType == position / mType) && Math.abs(blankPosition - position) == 1))) { //判断左右
            swapItem(mPuzzleList.get(position), getBlankBitmap());
            return true;
        }
        return false;
    }

    private void swapItem(ItemBean from, ItemBean blankBitmap) {
        if (from == blankBitmap) {
            return;
        }
        ItemBean temp = new ItemBean();
        temp.setPuzzleID(from.getPuzzleID());
        temp.setBitmap(from.getBitmap());

        from.setPuzzleID(blankBitmap.getPuzzleID());
        from.setBitmap(blankBitmap.getBitmap());

        blankBitmap.setPuzzleID(temp.getPuzzleID());
        blankBitmap.setBitmap(temp.getBitmap());
    }


    private boolean isSuccess() {
        boolean isSuccess = true;
        for (ItemBean bean : mPuzzleList) {
            if (bean.getPuzzleID() != 0 && bean.getOriID() == bean.getPuzzleID()) { //非空白格,两个ID相等
                continue;
            } else if (bean.getPuzzleID() == 0 && bean.getOriID() == mType * mType) { //空白格,源ID等于最后一个
                continue;
            } else {
                isSuccess = false;
            }
        }
        return isSuccess;
    }

    private void shufflePuzzle() {
        for (int i = 0; i < mPuzzleList.size(); i++) {  //打乱顺序
            int index = (int) (Math.random() * mPuzzleList.size());
            swapItem(mPuzzleList.get(index), getBlankBitmap());
        }
        List<Integer> data = new ArrayList<>();
        for (int i = 0; i < mPuzzleList.size(); i++) {
            data.add(mPuzzleList.get(i).getPuzzleID());
        }
        if (!canSolve(data, getBlankBitmap().getOriID()) || isSuccess()) {
            shufflePuzzle();
        }
    }

    private boolean canSolve(List<Integer> data, int blankPosition) {
        // 个数为奇数时,倒置和为偶数才有解
        if (data.size() % 2 == 1) {
            return getInversions(data) % 2 == 0;
        } else {    //个数为偶数时
            if (((blankPosition - 1) / mType) % 2 == 1) {  //空格位于奇数行,倒置和为偶数才有解
                return getInversions(data) % 2 == 0;
            } else {    //空格位于偶数行,倒置和为奇数才有解
                return getInversions(data) % 2 == 1;
            }
        }
    }

    //计算比第i位元素小的元素个数的总和,如[3,2,1],倒置和为[2,1,0] = 3
    private int getInversions(List<Integer> data) {
        int inversions = 0;
        int inversionCount = 0;
        for (int i = 0; i < data.size(); i++) {
            for (int j = i + 1; j < data.size(); j++) {
                int index = data.get(i);
                if (data.get(j) != 0 && data.get(j) < index)
                    inversionCount++;
            }
            inversions += inversionCount;
            inversionCount = 0;
        }
        return inversions;
    }
}

ui

IGameCallback

public interface IGameCallback {

   //获取拼图数据
    void onLoadPuzzleData(Bitmap resizeOriBitmap, List<ItemBean> itemBeanList);

	//交换拼图
    void onSwapPuzzle(boolean isMovable, List<ItemBean> itemBeanList);

	//倒计时
    void onCountDown(int time, int step);

	//结束游戏
    void onFinishGame(IPuzzlePresenter.STATE state);

	//复位游戏
    void onResetGame(List<ItemBean> itemBeanList);
}

utils

Constant

public class Constant {
    public static final String ORIGINAL_BITMAP = "Original_Bitmap";
    public static final String PUZZLE_TYPE = "puzzle_type";
}

ImagesUtils

public class ImagesUtils {

	//生成带文字的图片
    public static Bitmap createBitmap(int bitmapWidth, int bitmapHeight, String text) {
        Bitmap bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        canvas.drawColor(Color.GRAY);

        if (!TextUtils.isEmpty(text)) {
            Paint textPaint = new Paint();
            textPaint.setColor(Color.BLACK);

            textPaint.setTextSize(bitmapWidth / text.length());
            float textWidth = textPaint.measureText(text, 0, text.length());   //用于水平居中
            Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
            float offset = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom; //用于垂直居中
            canvas.drawText(text, (bitmapWidth - textWidth) / 2f, bitmapWidth / 2f + offset, textPaint);
        }
        return bitmap;
    }

	//修改图片大小
    public static Bitmap resizeBitmap(Bitmap bitmap, float newWidth, float newHeight) {
        Matrix matrix = new Matrix();
        matrix.postScale(newWidth / bitmap.getWidth(), newHeight / bitmap.getHeight());
        return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
    }
}

ScreenUtils

public class ScreenUtils {

    public static DisplayMetrics getDisplayMetrics(Context context) {
        DisplayMetrics metrics = new DisplayMetrics();
        WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        Display display = windowManager.getDefaultDisplay();
        display.getMetrics(metrics);
        return metrics;
    }

    public static int getScreenWidthPixels(Context context) {
        return getDisplayMetrics(context).widthPixels;
    }

    public static int getScreenHeightPixels(Context context) {
        return getDisplayMetrics(context).heightPixels;
    }
}

View

MainActivity

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    private TextView mTypeSelector;
    private GridView mPicSelector;
    private static final int RESULT_IMAGE = 100;
    private static final int RESULT_CAMERA = 200;
    private int mCurrentType = 2;
    private String[] mPicStrings = new String[]{
            "赵", "钱", "孙", "李",
            "周", "吴", "郑", "王",
            "冯", "陈", "褚", "卫",
            "蒋", "沈", "韩", "选择图片"
    };
    private int mPicListColumns = (int) Math.sqrt(mPicStrings.length);
    private String mCameraTempPath;
    private List<Bitmap> mPicList;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        initData();
        initListener();
    }

    private void initListener() {
        mTypeSelector.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mCurrentType == 4) {
                    mCurrentType = 2;
                } else {
                    mCurrentType++;
                }
                mTypeSelector.setText(mCurrentType + " × " + mCurrentType);
            }
        });
        mPicSelector.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                if (position == mPicStrings.length - 1) {
                    AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                    builder.setItems(new String[]{"从相册选择", "拍摄"}, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            Log.d(TAG, "onClick: which = " + which);
                            if (which == 0) {
                                Intent intent = new Intent(Intent.ACTION_PICK, null);
                                intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
                                startActivityForResult(intent, RESULT_IMAGE);
                            } else {
                                Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                                Uri photoUri = Uri.fromFile(new File(mCameraTempPath));
                                intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
                                startActivityForResult(intent, RESULT_CAMERA);
                            }
                        }
                    });
                    builder.show();
                } else {
                    PuzzleActivity.startPuzzleActivity(MainActivity.this, mPicList.get(position), mCurrentType);
                }
            }
        });
    }

    private void initView() {
        mTypeSelector = findViewById(R.id.tv_type_selector);
        mPicSelector = findViewById(R.id.gv_pic_list);
    }

    private void initData() {
        mCameraTempPath = getExternalFilesDir(Environment.DIRECTORY_PICTURES) + "/temp.png";
        Log.d(TAG, "onClick: mImagePath = " + mCameraTempPath);
        //解决上面路径报错问题
        StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
        StrictMode.setVmPolicy(builder.build());
        builder.detectFileUriExposure();

        mPicSelector.setNumColumns(mPicListColumns);
        mPicList = new ArrayList<>();
        for (String string : mPicStrings) {
            mPicList.add(ImagesUtils.createBitmap(
                    ScreenUtils.getScreenWidthPixels(this) / mPicListColumns,
                    ScreenUtils.getScreenWidthPixels(this) / mPicListColumns,
                    string));
        }
        mPicSelector.setAdapter(new PictureListAdapter(this, mPicList));
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        Log.d(TAG, "onActivityResult: resultCode = " + resultCode);
        if (resultCode == RESULT_OK) {
            Bitmap oriBitmap = null;
            InputStream inputStream = null;
            Log.d(TAG, "onActivityResult: requestCode = " + requestCode);
            if (requestCode == RESULT_IMAGE && data != null) {
                try {
                    inputStream = getContentResolver().openInputStream(data.getData());
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }

            } else if (requestCode == RESULT_CAMERA) {
                try {
                    inputStream = new FileInputStream(mCameraTempPath);
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
            }
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            options.inSampleSize = 4;   //图片太大,缩放到1/4
            options.inJustDecodeBounds = false;
            oriBitmap = BitmapFactory.decodeStream(inputStream, null, options);
            Log.d(TAG, "onActivityResult: oriBitmap = " + oriBitmap);
            PuzzleActivity.startPuzzleActivity(MainActivity.this, oriBitmap, mCurrentType);
        }
    }
}

PuzzleActivity

public class PuzzleActivity extends AppCompatActivity implements View.OnClickListener, IGameCallback {

    private static final String TAG = "PuzzleActivity";
    private TextView mStep;
    private TextView mCountDown;
    private GridView mPuzzleGv;
    private ImageView mOriPic;
    private Button mShowPicBtn;
    private Button mResetBtn;
    private Button mBackBtn;

    private PuzzleAdapter mPuzzleAdapter;
    private IPuzzlePresenter mPuzzlePresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_puzzle);
        initView();
        initData();
        initListener();
    }

    private void initView() {
        mStep = findViewById(R.id.tv_step_count);
        mCountDown = findViewById(R.id.tv_countdown);
        mPuzzleGv = findViewById(R.id.gv_puzzle);
        mOriPic = findViewById(R.id.iv_ori_pic);
        mShowPicBtn = findViewById(R.id.btn_ori_pic);
        mResetBtn = findViewById(R.id.btn_reset);
        mBackBtn = findViewById(R.id.btn_back);
    }

    public static void startPuzzleActivity(Context context, Bitmap oriBitmap, int type) {
        Intent intent = new Intent(context, PuzzleActivity.class);
        intent.putExtra(Constant.ORIGINAL_BITMAP, oriBitmap);
        intent.putExtra(Constant.PUZZLE_TYPE, type);
        context.startActivity(intent);
    }

    private void initData() {
        Intent intent = getIntent();
        Bitmap oriBitmap = intent.getParcelableExtra(Constant.ORIGINAL_BITMAP);
        int type = intent.getIntExtra(Constant.PUZZLE_TYPE, 2);

        mPuzzleGv.setNumColumns(type);

        mPuzzlePresenter = new PuzzlePresenterImpl(type);
        mPuzzlePresenter.registerGameCallback(this);
        mPuzzlePresenter.loadPuzzleData(this, oriBitmap);

        /* print log start */
        Log.d(TAG, "initData: oriBitmap = " + oriBitmap);
        Log.d(TAG, "initData: type = " + type);
        /* print log end */
    }


    private void showFinishDialog(String msg) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setMessage(msg);
        builder.setCancelable(false);
        builder.setNegativeButton("再来一次", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                mPuzzlePresenter.restartGame();
                dialog.dismiss();
            }
        });
        builder.setPositiveButton("重选图片", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
                finish();
            }
        });
        builder.show();
    }

    private void initListener() {
        mShowPicBtn.setOnClickListener(this);
        mResetBtn.setOnClickListener(this);
        mBackBtn.setOnClickListener(this);
        mPuzzleGv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Log.d(TAG, "onItemClick: position = " + position);
                mPuzzlePresenter.swapPuzzle(position);
            }
        });
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_ori_pic:
                Log.d(TAG, "onClick: btn_ori_pic");
                if (mOriPic.getVisibility() == View.INVISIBLE) {
                    mPuzzleGv.setVisibility(View.INVISIBLE);
                    mOriPic.setVisibility(View.VISIBLE);
                    mShowPicBtn.setText("隐藏原图");
                } else if (mOriPic.getVisibility() == View.VISIBLE) {
                    mPuzzleGv.setVisibility(View.VISIBLE);
                    mOriPic.setVisibility(View.INVISIBLE);
                    mShowPicBtn.setText("显示原图");
                }
                break;
            case R.id.btn_reset:
                AlertDialog.Builder builder = new AlertDialog.Builder(this);
                builder.setMessage("是否将拼图还原到最开始的状态");
                builder.setCancelable(false);
                builder.setNegativeButton("还原", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        mPuzzlePresenter.resetGame();
                    }
                });
                builder.setPositiveButton("取消", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                });
                builder.show();
                break;
            case R.id.btn_back:
                finish();
                break;
        }
    }

    @Override
    public void onLoadPuzzleData(Bitmap resizeOriBitmap, List<ItemBean> itemBeanList) {
        mOriPic.setImageBitmap(resizeOriBitmap);
        updateUI(itemBeanList);
    }

    private void updateUI(List<ItemBean> itemBeanList) {
        if (mPuzzleAdapter == null) {
            mPuzzleAdapter = new PuzzleAdapter();
            mPuzzleAdapter.setData(itemBeanList);
            mPuzzleGv.setAdapter(mPuzzleAdapter);
        } else {
            mPuzzleAdapter.setData(itemBeanList);
            mPuzzleAdapter.notifyDataSetChanged();
        }
    }

    @Override
    public void onSwapPuzzle(boolean isMovable, List<ItemBean> itemBeanList) {
        if (isMovable) {
            updateUI(itemBeanList);
        } else {
            Toast.makeText(this, "请点击空白格附近的图片进行交换", Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    public void onCountDown(int time, int step) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mCountDown.setText("剩余时间: " + time / 60 + "分" + time % 60 + "秒");
                mStep.setText("剩余步数: " + step);
            }
        });
    }

    @Override
    public void onFinishGame(IPuzzlePresenter.STATE state) {
        switch (state) {
            case SUCCESS:
                showFinishDialog("拼图成功");
                break;
            case FAILED_STEP:
                showFinishDialog("步数已用完");
                break;
            case FAILED_TIME:
                showFinishDialog("倒计时结束");
                break;
        }
    }

    @Override
    public void onResetGame(List<ItemBean> itemBeanList) {
        updateUI(itemBeanList);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mPuzzlePresenter.unRegisterGameCallback();
    }

}

布局

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:id="@+id/ll_type_container"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_margin="10dp"
        android:padding="3dp">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="选择难度"
            android:textSize="30sp" />

        <TextView
            android:id="@+id/tv_type_selector"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:background="#33000000"
            android:padding="3dp"
            android:text="2 × 2"
            android:textSize="25sp" />
    </LinearLayout>

    <GridView
        android:id="@+id/gv_pic_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@id/ll_type_container"
        android:horizontalSpacing="1dp"
        android:verticalSpacing="1dp" />
</RelativeLayout>

activity_puzzle.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".view.PuzzleActivity">

    <LinearLayout
        android:id="@+id/ll_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center">

        <TextView
            android:id="@+id/tv_step_count"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="10dp"
            android:background="#33000000"
            android:padding="10dp"
            android:text="步数"
            android:textSize="25sp" />

        <TextView
            android:id="@+id/tv_countdown"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="10dp"
            android:background="#33000000"
            android:padding="10dp"
            android:text="时间"
            android:textSize="25sp" />
    </LinearLayout>

    <GridView
        android:id="@+id/gv_puzzle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@id/ll_btns"
        android:layout_below="@id/ll_title"
        android:horizontalSpacing="2dp"
        android:verticalSpacing="2dp" />

    <ImageView
        android:id="@+id/iv_ori_pic"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignTop="@id/gv_puzzle"
        android:visibility="invisible" />

    <LinearLayout
        android:id="@+id/ll_btns"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:gravity="center">

        <Button
            android:id="@+id/btn_ori_pic"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="30dp"
            android:background="#33000000"
            android:text="显示原图"
            android:textSize="30sp" />

        <Button
            android:id="@+id/btn_reset"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="30dp"
            android:background="#33000000"
            android:text="复位"
            android:textSize="30sp" />

        <Button
            android:id="@+id/btn_back"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="30dp"
            android:background="#33000000"
            android:text="返回"
            android:textSize="30sp" />
    </LinearLayout>
</RelativeLayout>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/21336.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

手写西瓜书bp神经网络 mnist10 c#版本

本文根据西瓜书第五章中给出的公式编写&#xff0c;书中给出了全连接神经网络的实现逻辑&#xff0c;本文在此基础上编写了Mnist10手写10个数字的案例&#xff0c;网上也有一些其他手写的例子参考。demo使用unity进行编写&#xff0c;方便且易于查错。 该案例仅作为学习&#x…

ROS学习(1)——ROS1和ROS2的区别

因为机器人是一个系统工程&#xff0c;它包括了机械臂结构&#xff0c;电子电路&#xff0c;驱动程序&#xff0c;通信框架&#xff0c;组装集成&#xff0c;调试和各种感知决策算法等方面&#xff0c;任何一个人甚至是一个公司都不可能完成机器人系统的研发工作 。但是我们又希…

TMP的阴影性能如何

1&#xff09;TMP的阴影性能如何 ​2&#xff09;CommandBuffer.DrawMeshInstanced无法画阴影问题 3&#xff09;Unity编辑器在Require大量加载Lua文件时&#xff0c;经常报出not enough memory 4&#xff09;场景制作的时候&#xff0c;2D资源受后处理调色影响比较大 这是第33…

数据结构:栈和队列

朋友们、伙计们&#xff0c;我们又见面了&#xff0c;本期来给大家解读一下栈和队列方面的相关知识点&#xff0c;如果看完之后对你有一定的启发&#xff0c;那么请留下你的三连&#xff0c;祝大家心想事成&#xff01; C 语 言 专 栏&#xff1a;C语言&#xff1a;从入门到精通…

面试了一个00后,绝对能称为是内卷届的天花板

前言 公司前段缺人&#xff0c;也面了不少测试&#xff0c;结果竟然没有一个合适的。一开始瞄准的就是中级的水准&#xff0c;也没指望来大牛&#xff0c;提供的薪资也不低&#xff0c;面试的人很多&#xff0c;但平均水平很让人失望。令我印象最深的是一个00后测试员&#xf…

期刊介绍|骨科老牌期刊,无版面费,审稿极速,毕业不二之选!

今天给大家介绍一本中药方面的期刊&#xff1a;JOURNAL OF ORTHOPAEDIC RESEARCH 一、基本信息 1、期刊名称&#xff1a;JOURNAL OF ORTHOPAEDIC RESEARCH&#xff1b; 2、期刊ISSN: 0736-0266&#xff1b; 3、研究方向&#xff1a;医学-整形外科&#xff1b; 4、出版社&#x…

Maven多环境配置与使用、跳过测试的三种方法

文章目录 1 多环境开发步骤1:父工程配置多个环境,并指定默认激活环境步骤2:执行安装查看env_dep环境是否生效步骤3:切换默认环境为生产环境步骤4:执行安装并查看env_pro环境是否生效步骤5:命令行实现环境切换步骤6:执行安装并查看env_test环境是否生效 2 跳过测试方式1:IDEA工具…

(转载)从0开始学matlab(第9天)—第一阶段总结

1.编程实例 下面的例子将向大家介绍如何用 MATLAB 解决问题。 例1 温度转换程序 问题&#xff1a; 设计一个 MATLAB 程序&#xff0c;读取一个华氏温度的输入&#xff0c;输出开尔文温度。 答案&#xff1a; 华氏温度和开尔文温度的转换关系式可在物理学课本中找到。其关系式…

JVM面试题(一)

JVM内存分哪几个区&#xff0c;每个区的作用是什么? java虚拟机主要分为以下几个区: JVM中方法区和堆空间是线程共享的&#xff0c;而虚拟机栈、本地方法栈、程序计数器是线程独享的。 &#xff08;1&#xff09;方法区&#xff1a; a. 有时候也成为永久代&#xff0c;在该区内…

电极法测污水常规五参数(PH、电导率、溶解氧、温度、浊度)

检测水质常规五参数的意义&#xff1a; pH&#xff1a;地表水水质中pH值的变化会影响藻类对氧气的摄入能力及动物对食物的摄取敏感度&#xff1b; 电导率&#xff1a;主要是测水的导电性&#xff0c;监测水体中总的离子浓度。包含了各种化学物质、重金属、杂质等等各种导电性物…

低代码,或将颠覆开发行业?

前言 传统的软件开发过程往往需要耗费大量的时间和精力&#xff0c;因为开发人员需编写复杂的代码以完成各种功能。 低代码行业的发展&#xff0c;正好解决了这个问题&#xff0c;让复杂的代码编写一去不复返了。 文章目录 前言引入强大的平台总结 引入 低代码平台 是一种通过可…

超级独角兽 Databricks 的崛起之路

在数据扩张以及 AI 兴起的时代&#xff0c;数据存储和分析平台拥有巨大价值和能量。 随着互联网数据的爆炸性增长&#xff0c;数据已经成为企业的新型资源&#xff0c;犹如石油般重要。越来越多的企业希望利用各种结构化和非结构化数据来发挥自己的优势。 然而&#xff0c;他…

通过关键字搜索接口获取alibaba国际站商品列表

作为一名技术爱好者&#xff0c;我们总会遇到各种各样的技术问题&#xff0c;需要寻找合适的技术解决方案。而在互联网时代&#xff0c;我们可以快速通过搜索引擎获取丰富的技术资源和解决方案。然而&#xff0c;在不同的技术分享中&#xff0c;我们常常会遇到质量参差不齐的文…

计算机网络第一章(谢希仁第8版学习)

作者&#xff1a;爱塔居 专栏&#xff1a;计算机网络 作者简介&#xff1a;大三学生&#xff0c;希望和大家一起加油 文章目录 目录 文章目录 一、网络、互连网、互联网&#xff08;因特网&#xff09;的概念 二、因特网的组成 三、交换方式 3.1 电路交换 3.2 分组交换 3.3 电路…

微信小程序nodejs+vue校园二手商城交易(积分兑换)38gw6

随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;校园二手交易被用户普遍使用&#xff0c;为方便用户能够可以随时…

实操带你使用Mybatis_plus(2)

文章目录 一、通用ServiceService CRUD 接口a> IServiceb>创建Service接口和实现类测试 二、常用注解1、TableName2、TableId雪花算法3、TableField4、TableLogic 一、通用Service Service CRUD 接口 通用 Service CRUD 封装IService 接口&#xff0c;进一步封装 CRUD …

大模型高效调参—PEFT库( Parameter-Efficient Fine-Tuning)

介绍 在面对特定的下游任务时&#xff0c;如果进行Full FineTuning&#xff08;即对预训练模型中的所有参数都进行微调&#xff09;&#xff0c;太过低效&#xff1b;而如果采用固定预训练模型的某些层&#xff0c;只微调接近下游任务的那几层参数&#xff0c;又难以达到较好的…

桥梁安全监测,智能化桥梁结构健康监测方案

桥梁是现代城市交通网络中不可或缺的组成部分&#xff0c;但由于长期受到自然环境和人为因素的影响&#xff0c;桥梁的安全问题一直备受关注。传统的桥梁检测方式主要是靠人力进行巡查&#xff0c;这种方式效率低下、成本高&#xff0c;而且难以全面掌握桥梁结构的真实情况。随…

软件测试外包干了4年,感觉废了..

先说一下自己的情况&#xff0c;大专生&#xff0c;18年通过校招进入湖南某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试…

国考省考行测:资料分析,两年复合增长率

国考省考行测&#xff1a;资料分析&#xff0c;两年复合增长率 2022找工作是学历、能力和运气的超强结合体! 公务员特招重点就是专业技能&#xff0c;附带行测和申论&#xff0c;而常规国考省考最重要的还是申论和行测&#xff0c;所以大家认真准备吧&#xff0c;我讲一起屡屡…
最新文章