Android应用开发入门篇-拼图游戏

    前一段时间为了学习android应用开发,尝试写了个简单的拼图应用,在此记录下实现流程的核心部分,同时也希望给其他开发者入门参考带来帮助。


1. 基本的界面设计

    首先应该设计出各个界面(Activity)的样式以及界面间跳转需要通过Intent传递哪些数据。本例包括4个Activity: 

Android应用开发入门篇-拼图游戏

    a. MainActivity主界面,只包含1个TextView和3个ButtonView,每个按钮点击应改变难度的值,这个值应该同过Intent继续传递下去的;

    b. SourceActivity图片源选取,这个界面应成Dialog对话框形式展现 ,要在AndroidManifest.xml文件中声明,然后控件采用ListView;

        <activity
            android:name="com.sean.puzzle.SourceActivity"
            android:label="选择素材源"
            android:theme="@android:style/Theme.Dialog" >


    c. GameActivity游戏界面,核心就是上面的1个ImageView,此外有1个ButtonView和2个TextView;

    d. EndActivity最后一个结束界面没有什么特殊的,主要是为了提醒游戏结束,同样是一个Dialog风格的Activity,与b同。

    有了以上4个界面这个简单的游戏框架就有了,接下来重点就是实现b和c界面中的功能了。


2. 拍照或从相册选取图片

    这个首先要在AndroidManifest.xml文件中设置下允许SD卡读写

    <!-- 在SD卡中创建与删除文件权限 -->
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
    <!-- 从SD卡中读入数据权限 -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />    
    <!-- 向SD卡中写入数据权限 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />


    然后,相机拍照并保存的代码如下,主要图片保存路径的设置(SD卡路径+图片文件夹路径)

				Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
				//获取SD卡对应的存储目录
	            File sdCardDir = Environment.getExternalStorageDirectory();
	            //将拍照时间作为照片文件名并保存
	            Date date = new Date();
	            SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd_HHmmss");//获取当前时间,进一步转化为字符串
	            String path = null;
				try {
					path = sdCardDir.getCanonicalPath() + "/DCIM/Camera/";
				} catch (IOException e) {
					e.printStackTrace();
				}
	            String fileName = "IMG_" + format.format(date) + ".jpg";       
				Uri imageUri = Uri.fromFile(new File(path, fileName));  
				filePath = path + fileName; 				
				//指定照片保存路径(SD卡)
				intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
				startActivityForResult(intent, 0);

    从相册选取图片只需要如下几行

				Intent intent = new Intent(Intent.ACTION_PICK);
				intent.setType("image/*");
				startActivityForResult(intent, 1);


    以上两种选择图片都调用了startActivityForResult方法,主要requestCode参数需要不同,对应的响应方法及实现代码如下:

    @Override  
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
        super.onActivityResult(requestCode, resultCode, data);  
            
        if (requestCode == 0 && resultCode == RESULT_OK) {  
			//pass
        }  
        else if(requestCode == 1 && resultCode == RESULT_OK){ 
        	//The 3 lines below is useful!
            Cursor cursor = this.getContentResolver().query(data.getData(), null, null, null, null);  
            cursor.moveToFirst();  
            filePath = cursor.getString(cursor.getColumnIndex("_data"));         
        }
        
        Intent intent = new Intent();
        intent.setClass(SourceActivity.this, GameActivity.class);
        intent.putExtra("imgPath", filePath);//将获得的图片路径传递到GameActivity即可
        intent.putExtra("level", level);
        startActivity(intent);	  
        finish();
    }  


3. 将图片随机切割

    上一步传递图片路径到GameActivity,但是我们读取图像不可能把几MB的图像放在ImageView里,实际采用的是Bitmap读入图像(此时图像已被缩小),用变量记录缩小的scale。然后将这个Bitmap显示到ImageView上是没有问题的,通过设置匹配方式为centerCrop,图像会很舒服的显示出来。难题在于,点击开始后需要随机切割图像并显示。由于Bitmap虽然缩小了原图像,但是仍保持原图的长宽比,而这个比例和ImageView的比例一般是不一样的,所以如果直接在原始Bitmap上切割的话,显示在ImageView里的图像一定不是规整的3*3或5*5图块。

    解决办法,需要在原始Bitmap上先进行按ImageView比例的centerCrop裁剪, 方法如下

	private Bitmap centerCrop(Bitmap src, int W, int H){
		int w = src.getWidth();
		int h = src.getHeight();
		float ratio = (float)W/H;
		Bitmap dst = null;
		if(((float)w/h) > ratio){//crop width
			dst = Bitmap.createBitmap(src_bitmap, (int)((w-ratio*h)/2), 0, (int)(ratio*h), h);
		}else{//crop height
			dst = Bitmap.createBitmap(src_bitmap, 0, (int)((h-w/ratio)/2), w, (int)(w/ratio));
		}
		return dst;
	}

    然后显示的就是规整的3*3(初级)或5*5(中级)模式了,这里省略了随机切割图像块的方法,主要就是通过Random类的对象生成随机数(但是不能重复)。此外,用到了一个Bitmap的数组来保存每个图块,一个int数组保存每个位置的图块索引。

4. 点击交换图块位置

    给ImageView绑定一个OnTouchListener接口的实现,点击图像时触发。

	class ImageListener implements OnTouchListener{
		@Override
		public boolean onTouch(View arg0, MotionEvent arg1) {
			float x = arg1.getX();//get clicked x position
			float y = arg1.getY();//get clicked y position
			int tmp_col = (int) (x / (width/col));
			int tmp_row = (int) (y / (height/row));
			int new_chosen_num = tmp_row * col + tmp_col;
			if((chosen_num != -1) && (new_chosen_num != chosen_num)){//do swap
				swapBlock(chosen_num, new_chosen_num);
				txt_count.setText(step+"");
				chosen_num = -1;
				if(mis_count == 0){//Game Win!
					Intent intent = new Intent();
					intent.setClass(GameActivity.this, EndActivity.class);
					startActivity(intent);					
				}
			}else{//set one chosen
				chosen_num = new_chosen_num;
			}
			return false;
		}		
	}

    最后交换图像块后ImageView上的显示问题用到了Canvas,相当于把Bitmap作为一个画布,在上面画出交换结果,然后将新Bitmap显示到ImageView上。

		Canvas to_draw = new Canvas(new_bitmap);
		for(int i = 0; i < row; i++)
			for(int j = 0; j < col; j++)
				to_draw.drawBitmap(pic_arr[i * row + j], j*(tmp_width/col), i*(tmp_height/row), null);
		to_draw.save(Canvas.ALL_SAVE_FLAG);
		to_draw.restore();
		img.setImageBitmap(new_bitmap);
		img.setOnTouchListener(new ImageListener());


     以上内容总结的很潦草,可能更方便自己日后查看,里面的一些代码可能缺少连贯性。只希望某些地方的处理方法能给入门的朋友一些借鉴,如果有不清楚想仔细了解的朋友请留言。

Android应用开发入门篇-拼图游戏,布布扣,bubuko.com

Android应用开发入门篇-拼图游戏

上一篇:鼎力支持地球一小时,点心移动彰显移动互联网“社会责任感”


下一篇:Oracle 字符串截取和位置