当前位置:  编程技术>移动开发
本页文章导读:
    ▪[转]依据两点经纬度计算距离        [转]根据两点经纬度计算距离 根据两点经纬度计算距离 这些经纬线是怎样定出来的呢?地球是在不停地绕地轴旋转(地轴是一根通过地球南北两极和地球中心的 假想线),在地球中腰画一.........
    ▪ 兑现半透明的popupwindow的源码        实现半透明的popupwindow的源码 1.设置半透明主题 2.设置window的alpha值 //                        WindowManager.LayoutParams lp = getWindow().getAttributes(); //            lp.alpha = 0.5f; //0.0-1.0 //  .........
    ▪ 排雷游戏       扫雷游戏 MineSweeper是一个不错的Android开源扫雷游戏,对于初学Android开发网的网友可能有很大的帮助,对于Java游戏开发也有一定的参考意义。该游戏主要有以下技术值得学习:  1. 个性化字.........

[1][转]依据两点经纬度计算距离
    来源: 互联网  发布时间: 2014-02-18
[转]根据两点经纬度计算距离
根据两点经纬度计算距离
这些经纬线是怎样定出来的呢?地球是在不停地绕地轴旋转(地轴是一根通过地球南北两极和地球中心的
假想线),在地球中腰画一个与地轴垂直的大圆圈,使圈上的每一点都和南北两极的距离相等,这个圆圈
就叫作“赤道”。在赤道的南北两边,画出许多和赤道平行的圆圈,就是“纬圈”;构成这些圆圈的线段,
叫做纬线。我们把赤道定为纬度零度,向南向北各为90度,在赤道以南的叫南纬,在赤道以北的叫北纬。
北极就是北纬90度,南极就是南纬90度。纬度的高低也标志着气候的冷热,如赤道和低纬度地地区无冬,
两极和高纬度地区无夏,中纬度地区四季分明。 
其次,从北极点到南极点,可以画出许多南北方向的与地球赤道垂直的大圆圈,这叫作“经圈”;构成这
些圆圈的线段,就叫经线。公元1884平面坐标图年,国际上规定以通过英国伦敦近郊的格林尼治天文台的
经线作为计算经度的起点,即经度零度零分零秒,也称“本初子午线”。在它东面的为东经,共180度;
在它西面的为西经,共180度。因为地球是圆的,所以东经180度和西经180度的经线是同一条经线。各国
公定180度经线为“国际日期变更线”。为了避免同一地区使用两个不同的日期,国际日期变线在遇陆地时
略有偏离。 
  每一经度和纬度还可以再细分为60分,每一分再分为60秒以及秒的小数。利用经纬线,我们就可以确定
地球上每一个地方的具体位置,并且把它在地图或地球仪上表示出来。例如问北京的经纬度是多少?我们
很容易从地图上查出来是东经116度24分,北纬39度54分。在大海中航行的船只,只要把所在地的经度测
出来,就可以确定船在海洋中的位置和前进方向。 纬度共有90度。赤道为0度,向两极排列,圈子越小,
度数越大。
横线是纬度,竖线是经度。 
当然可以计算,四元二次方程。 
经度和纬度都是一种角度。经度是个两面角,是两个经线平面的夹角。因所有经线都是一样长,为了度量
经度选取一个起点面,经1884年国际会议协商,决定以通过英国伦敦近郊、泰晤士河南岸的格林尼治皇家
天文台(旧址)的一台主要子午仪十字丝的那条经线为起始经线,称为本初子午线。本初子午线平面是起
点面,终点面是本地经线平面。某一点的经度,就是该点所在的经线平面与本初子午线平面间的夹角。在
赤道上度量,自本初子午线平面作为起点面,分别往东往西度量,往东量值称为东经度,往西量值称为西
经度。由此可见,一地的经度是该地对于本初子午线的方向和角距离。本初子午线是0°经度,东经度的最
大值为180°,西经度的最大值为180°,东、西经180°经线是同一根经线,因此不分东经或西经,而统称
180°经线。 
  纬度是个线面角。起点面是赤道平面,线是本地的地面法线。所谓法线,即垂直于参考扁球体表面的线。
某地的纬度就是该地的法线与赤道平面之间的夹角。纬度在本地经线上度量,由赤道向南、北度量,向北
量值称为北纬度,向南量值称为南纬度。由此可见,一地的纬度是该地对于赤道的方向和角距离。赤道是
0°纬线,北纬度的最大值为90°,即北极点;南纬度的最大值为90°,即南极点。

经纬度互换

度(DDD):E 108.90593度    N 34.21630度

    如何将度(DDD):: 108.90593度换算成度分秒(DMS)东经E 108度54分22.2秒?转换方法是将108.90593整数位不变取108(度),用0.90593*60=54.3558,取整数位 54(分),0.3558*60=21.348再取整数位21(秒),故转化为108度54分21秒.

   同样将度分秒(DMS):东经E 108度54分22.2秒 换算成度(DDD)的方法如下:108度54分22.2秒=108+(54/60)+(22.2/3600)=108.90616度

因为计算时小数位保留的原因,导致正反计算存在一定误差,但误差影响不是很大。1秒的误差就是几米的样子。GPS车友可以用上述方法换算成自己需要的单位坐标。

 

 

经纬度换算成米

纬度分为60分,每一分再分为60秒以及秒的小数。

纬度线投射在图上看似水平的平行线,但实际上是不同半径的圆。有相同特定纬度的所有位置都在同一个纬线上。
赤道的纬度为0°,将行星平分为南半球和北半球。
纬度是指某点与地球球心的连线和地球赤道面所成的线面角,其数值在0至90度之间。位于赤道以北的点的纬度叫北纬,记为N,位于赤道以南的点的纬度称南纬,记为S。
纬度数值在0至30度之间的地区称为低纬地区,纬度数值在30至60度之间的地区称为中纬地区,纬度数值在60至90度之间的地区称为高纬地区。
赤道、南回归线、北回归线、南极圈和北极圈是特殊的纬线。
纬度1秒的长度
地球的子午线总长度大约40008km。平均:
纬度1度 = 大约111km
纬度1分 = 大约1.85km
纬度1秒 = 大约30.9m

根据地球上任意两点的经纬度计算两点间的距离

地球是一个近乎标准的椭球体,它的赤道半径为6378.140千米,极半径为 6356.755千米,平均半径6371.004千米。如果我们假设地球是一个完美的球体,那么它的半径就是地球的平均半径,记为R。如果以0度经线为基 准,那么根据地球表面任意两点的经纬度就可以计算出这两点间的地表距离(这里忽略地球表面地形对计算带来的误差,仅仅是理论上的估算值)。设第一点A的经 纬度为(LonA, LatA),第二点B的经纬度为(LonB, LatB),按照0度经线的基准,东经取经度的正值(Longitude),西经取经度负值(-Longitude),北纬取90-纬度值(90- Latitude),南纬取90+纬度值(90+Latitude),则经过上述处理过后的两点被计为(MLonA, MLatA)和(MLonB, MLatB)。那么根据三角推导,可以得到计算两点距离的如下公式:

C = sin(MLatA)*sin(MLatB)*cos(MLonA-MLonB) + cos(MLatA)*cos(MLatB)

Distance = R*Arccos(C)*Pi/180

这里,R和Distance单位是相同,如果是采用6371.004千米作为半径,那么Distance就是千米为单位,如果要使用其他单位,比如mile,还需要做单位换算,1千米=0.621371192mile

如果仅对经度作正负的处理,而不对纬度作90-Latitude(假设都是北半球,南半球只有澳洲具有应用意义)的处理,那么公式将是:

C = sin(LatA)*sin(LatB) + cos(LatA)*cos(LatB)*cos(MLonA-MLonB)

Distance = R*Arccos(C)*Pi/180

以上通过简单的三角变换就可以推出。

如果三角函数的输入和输出都采用弧度值,那么公式还可以写作:

C = sin(LatA*Pi/180)*sin(LatB*Pi/180) + cos(LatA*Pi/180)*cos(LatB*Pi/180)*cos((MLonA-MLonB)*Pi/180)

Distance = R*Arccos(C)*Pi/180

也就是:

C = sin(LatA/57.2958)*sin(LatB/57.2958) + cos(LatA/57.2958)*cos(LatB/57.2958)*cos((MLonA-MLonB)/57.2958)

Distance = R*Arccos(C) = 6371.004*Arccos(C) kilometer = 0.621371192*6371.004*Arccos(C) mile = 3958.758349716768*Arccos(C) mile

在实际应用当中,一般是通过一个个体的邮政编码来查找该邮政编码对应的地区中心的经纬度,然 后再根据这些经纬度来计算彼此的距离,从而估算出某些群体之间的大致距离范围(比如酒店旅客的分布范围-各个旅客的邮政编码对应的经纬度和酒店的经纬度所 计算的距离范围-等等),所以,通过邮政编码查询经纬度这样一个数据库是一个很有用的资源。
附:C#代码:

private const double EARTH_RADIUS = 6378.137;//地球半径
private static double rad(double d)
{
   return d * Math.PI / 180.0;
}

public static double GetDistance(double lat1, double lng1, double lat2, double lng2)
{
   double radLat1 = rad(lat1);
   double radLat2 = rad(lat2);
   double a = radLat1 - radLat2;
   double b = rad(lng1) - rad(lng2);

   double s = 2 * Math.Asin(Math.Sqrt(Math.Pow(Math.Sin(a/2),2) +
    Math.Cos(radLat1)*Math.Cos(radLat2)*Math.Pow(Math.Sin(b/2),2)));
   s = s * EARTH_RADIUS;
   s = Math.Round(s * 10000) / 10000;
   return s;
}


    
[2] 兑现半透明的popupwindow的源码
    来源: 互联网  发布时间: 2014-02-18
实现半透明的popupwindow的源码

1.设置半透明主题
2.设置window的alpha值

  • //                        WindowManager.LayoutParams lp = getWindow().getAttributes();
  • //            lp.alpha = 0.5f; //0.0-1.0
  • //            getWindow().setAttributes(lp);
  • 复制代码


    发现这两种都不能满足要求,起码的颜色就不太对。想做好点,做成类似alertDialog的样子,带边框,弹出窗口带动画效果,之后背景置灰,那多帅。
    看到那个仿uc浏览器的源码,是用alertdialog做的,达到那种效果,加点动画就行了。下图是从那个ucweb源码里面弄出来的。

    2010-12-2 15:31 上传
    下载附件 (426.79 KB)



           上面的代码就不贴了,我上传的项目文件里面也有。
            下面是弹出popupwindow的图片,第一张是动画中,第二张是完全弹出的:

    2010-12-2 15:31 上传
    下载附件 (422.22 KB)

     

    2010-12-2 15:31 上传
    下载附件 (457.53 KB)




             弹出popwindow的代码如下,比较乱,多包涵:

     

  • popupWindow = new PopupWindow(menuView, LayoutParams.FILL_PARENT,
  •                                 LayoutParams.FILL_PARENT, true);
  •                 popupWindow.showAtLocation(findViewById(R.id.parent), Gravity.CENTER
  •                                 | Gravity.CENTER, 0, 0);
  •                 popupWindow.setAnimationStyle(R.style.PopupAnimation);
  •                 // 加上下面两行可以用back键关闭popupwindow,否则必须调用dismiss();
  •                 ColorDrawable dw = new ColorDrawable(-00000);
  •                 popupWindow.setBackgroundDrawable(dw);
  •                 popupWindow.update();
  • 复制代码


    下面是实现步骤:    
    1。背景置灰:
         popupWindow = new PopupWindow(menuView, LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT, true);
         第二三个参数必须是LayoutParams.FILL_PARENT,这样才能填充整个屏幕,达到背景置灰的目的。
         整个popupwindow里面是一个GridView,图片什么的也是用的那个仿UC浏览器界面项目的,在此谢谢了。
         关键的东西都在xml里面。

          
  • <?xml version="1.0" encoding="utf-8"?>
  • <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  •            android:orientation="vertical" android:layout_width="fill_parent"
  •             android:gravity="center" android:layout_height="fill_parent"
  •            android:layout_gravity="center" android:background="#b0000000" >
  •          <LinearLayout android:orientation="vertical"
  •                    android:layout_width="wrap_content" android:gravity="center"
  •                    android:layout_height="wrap_content" android:layout_gravity="center"
  •                    android:background="@drawable/downbutton_corner">
  •                  <GridView android:id="@+id/gridview" android:layout_width="wrap_content"
  •                             android:layout_height="wrap_content" android:numColumns="4"
  •                             android:verticalSpacing="5dip" android:horizontalSpacing="5dip"
  •                             android:stretchMode="columnWidth" android:gravity="center"
  •                             android:layout_gravity="center" /></LinearLayout></LinearLayout>
  • 复制代码


    第一个linearlayout里面的android:background="#b0000000",就是全屏背景,网上搜的好多半透明都是“#e0000000”,我觉得那颜色太深,“#b0000000”更合适。
    第二个linearlayout是popupwind的背景,里面的android:background="@drawable/downbutton_corner"是关键,边框,圆角都是里面定义的。

    2。popupwindow的边框,圆角背景。downbutton_corne.xml

  • <shape xmlns:android="http://schemas.android.com/apk/res/android"
  •         android:shape="rectangle">
  •         <gradient android:startColor="#c0000000" android:endColor="#c0000000"
  •                 android:angle="90" /><!--背景颜色渐变 -->
  •         <stroke android:dashWidth="2dp" android:dashGap="2dp"
  •                 android:width="2dp" android:color="#FF00ff00"></stroke>
  •         <!--描边 -->
  •         <corners android:bottomRightRadius="5dp"
  •                 android:bottomLeftRadius="5dp" android:topLeftRadius="5dp"
  •                 android:topRightRadius="5dp" /><!--设置圆角-->
  • </shape>
  • 复制代码


    这个涉及到shape画图,要是不懂的话。网上很多资料,搜一下就是了。我博客里面也有,http://blog.csdn.net/ymdcr/archive/2010/12/01/6048256.aspx
    <gradient android:startColor="#c0000000" android:endColor="#c0000000" android:angle="90" /><!--背景颜色渐变 -->
    我就设置了一个固定的颜色"#c0000000"。android:angle="90"这个是设置颜色渐变方向,从上到下啊,从左到右啊,貌似只能90的倍数,也只有四个方向嘛。
    <stroke ></stroke>,边框就是这个实现的。
    dashWidth指的是边线的宽度 dashGap 指的是每条线之间的间距,(因为是边线是很多小横线组成的)。

    3。淡入淡出动画
    popupWindow.setAnimationStyle(R.style.PopupAnimation);
    这条代码是设置style的,动画文件就是在style文件里面引入的。下面是淡入的动画,动画教程网上也很多。淡出的动画就这些参数值交换位置就是了。android:duration这个是持续时间,为了截图,我把它弄成5秒了。

  • <set xmlns:android="http://schemas.android.com/apk/res/android">
  •         <scale android:fromXScale="0.6" android:toXScale="1.0"
  •                 android:fromYScale="0.6" android:toYScale="1.0" android:pivotX="50%"
  •                 android:pivotY="50%" android:duration="5000" />
  •         <alpha android:interpolator="@android:anim/decelerate_interpolator"
  •                 android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="5000" />
  • </set>
  • 复制代码



    大概就是这些了。

    还有一个关键的问题。弹出pop之后,back键无效了,必须在pop里面设置事件dismiss掉。下面是问题的描述,哪位解决了,告诉我一下,谢谢。我的邮箱:ytdcr@tom.com

    问题解决了,是因为没设置背景的原因。   
    popupWindow.setBackgroundDrawable(new BitmapDrawable());
    //把这一行放在 showAtLocation前面就行了,以前是放在后面的,粗心了。
    popupWindow.showAtLocation(findViewById(R.id.parent), Gravity.CENTER
                    | Gravity.CENTER, 0, 0);   
    网上也有很多人说,弹出pop之后,不响应键盘事件了,这个其实是焦点在pop里面的view去了。
    以这个为例,焦点就在gridview上面去了。28楼的兄弟提示的,谢了。
    在gridview加上 setOnKeyListener,就能解决。
  • menuGrid.setOnKeyListener(new OnKeyListener() {
  •             @Override
  •             public boolean onKey(View v, int keyCode, KeyEvent event) {
  •                 switch (keyCode) {
  •                 case KeyEvent.KEYCODE_MENU:
  •                     if (popupWindow != null && popupWindow.isShowing()) {
  •                         popupWindow.dismiss();
  •                         System.out.println("menuGridfdsfdsfdfd");
  •                     }
  •                     break;
  •                 }
  •             
  •                 return true;
  •             }
  •         });
  • 复制代码
    [ code]/**
         * ColorDrawable dw = new ColorDrawable(-00000);
         * popupWindow.setBackgroundDrawable(dw);
         * 本来看了个示例,加上上面这两行就不用调用dismiss,点击窗口之外的部位,或者按back键都能关闭窗口。 但是我这样写了,还是不行。
         * 而且竟然捕获不到键盘事件,杯具,希望哪个解决了这个问题告诉我,谢谢。 ytdcr@tom.com
         */
        /*
         * @Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch
         * (keyCode) { case KeyEvent.KEYCODE_BACK: if (popupWindow != null) {
         * popupWindow.dismiss(); }
         *
         * Toast.makeText(this, "fd", 1000).show(); break;
         *
         * } return super.onKeyUp(keyCode, event); }
         */[/code]


    demo.rar (1.73 MB, 下载次数: 331)

    2010-12-2 15:25 上传
    下载次数: 331
    下载积分: e币 -1 元

    修改版demo.rar (1.74 MB, 下载次数: 1486)

    1 楼 lc_android 2011-07-30  
    2 楼 yangwuweiyouwei 2011-08-03  
    好东西啊。。。
    3 楼 琼露露 2011-08-29  
    很好,学习,谢谢
    4 楼 eclipse0207 2011-09-23  
    怎么不能下载  

        
    [3] 排雷游戏
        来源: 互联网  发布时间: 2014-02-18
    扫雷游戏
    MineSweeper是一个不错的Android开源扫雷游戏,对于初学Android开发网的网友可能有很大的帮助,对于Java游戏开发也有一定的参考意义。该游戏主要有以下技术值得学习:

      1. 个性化字体,计分器使用的是LED字体,可以帮助我们如何导入外部字体在Android平台中显示。

      2. 带图片的Toast,下面的You won in 36 seconds这个Toast使用了自定义的布局,可以显示图片和文字。

      3. 自定义Button控件,可以看到标记是否为雷,显示附近地雷数量的按钮控件,初学者可以很容易的学习到Android开发中常用的自定义控件技术。
     
      4.  因为游戏实时性不高,这里没有用到SurfaceView,下次Android开发网给大家提供一个将对高级些的例子。

    关键代码如下:
    main.xml
    <?xml version="1.0" encoding="utf-8"?>
    <TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:stretchColumns="0,2"
        android:background="@drawable/back">
         <TableRow>
    		<TextView
    			android:id="@+id/Timer"
    			android:layout_column="0"
    			android:layout_width="fill_parent"
    			android:layout_height="48px"
    			android:gravity="center_horizontal"
    			android:padding="5dip" 
    			android:textColor="#FFFFFF"
    			android:textSize="40sp"
    			android:text="000" />
    			
    		<ImageButton android:id="@+id/Smiley"
    			android:layout_column="1"
    			android:background="@drawable/smiley_button_states"
    			android:scaleType="center"
    			android:padding="5dip" 
    			android:layout_width="48px"
    			android:layout_height="48px"/>
    		
    		<TextView
    			android:id="@+id/MineCount"
    			android:layout_column="2"
    			android:layout_width="fill_parent"
    			android:layout_height="48px"
    			android:gravity="center_horizontal"
    			android:padding="5dip" 
    			android:textColor="#FFFFFF"
    			android:textSize="40sp"
    			android:text="000" />
        </TableRow>
        
    	<TableRow>
    		<TextView
    			android:layout_column="0"
    			android:layout_width="fill_parent"
    			android:layout_height="50px"
    			android:layout_span="3"
    			android:padding="10dip"/>
    	</TableRow>
    		
        <TableRow>
    		<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
        		android:id="@+id/MineField"
        		android:layout_width="260px"
    		    android:layout_height="260px"
    		    android:gravity="bottom"
    		    android:stretchColumns="*"
    		    android:layout_span="3"
    		    android:padding="5dip" >
    		</TableLayout>
        </TableRow>
    </TableLayout>
    

    smiley_button_states.xml
    <?xml version="1.0" encoding="utf-8"?>
    <selector xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:state_focused="true" 
        	  android:state_pressed="false" 
        	  android:layout_width="48px"
        	  android:layout_height="48px"
        	  android:drawable="@drawable/smile" />
        	  
        <item android:state_focused="true" 
        	  android:state_pressed="true"
        	  android:layout_width="48px"
        	  android:layout_height="48px"
        	  android:drawable="@drawable/surprise" />
        	  
        <item android:state_focused="false" 
        	  android:state_pressed="true"
        	  android:layout_width="48px"
        	  android:layout_height="48px"
    	      android:drawable="@drawable/surprise" />
    	      
        <item android:layout_width="48px"
         	  android:layout_height="48px"
        	  android:drawable="@drawable/smile" />
    </selector>
    

    Block.java
    package com.VertexVerveInc.Games;
    
    import android.content.Context;
    import android.graphics.Color;
    import android.graphics.Typeface;
    import android.util.AttributeSet;
    import android.widget.Button;
    
    public class Block extends Button
    {
    	private boolean isCovered; // is block covered yet
    	private boolean isMined; // does the block has a mine underneath
    	private boolean isFlagged; // is block flagged as a potential mine
    	private boolean isQuestionMarked; // is block question marked
    	private boolean isClickable; // can block accept click events
    	private int numberOfMinesInSurrounding; // number of mines in nearby blocks
    
    	public Block(Context context)
    	{
    		super(context);
    	}
    
    	public Block(Context context, AttributeSet attrs)
    	{
    		super(context, attrs);
    	}
    
    	public Block(Context context, AttributeSet attrs, int defStyle)
    	{
    		super(context, attrs, defStyle);
    	}
    
    	// set default properties for the block
    	public void setDefaults()
    	{
    		isCovered = true;
    		isMined = false;
    		isFlagged = false;
    		isQuestionMarked = false;
    		isClickable = true;
    		numberOfMinesInSurrounding = 0;
    
    		this.setBackgroundResource(R.drawable.square_blue);
    		setBoldFont();
    	}
    
    	// mark the block as disabled/opened
    	// update the number of nearby mines
    	public void setNumberOfSurroundingMines(int number)
    	{
    		this.setBackgroundResource(R.drawable.square_grey);
    		
    		updateNumber(number);
    	}
    
    	// set mine icon for block
    	// set block as disabled/opened if false is passed
    	public void setMineIcon(boolean enabled)
    	{
    		this.setText("M");
    
    		if (!enabled)
    		{
    			this.setBackgroundResource(R.drawable.square_grey);
    			this.setTextColor(Color.RED);
    		}
    		else
    		{
    			this.setTextColor(Color.BLACK);
    		}
    	}
    
    	// set mine as flagged
    	// set block as disabled/opened if false is passed
    	public void setFlagIcon(boolean enabled)
    	{
    		this.setText("F");
    
    		if (!enabled)
    		{
    			this.setBackgroundResource(R.drawable.square_grey);
    			this.setTextColor(Color.RED);
    		}
    		else
    		{
    			this.setTextColor(Color.BLACK);
    		}
    	}
    
    	// set mine as question mark
    	// set block as disabled/opened if false is passed
    	public void setQuestionMarkIcon(boolean enabled)
    	{
    		this.setText("?");
    		
    		if (!enabled)
    		{
    			this.setBackgroundResource(R.drawable.square_grey);
    			this.setTextColor(Color.RED);
    		}
    		else
    		{
    			this.setTextColor(Color.BLACK);
    		}
    	}
    
    	// set block as disabled/opened if false is passed
    	// else enable/close it
    	public void setBlockAsDisabled(boolean enabled)
    	{
    		if (!enabled)
    		{
    			this.setBackgroundResource(R.drawable.square_grey);
    		}
    		else
    		{
    			this.setBackgroundResource(R.drawable.square_blue);
    		}
    	}
    
    	// clear all icons/text
    	public void clearAllIcons()
    	{
    		this.setText("");
    	}
    
    	// set font as bold
    	private void setBoldFont()
    	{
    		this.setTypeface(null, Typeface.BOLD);
    	}
    
    	// uncover this block
    	public void OpenBlock()
    	{
    		// cannot uncover a mine which is not covered
    		if (!isCovered)
    			return;
    
    		setBlockAsDisabled(false);
    		isCovered = false;
    
    		// check if it has mine
    		if (hasMine())
    		{
    			setMineIcon(false);
    		}
    		// update with the nearby mine count
    		else
    		{
    			setNumberOfSurroundingMines(numberOfMinesInSurrounding);
    		}
    	}
    
    	// set text as nearby mine count
    	public void updateNumber(int text)
    	{
    		if (text != 0)
    		{
    			this.setText(Integer.toString(text));
    
    			// select different color for each number
    			// we have already skipped 0 mine count
    			switch (text)
    			{
    				case 1:
    					this.setTextColor(Color.BLUE);
    					break;
    				case 2:
    					this.setTextColor(Color.rgb(0, 100, 0));
    					break;
    				case 3:
    					this.setTextColor(Color.RED);
    					break;
    				case 4:
    					this.setTextColor(Color.rgb(85, 26, 139));
    					break;
    				case 5:
    					this.setTextColor(Color.rgb(139, 28, 98));
    					break;
    				case 6:
    					this.setTextColor(Color.rgb(238, 173, 14));
    					break;
    				case 7:
    					this.setTextColor(Color.rgb(47, 79, 79));
    					break;
    				case 8:
    					this.setTextColor(Color.rgb(71, 71, 71));
    					break;
    				case 9: 
    					this.setTextColor(Color.rgb(205, 205, 0));
    					break;
    			}
    		}
    	}
    
    	// set block as a mine underneath
    	public void plantMine()
    	{
    		isMined = true;
    	}
    
    	// mine was opened
    	// change the block icon and color
    	public void triggerMine()
    	{
    		setMineIcon(true);
    		this.setTextColor(Color.RED);
    	}
    
    	// is block still covered
    	public boolean isCovered()
    	{
    		return isCovered;
    	}
    
    	// does the block have any mine underneath
    	public boolean hasMine()
    	{
    		return isMined;
    	}
    
    	// set number of nearby mines
    	public void setNumberOfMinesInSurrounding(int number)
    	{
    		numberOfMinesInSurrounding = number;
    	}
    
    	// get number of nearby mines
    	public int getNumberOfMinesInSorrounding()
    	{
    		return numberOfMinesInSurrounding;
    	}
    
    	// is block marked as flagged
    	public boolean isFlagged()
    	{
    		return isFlagged;
    	}
    
    	// mark block as flagged
    	public void setFlagged(boolean flagged)
    	{
    		isFlagged = flagged;
    	}
    
    	// is block marked as a question mark
    	public boolean isQuestionMarked()
    	{
    		return isQuestionMarked;
    	}
    
    	// set question mark for the block
    	public void setQuestionMarked(boolean questionMarked)
    	{
    		isQuestionMarked = questionMarked;
    	}
    
    	// can block receive click event
    	public boolean isClickable()
    	{
    		return isClickable;
    	}
    
    	// disable block for receive click events
    	public void setClickable(boolean clickable)
    	{
    		isClickable = clickable;
    	}
    }
    

    MinesweeperGame.java
    package com.VertexVerveInc.Games;
    
    import java.util.Random;
    
    import android.app.Activity;
    import android.graphics.Typeface;
    import android.os.Bundle;
    import android.os.Handler;
    import android.view.Gravity;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.view.View.OnLongClickListener;
    import android.widget.ImageButton;
    import android.widget.ImageView;
    import android.widget.LinearLayout;
    import android.widget.TableRow.LayoutParams;
    import android.widget.TableLayout;
    import android.widget.TableRow;
    import android.widget.TextView;
    import android.widget.Toast;
    
    public class MinesweeperGame extends Activity
    {
    	private TextView txtMineCount;
    	private TextView txtTimer;
    	private ImageButton btnSmile;
    
    	private TableLayout mineField; // table layout to add mines to
    
    	private Block blocks[][]; // blocks for mine field	
    	private int blockDimension = 24; // width of each block
    	private int blockPadding = 2; // padding between blocks
    
    	private int numberOfRowsInMineField = 9;
    	private int numberOfColumnsInMineField = 9;
    	private int totalNumberOfMines = 10;
    
    	// timer to keep track of time elapsed
    	private Handler timer = new Handler();
    	private int secondsPassed = 0;
    
    	private boolean isTimerStarted; // check if timer already started or not
    	private boolean areMinesSet; // check if mines are planted in blocks
    	private boolean isGameOver;
    	private int minesToFind; // number of mines yet to be discovered
    
    	@Override
    	public void onCreate(Bundle savedInstanceState)
    	{
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.main);
    		
    		txtMineCount = (TextView) findViewById(R.id.MineCount);
    		txtTimer = (TextView) findViewById(R.id.Timer);
    		
    		// set font style for timer and mine count to LCD style
    		Typeface lcdFont = Typeface.createFromAsset(getAssets(),
    				"fonts/lcd2mono.ttf");
    		txtMineCount.setTypeface(lcdFont);
    		txtTimer.setTypeface(lcdFont);
    		
    		btnSmile = (ImageButton) findViewById(R.id.Smiley);
    		btnSmile.setOnClickListener(new OnClickListener()
    		{
    			@Override
    			public void onClick(View view)
    			{
    				endExistingGame();
    				startNewGame();
    			}
    		});
    		
    		mineField = (TableLayout)findViewById(R.id.MineField);
    		
    		showDialog("Click smiley to start New Game", 2000, true, false);
    	}
    
    	private void startNewGame()
    	{
    		// plant mines and do rest of the calculations
    		createMineField();
    		// display all blocks in UI
    		showMineField();
    		
    		minesToFind = totalNumberOfMines;
    		isGameOver = false;
    		secondsPassed = 0;
    	}
    
    	private void showMineField()
    	{
    		// remember we will not show 0th and last Row and Columns
    		// they are used for calculation purposes only
    		for (int row = 1; row < numberOfRowsInMineField + 1; row++)
    		{
    			TableRow tableRow = new TableRow(this);  
    			tableRow.setLayoutParams(new LayoutParams((blockDimension + 2 * blockPadding) * numberOfColumnsInMineField, blockDimension + 2 * blockPadding));
    
    			for (int column = 1; column < numberOfColumnsInMineField + 1; column++)
    			{
    				blocks[row][column].setLayoutParams(new LayoutParams(  
    						blockDimension + 2 * blockPadding,  
    						blockDimension + 2 * blockPadding)); 
    				blocks[row][column].setPadding(blockPadding, blockPadding, blockPadding, blockPadding);
    				tableRow.addView(blocks[row][column]);
    			}
    			mineField.addView(tableRow,new TableLayout.LayoutParams(  
    					(blockDimension + 2 * blockPadding) * numberOfColumnsInMineField, blockDimension + 2 * blockPadding));  
    		}
    	}
    
    	private void endExistingGame()
    	{
    		stopTimer(); // stop if timer is running
    		txtTimer.setText("000"); // revert all text
    		txtMineCount.setText("000"); // revert mines count
    		btnSmile.setBackgroundResource(R.drawable.smile);
    		
    		// remove all rows from mineField TableLayout
    		mineField.removeAllViews();
    		
    		// set all variables to support end of game
    		isTimerStarted = false;
    		areMinesSet = false;
    		isGameOver = false;
    		minesToFind = 0;
    	}
    
    	private void createMineField()
    	{
    		// we take one row extra row for each side
    		// overall two extra rows and two extra columns
    		// first and last row/column are used for calculations purposes only
    		//	 x|xxxxxxxxxxxxxx|x
    		//	 ------------------
    		//	 x|              |x
    		//	 x|              |x
    		//	 ------------------
    		//	 x|xxxxxxxxxxxxxx|x
    		// the row and columns marked as x are just used to keep counts of near by mines
    
    		blocks = new Block[numberOfRowsInMineField + 2][numberOfColumnsInMineField + 2];
    
    		for (int row = 0; row < numberOfRowsInMineField + 2; row++)
    		{
    			for (int column = 0; column < numberOfColumnsInMineField + 2; column++)
    			{	
    				blocks[row][column] = new Block(this);
    				blocks[row][column].setDefaults();
    
    				// pass current row and column number as final int's to event listeners
    				// this way we can ensure that each event listener is associated to 
    				// particular instance of block only
    				final int currentRow = row;
    				final int currentColumn = column;
    
    				// add Click Listener
    				// this is treated as Left Mouse click
    				blocks[row][column].setOnClickListener(new OnClickListener()
    				{
    					@Override
    					public void onClick(View view)
    					{
    						// start timer on first click
    						if (!isTimerStarted)
    						{
    							startTimer();
    							isTimerStarted = true;
    						}
    
    						// set mines on first click
    						if (!areMinesSet)
    						{
    							areMinesSet = true;
    							setMines(currentRow, currentColumn);
    						}
    
    						// this is not first click
    						// check if current block is flagged
    						// if flagged the don't do anything
    						// as that operation is handled by LongClick
    						// if block is not flagged then uncover nearby blocks
    						// till we get numbered mines
    						if (!blocks[currentRow][currentColumn].isFlagged())
    						{
    							// open nearby blocks till we get numbered blocks
    							rippleUncover(currentRow, currentColumn);
    							
    							// did we clicked a mine
    							if (blocks[currentRow][currentColumn].hasMine())
    							{
    								// Oops, game over
    								finishGame(currentRow,currentColumn);
    							}
    
    							// check if we win the game
    							if (checkGameWin())
    							{
    								// mark game as win
    								winGame();
    							}
    						}
    					}
    				});
    
    				// add Long Click listener
    				// this is treated as right mouse click listener
    				blocks[row][column].setOnLongClickListener(new OnLongClickListener()
    				{
    					public boolean onLongClick(View view)
    					{
    						// simulate a left-right (middle) click
    						// if it is a long click on an opened mine then
    						// open all surrounding blocks
    						if (!blocks[currentRow][currentColumn].isCovered() && (blocks[currentRow][currentColumn].getNumberOfMinesInSorrounding() > 0) && !isGameOver)
    						{
    							int nearbyFlaggedBlocks = 0;
    							for (int previousRow = -1; previousRow < 2; previousRow++)
    							{
    								for (int previousColumn = -1; previousColumn < 2; previousColumn++)
    								{
    									if (blocks[currentRow + previousRow][currentColumn + previousColumn].isFlagged())
    									{
    										nearbyFlaggedBlocks++;
    									}
    								}
    							}
    
    							// if flagged block count is equal to nearby mine count
    							// then open nearby blocks
    							if (nearbyFlaggedBlocks == blocks[currentRow][currentColumn].getNumberOfMinesInSorrounding())
    							{
    								for (int previousRow = -1; previousRow < 2; previousRow++)
    								{
    									for (int previousColumn = -1; previousColumn < 2; previousColumn++)
    									{
    										// don't open flagged blocks
    										if (!blocks[currentRow + previousRow][currentColumn + previousColumn].isFlagged())
    										{
    											// open blocks till we get numbered block
    											rippleUncover(currentRow + previousRow, currentColumn + previousColumn);
    
    											// did we clicked a mine
    											if (blocks[currentRow + previousRow][currentColumn + previousColumn].hasMine())
    											{
    												// oops game over
    												finishGame(currentRow + previousRow, currentColumn + previousColumn);
    											}
    
    											// did we win the game
    											if (checkGameWin())
    											{
    												// mark game as win
    												winGame();
    											}
    										}
    									}
    								}
    							}
    
    							// as we no longer want to judge this gesture so return
    							// not returning from here will actually trigger other action
    							// which can be marking as a flag or question mark or blank
    							return true;
    						}
    
    						// if clicked block is enabled, clickable or flagged
    						if (blocks[currentRow][currentColumn].isClickable() && 
    								(blocks[currentRow][currentColumn].isEnabled() || blocks[currentRow][currentColumn].isFlagged()))
    						{
    
    							// for long clicks set:
    							// 1. empty blocks to flagged
    							// 2. flagged to question mark
    							// 3. question mark to blank
    
    							// case 1. set blank block to flagged
    							if (!blocks[currentRow][currentColumn].isFlagged() && !blocks[currentRow][currentColumn].isQuestionMarked())
    							{
    								blocks[currentRow][currentColumn].setBlockAsDisabled(false);
    								blocks[currentRow][currentColumn].setFlagIcon(true);
    								blocks[currentRow][currentColumn].setFlagged(true);
    								minesToFind--; //reduce mine count
    								updateMineCountDisplay();
    							}
    							// case 2. set flagged to question mark
    							else if (!blocks[currentRow][currentColumn].isQuestionMarked())
    							{
    								blocks[currentRow][currentColumn].setBlockAsDisabled(true);
    								blocks[currentRow][currentColumn].setQuestionMarkIcon(true);
    								blocks[currentRow][currentColumn].setFlagged(false);
    								blocks[currentRow][currentColumn].setQuestionMarked(true);
    								minesToFind++; // increase mine count
    								updateMineCountDisplay();
    							}
    							// case 3. change to blank square
    							else
    							{
    								blocks[currentRow][currentColumn].setBlockAsDisabled(true);
    								blocks[currentRow][currentColumn].clearAllIcons();
    								blocks[currentRow][currentColumn].setQuestionMarked(false);
    								// if it is flagged then increment mine count
    								if (blocks[currentRow][currentColumn].isFlagged())
    								{
    									minesToFind++; // increase mine count
    									updateMineCountDisplay();
    								}
    								// remove flagged status
    								blocks[currentRow][currentColumn].setFlagged(false);
    							}
    							
    							updateMineCountDisplay(); // update mine display
    						}
    
    						return true;
    					}
    				});
    			}
    		}
    	}
    
    	private boolean checkGameWin()
    	{
    		for (int row = 1; row < numberOfRowsInMineField + 1; row++)
    		{
    			for (int column = 1; column < numberOfColumnsInMineField + 1; column++)
    			{
    				if (!blocks[row][column].hasMine() && blocks[row][column].isCovered())
    				{
    					return false;
    				}
    			}
    		}
    		return true;
    	}
    
    	private void updateMineCountDisplay()
    	{
    		if (minesToFind < 0)
    		{
    			txtMineCount.setText(Integer.toString(minesToFind));
    		}
    		else if (minesToFind < 10)
    		{
    			txtMineCount.setText("00" + Integer.toString(minesToFind));
    		}
    		else if (minesToFind < 100)
    		{
    			txtMineCount.setText("0" + Integer.toString(minesToFind));
    		}
    		else
    		{
    			txtMineCount.setText(Integer.toString(minesToFind));
    		}
    	}
    
    	private void winGame()
    	{
    		stopTimer();
    		isTimerStarted = false;
    		isGameOver = true;
    		minesToFind = 0; //set mine count to 0
    
    		//set icon to cool dude
    		btnSmile.setBackgroundResource(R.drawable.cool);
    
    		updateMineCountDisplay(); // update mine count
    
    		// disable all buttons
    		// set flagged all un-flagged blocks
    		for (int row = 1; row < numberOfRowsInMineField + 1; row++)
    		{
    			for (int column = 1; column < numberOfColumnsInMineField + 1; column++)
    			{
    				blocks[row][column].setClickable(false);
    				if (blocks[row][column].hasMine())
    				{
    					blocks[row][column].setBlockAsDisabled(false);
    					blocks[row][column].setFlagIcon(true);
    				}
    			}
    		}
    
    		// show message
    		showDialog("You won in " + Integer.toString(secondsPassed) + " seconds!", 1000, false, true);
    	}
    
    	private void finishGame(int currentRow, int currentColumn)
    	{
    		isGameOver = true; // mark game as over
    		stopTimer(); // stop timer
    		isTimerStarted = false;
    		btnSmile.setBackgroundResource(R.drawable.sad);
    
    		// show all mines
    		// disable all blocks
    		for (int row = 1; row < numberOfRowsInMineField + 1; row++)
    		{
    			for (int column = 1; column < numberOfColumnsInMineField + 1; column++)
    			{
    				// disable block
    				blocks[row][column].setBlockAsDisabled(false);
    				
    				// block has mine and is not flagged
    				if (blocks[row][column].hasMine() && !blocks[row][column].isFlagged())
    				{
    					// set mine icon
    					blocks[row][column].setMineIcon(false);
    				}
    
    				// block is flagged and doesn't not have mine
    				if (!blocks[row][column].hasMine() && blocks[row][column].isFlagged())
    				{
    					// set flag icon
    					blocks[row][column].setFlagIcon(false);
    				}
    
    				// block is flagged
    				if (blocks[row][column].isFlagged())
    				{
    					// disable the block
    					blocks[row][column].setClickable(false);
    				}
    			}
    		}
    
    		// trigger mine
    		blocks[currentRow][currentColumn].triggerMine();
    
    		// show message
    		showDialog("You tried for " + Integer.toString(secondsPassed) + " seconds!", 1000, false, false);
    	}
    
    
    	private void setMines(int currentRow, int currentColumn)
    	{
    		// set mines excluding the location where user clicked
    		Random rand = new Random();
    		int mineRow, mineColumn;
    
    		for (int row = 0; row < totalNumberOfMines; row++)
    		{
    			mineRow = rand.nextInt(numberOfColumnsInMineField);
    			mineColumn = rand.nextInt(numberOfRowsInMineField);
    			if ((mineRow + 1 != currentColumn) || (mineColumn + 1 != currentRow))
    			{
    				if (blocks[mineColumn + 1][mineRow + 1].hasMine())
    				{
    					row--; // mine is already there, don't repeat for same block
    				}
    				// plant mine at this location
    				blocks[mineColumn + 1][mineRow + 1].plantMine();
    			}
    			// exclude the user clicked location
    			else
    			{
    				row--;
    			}
    		}
    
    		int nearByMineCount;
    
    		// count number of mines in surrounding blocks 
    		for (int row = 0; row < numberOfRowsInMineField + 2; row++)
    		{
    			for (int column = 0; column < numberOfColumnsInMineField + 2; column++)
    			{
    				// for each block find nearby mine count
    				nearByMineCount = 0;
    				if ((row != 0) && (row != (numberOfRowsInMineField + 1)) && (column != 0) && (column != (numberOfColumnsInMineField + 1)))
    				{
    					// check in all nearby blocks
    					for (int previousRow = -1; previousRow < 2; previousRow++)
    					{
    						for (int previousColumn = -1; previousColumn < 2; previousColumn++)
    						{
    							if (blocks[row + previousRow][column + previousColumn].hasMine())
    							{
    								// a mine was found so increment the counter
    								nearByMineCount++;
    							}
    						}
    					}
    
    					blocks[row][column].setNumberOfMinesInSurrounding(nearByMineCount);
    				}
    				// for side rows (0th and last row/column)
    				// set count as 9 and mark it as opened
    				else
    				{
    					blocks[row][column].setNumberOfMinesInSurrounding(9);
    					blocks[row][column].OpenBlock();
    				}
    			}
    		}
    	}
    
    	private void rippleUncover(int rowClicked, int columnClicked)
    	{
    		// don't open flagged or mined rows
    		if (blocks[rowClicked][columnClicked].hasMine() || blocks[rowClicked][columnClicked].isFlagged())
    		{
    			return;
    		}
    
    		// open clicked block
    		blocks[rowClicked][columnClicked].OpenBlock();
    
    		// if clicked block have nearby mines then don't open further
    		if (blocks[rowClicked][columnClicked].getNumberOfMinesInSorrounding() != 0 )
    		{
    			return;
    		}
    
    		// open next 3 rows and 3 columns recursively
    		for (int row = 0; row < 3; row++)
    		{
    			for (int column = 0; column < 3; column++)
    			{
    				// check all the above checked conditions
    				// if met then open subsequent blocks
    				if (blocks[rowClicked + row - 1][columnClicked + column - 1].isCovered()
    						&& (rowClicked + row - 1 > 0) && (columnClicked + column - 1 > 0)
    						&& (rowClicked + row - 1 < numberOfRowsInMineField + 1) && (columnClicked + column - 1 < numberOfColumnsInMineField + 1))
    				{
    					rippleUncover(rowClicked + row - 1, columnClicked + column - 1 );
    				} 
    			}
    		}
    		return;
    	}
    
    	public void startTimer()
    	{
    		if (secondsPassed == 0) 
    		{
    			timer.removeCallbacks(updateTimeElasped);
    			// tell timer to run call back after 1 second
    			timer.postDelayed(updateTimeElasped, 1000);
    		}
    	}
    
    	public void stopTimer()
    	{
    		// disable call backs
    		timer.removeCallbacks(updateTimeElasped);
    	}
    
    	// timer call back when timer is ticked
    	private Runnable updateTimeElasped = new Runnable()
    	{
    		public void run()
    		{
    			long currentMilliseconds = System.currentTimeMillis();
    			++secondsPassed;
    
    			if (secondsPassed < 10)
    			{
    				txtTimer.setText("00" + Integer.toString(secondsPassed));
    			}
    			else if (secondsPassed < 100)
    			{
    				txtTimer.setText("0" + Integer.toString(secondsPassed));
    			}
    			else
    			{
    				txtTimer.setText(Integer.toString(secondsPassed));
    			}
     
    			// add notification
    			timer.postAtTime(this, currentMilliseconds);
    			// notify to call back after 1 seconds
    			// basically to remain in the timer loop
    			timer.postDelayed(updateTimeElasped, 1000);
    		}
    	};
    	
    	private void showDialog(String message, int milliseconds, boolean useSmileImage, boolean useCoolImage)
    	{
    		// show message
    		Toast dialog = Toast.makeText(
    				getApplicationContext(),
    				message,
    				Toast.LENGTH_LONG);
    
    		dialog.setGravity(Gravity.CENTER, 0, 0);
    		LinearLayout dialogView = (LinearLayout) dialog.getView();
    		ImageView coolImage = new ImageView(getApplicationContext());
    		if (useSmileImage)
    		{
    			coolImage.setImageResource(R.drawable.smile);
    		}
    		else if (useCoolImage)
    		{
    			coolImage.setImageResource(R.drawable.cool);
    		}
    		else
    		{
    			coolImage.setImageResource(R.drawable.sad);
    		}
    		dialogView.addView(coolImage, 0);
    		dialog.setDuration(milliseconds);
    		dialog.show();
    	}
    }
    

        
    最新技术文章:
    ▪Android开发之登录验证实例教程
    ▪Android开发之注册登录方法示例
    ▪Android获取手机SIM卡运营商信息的方法
    ▪Android实现将已发送的短信写入短信数据库的...
    ▪Android发送短信功能代码
    ▪Android根据电话号码获得联系人头像实例代码
    ▪Android中GPS定位的用法实例
    ▪Android实现退出时关闭所有Activity的方法
    ▪Android实现文件的分割和组装
    ▪Android录音应用实例教程
    ▪Android双击返回键退出程序的实现方法
    ▪Android实现侦听电池状态显示、电量及充电动...
    ▪Android获取当前已连接的wifi信号强度的方法
    ▪Android实现动态显示或隐藏密码输入框的内容
    ▪根据USER-AGENT判断手机类型并跳转到相应的app...
    ▪Android Touch事件分发过程详解
    ▪Android中实现为TextView添加多个可点击的文本
    ▪Android程序设计之AIDL实例详解
    ▪Android显式启动与隐式启动Activity的区别介绍
    ▪Android按钮单击事件的四种常用写法总结
    ▪Android消息处理机制Looper和Handler详解
    ▪Android实现Back功能代码片段总结
    ▪Android实用的代码片段 常用代码总结
    ▪Android实现弹出键盘的方法
    ▪Android中通过view方式获取当前Activity的屏幕截...
    ▪Android提高之自定义Menu(TabMenu)实现方法
    ▪Android提高之多方向抽屉实现方法
    ▪Android提高之MediaPlayer播放网络音频的实现方法...
    ▪Android提高之MediaPlayer播放网络视频的实现方法...
    ▪Android提高之手游转电视游戏的模拟操控
     


    站内导航:


    特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,,E-mail:www_#163.com(请将#改为@)

    浙ICP备11055608号-3