项目简介

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

在这里插入图片描述

游戏界面如下

在这里插入图片描述

采用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>
Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐