2012年8月30日 星期四

Android:藉由VelocityTracker偵測觸控滑動方向


package com.example.mytouch2;

import...

public class MainActivity extends Activity {
private TextView msg;
private StringBuilder data;
//宣告觸控速率追蹤物件之變數
private VelocityTracker tracker;
private LinearLayout rootlayout;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
data = new StringBuilder();
msg = (TextView) findViewById(R.id.msg);
msg.setTextSize(26);
rootlayout = (LinearLayout) findViewById(R.id.rootlayout);
//註冊觸控監聽物件以及處理
rootlayout.setOnTouchListener(new OnTouchListener() {

public boolean onTouch(View view, MotionEvent event) {
// TODO Auto-generated method stub
switch (event.getAction()) {

case MotionEvent.ACTION_DOWN:
data.delete(0, data.length());
msg.setText("");
//取得觸控速率追蹤物件
tracker = VelocityTracker.obtain();
break;

case MotionEvent.ACTION_MOVE:
// 將觸控相關情境資料加入tracker中
tracker.addMovement(event);
break;

case MotionEvent.ACTION_UP:
// 計算目前速率,以每微秒1個像素為單位
tracker.computeCurrentVelocity(1);
/*
* 判斷X分量與Y分量何者較大(必須取絕對值),
* |x|>|Y|代表為接近水平方向, 其中,X>0為向右;X<0為向左
* |x|>|Y|代表為接近垂直方向, 其中,Y>0為向下;Y<0為向上                                
*/
if (Math.abs(tracker.getXVelocity()) > Math.abs(tracker
.getYVelocity())) {
if (tracker.getXVelocity() > 0) {
// 水平,向右
msg.setText(String.format("水平(左->右)X:%.4f%nY:%.4f",
tracker.getXVelocity(),
tracker.getYVelocity()));
rootlayout.setBackgroundColor(Color.BLUE);
} else {
// 水平向左
msg.setText(String.format("水平(右->左)X:%.4f%nY:%.4f",
tracker.getXVelocity(),
tracker.getYVelocity()));
rootlayout.setBackgroundColor(Color.YELLOW);
}

} else {
if (tracker.getYVelocity() > 0) {
// 垂直向下
msg.setText(String.format("垂直(上->下)X:%.4f%nY:%.4f",
tracker.getXVelocity(),
tracker.getYVelocity()));
rootlayout.setBackgroundColor(Color.RED);
} else {
// 垂直向上
msg.setText(String.format("垂直(上->下)X:%.4f%nY:%.4f",
tracker.getXVelocity(),
tracker.getYVelocity()));
rootlayout.setBackgroundColor(Color.GREEN);
}

}
break;

}

return true;
}

});

}

}

Android:多點觸控之應用以及取得螢幕解析度


package com.example.my_touch3;

import ...

public class MainActivity extends Activity {
private int x,y;//圓心
private int gRadius=100;//預設圓半徑
private int touchx1,touchy1,touchx2,touchy2;//用來判斷當兩點觸控圓心所用
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new TouchView(this));
       
        // 取得螢幕解析度,dm.widthPixels為寬,dm.heightPixels為高
        DisplayMetrics dm = new DisplayMetrics();
        this.getWindowManager().getDefaultDisplay().getMetrics(dm);
        x = (dm.widthPixels)/2;
        y = (dm.heightPixels)/2;
    }
//自建的類別,繼承View,當成活動之畫面呈現
private class TouchView extends View{
private Paint paint;
    //類別建構式
public TouchView(Context context) {
super(context);
paint=new Paint();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//設定畫筆顏色,反鋸齒,挖空心
paint.setColor(0xffff0000);
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
//畫圓 (圓心X,圓心Y,半徑,畫筆)
canvas.drawCircle(x, y, gRadius, paint);
}
//螢幕觸控事件處理,這邊是簡易方法只能偵測layout,無法針對特定元件進行監聽
@Override
public boolean onTouchEvent(MotionEvent event) {
int pointerNum=event.getPointerCount();
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
if(pointerNum==1){
//單點觸控時
x=(int) event.getX();
y=(int) event.getY();

}else if(pointerNum==2){
//兩點觸控時
touchx1=(int)event.getX(0);
touchy1=(int)event.getY(0);
touchx2=(int)event.getX(1);
touchy2=(int)event.getY(1);

x=(int)(touchx1+touchx2)/2;
y=(int)(touchy1+touchy2)/2;
//取得圓半徑的公式
gRadius=(int)(Math.sqrt( (Math.pow(touchx1-touchx2,2))+ (Math.pow(touchy1-
                                                      touchy2,2))));


}
invalidate();//自動呼叫onDraw方法
break;
case MotionEvent.ACTION_UP:
break;
}
return true;

}


}
}

Android:多點觸控偵測以及螢幕觸控事件監聽




package com.example.my_touch1;

import ....
public class MainActivity extends Activity {
LinearLayout layout;
TextView msg;
private StringBuilder data;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        layout=(LinearLayout)findViewById(R.id.rootlayout);
        msg=(TextView)findViewById(R.id.textView1);
        data=new StringBuilder();
        //註冊觸控監聽物件
        layout.setOnTouchListener(new MytouchListener());
    }
   //觸控監聽物件之類別,需實作OnTouchListener
   private class MytouchListener implements OnTouchListener{
   //實作OnTouchListener所需實作之方法
public boolean onTouch(View view, MotionEvent event) {
// 判斷觸控事件
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
data.delete(0, data.length());
msg.setText(data.append("Action down"+"("+event.getX()+","+event.getY()+")"+"\n"));
break;
case MotionEvent.ACTION_UP:
msg.setText(data.append("Action up"+"("+event.getX()+","+event.getY()+")"+"\n"));
break;
case MotionEvent.ACTION_MOVE:
msg.setText(data.append("Action move"+"("+event.getX()+","+event.getY()+")"+"\n"));
break;
}
//取得多點觸控的點數
int pCount=event.getPointerCount();
data.append("多點觸控點數為:"+pCount+"\n");
for(int i=0;i<pCount;i++){
//(event.getX(0),event.getY(0))代表第一點觸控點之座標
data.append(String.format("觸控點%d:(%.2f,%.2f)%n",
                                                                   event.getPointerId(i),event.getX(i),event.getY(i)));

}
msg.setText(data);
return true;
}

   }
}

2012年8月21日 星期二

Android:手勢(Gestrue)相關應用

自從SDK1.6之後,可以從模擬器內建之Gesture Builder建立來建立自己的手勢
可以選擇將模擬器建立好隻手勢資料庫放進專案 raw中,則可以發佈到任何Android



XML Layout


  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="vertical" >

           <!-- 建立GestureOverlayView 用來呈現手勢-->
           <android.gesture.GestureOverlayView
            android:id="@+id/gestureOverlayView1"
            android:layout_width="match_parent"
            android:layout_height="250dp"    
            android:background="#88333333" >
        </android.gesture.GestureOverlayView>

           <TextView
               android:id="@+id/textView1"
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:text="TextView" />


    </LinearLayout>

JAVA code


package com.example.mygestures;

import...

public class MainActivity extends Activity {
private GestureLibrary lib;

private TextView msg;
        //建立一個File物件變數,主要是要呈現函式資料庫於SD卡的位置
private final File PATH = new File(Environment.getExternalStorageDirectory(),
"gestures");
private final float SCORE = 3.0f;// 自訂比對成功分數基本值

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
msg = (TextView) findViewById(R.id.textView1);
GestureOverlayView gestures = (GestureOverlayView)        
                                                                   findViewById(R.id.gestureOverlayView1);

//登記監聽物件
gestures.addOnGesturePerformedListener(new MyGestureListener());

//兩種取得手勢函式庫的方法,從SD卡取得或是內建於專案res/raw資料夾中
/*GestureLibraries為類別GestureLibrary之工具類別,用來進行多種操作,
EX:幫助GestureLibrary建立並取得對應之手勢函式庫*/
lib = GestureLibraries.fromFile(PATH);
//lib=GestureLibraries.fromRawResource(this, R.raw.gestures);//從專案Raw中取得
//若手勢函式庫載入失敗,則直接結束活動
if (!lib.load()) {
finish();
}

}

// 內部監聽類別, 實作OnGesturePerformedListener介面
private class MyGestureListener implements OnGesturePerformedListener {

public void onGesturePerformed(GestureOverlayView overlay,
Gesture gesture) {
// TODO Auto-generated method stub
// 比對gesture取得可能吻合之手勢資料,並存入list中
ArrayList<Prediction> predictions = lib.recognize(gesture);
// 判斷gesture資料庫是否有資料
if (predictions.size() > 0) {
Prediction prediction = predictions.get(0);// 通常index=0代表是集合中分數
                                                                                                   最高的項目

                                        //當分數大於我們預先設定的分數,則辨識成功
                                        if (prediction.score > SCORE) {
/*Prediction類別的變數name,便是當初在模擬器中建立欲辨別
                                           的字串,之後若是要加以延伸之範例,主要便是利用此字串
                                           來判斷要 進行 何種活動
*/

msg.setText("比對到了\n比對資料:" + prediction.name + "\n比對分數:"
+ prediction.score);
} else {
msg.setText("比對不到" + "\n比對分數:" + prediction.score);
}
} else {
msg.setText("無資料");
}

}

}

}


2012年8月17日 星期五

Android:Surface以及Canvas高速繪圖(利用Canvas繪製Bitmap)


兩個矩形不斷作變化



package com.example.mysurfacetest;

import ....

public class MainActivity extends Activity {

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//將主程式畫面設給繼承SurfaceView的自建內部類別
setContentView(new MySurface(this));
}

//自建內部類別必須繼承SurfaceView以及實作SurfaceHolder.Callback
//並實作其抽象Callback方法
public class MySurface extends SurfaceView implements
SurfaceHolder.Callback {
//建立以及宣告Handler來管理以及調派執行緒
final private Handler handler = new Handler();
//宣告SurfaceHolder,之後用來取得Canvas物件
SurfaceHolder holder;
boolean flag;
//宣告畫筆變數以及建立物件
Paint mPaint = new Paint();
               //建構式
public MySurface(Context context) {
super(context);
// TODO Auto-generated constructor stub
//取得SurfaceHolder物件
holder = this.getHolder();
//addCallback()設定callback函式
holder.addCallback(this);
flag = true;
}

//繪圖的工作在自建執行緒t中執行
Thread t = new Thread(new Runnable() {

public void run() {
// TODO Auto-generated method stub
drawPicture();
}

});
               //Callback
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
                //Callback
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
//啟動執行緒
t.start();
}
                 //Callback
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
handler.removeCallbacks(t);
}

protected void drawPicture() {
// TODO Auto-generated method stub

// Bitmap bitmap=BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher);
                        //鎖定畫布,並利用SurfaceHolder取得canvas物件
Canvas c = holder.lockCanvas();
if (flag) {
mPaint.setColor(Color.RED);
// c.drawBitmap(bitmap, bitmap.getWidth(), bitmap.getHeight(), null);
//用Canvas繪圖
//RectF(原點X軸,原點Y軸,寬,高)
c.drawRect(new RectF(0, 100, 1200, 600), mPaint);
flag = false;
} else {
mPaint.setColor(Color.BLUE);
c.drawRect(new RectF(380, 380, 760, 760), mPaint);
flag = true;
}
//解鎖畫布
holder.unlockCanvasAndPost(c);
//執行緒t暫停1秒後再跑一次
handler.postDelayed(t, 1000);

}

}
}



Android:[轉] Surface、SurfaceView、SurfaceHolder及SurfaceHolder.Callback之间的关系


一、Surface
Surface就是“表面”的意思。在SDK的文档中,对Surface的描述是这样的:“Handle onto a raw buffer that is being managed by the screen compositor”,翻译成中文就是“由屏幕显示内容合成器(screen compositor)所管理的原生缓冲器的句柄”,这句话包括下面两个意思:
1.      通过Surface(因为Surface是句柄)就可以获得原生缓冲器以及其中的内容。就像在C语言中,可以通过一个文件的句柄,就可以获得文件的内容一样;
2.      原生缓冲器(rawbuffer)是用于保存当前窗口的像素数据的。
引伸地,可以认为Android中的Surface就是一个用来画图形(graphics)或图像(image)的地方。根据Java方面的常规知识,我们知道通常画图是在一个Canvas对象上面进行的,由此,可以推知一个Surface对象中应该包含有一个Canvas对象,事实上的确如此,而且这一点可以很容易通过debug运行程序的方式得到证明(将光标停留在对象变量surface上,会弹出一个对话框,其中红色方框的内容,就表面surface中有一个CompatileCanvas成员变量)当然,看源代码也是可以证明这一点:
因此,在前面提及的两个意思的基础上,可以再加上一条:
3.      Surface中有一个Canvas成员,专门用于画图的。
所以,Surface中的Canvas成员,是专门用于供程序员画图的场所,就像黑板一样;其中的原生缓冲器是用来保存数据的地方;Surface本身的作用类似一个句柄,得到了这个句柄就可以得到其中的Canvas、原生缓冲器以及其它方面的内容。

二、SurfaceView
SurfaceView,顾名思义就是Surface的View,通过SurfaceView就可以看到Surface的部分或者全部的内容,下面用一个图来形象地描述一下Surface和SurfaceView的关系:
也就是说,Surface是用通过SurfaceView才能展示其中的内容。从这个意思上来说,SurfaceView中的View之确切的含义应该是viewport即“视口”的意思,做过数据库设计的朋友知道,假定一个数据表有20个字段,但我们常常只用到其中的5个字段,那么就可以在原数据表的基础上,通过SQL语句CREATEVIEW来创建只包含那5个字段内容的view。
另一方面,SurfaceView是Android中View的子类。事实上,在Android中所有用于界面展示的类皆为View的子类,包括那些不可见的、各种各样的Layout。
所以说,SurfaceView中的View有两个含义:
1.      视口(viewport)的意思
2.      SurfaceView是View的派生类

在Android中Surface是从Object派生而来,且实现了Parcelable接口。看到Parcelable就让人能很自然地想到数据容器,SurfaceView就是用来展示Surface中的数据的。在这个层面上而言,Surface就是管理数据的地方,SurfaceView就是展示数据的地方。

三、SurfaceHolder
SurfaceHolder是一个接口,其作用就像一个关于Surface的监听器。提供访问和控制SurfaceView背后的Surface 相关的方法 (providingaccess and control over this SurfaceView's underlying surface),它通过三个回调方法,让我们可以感知到Surface的创建、销毁或者改变。在SurfaceView中有一个方法getHolder,可以很方便地获得SurfaceView所对应的Surface所对应的SurfaceHolder(有点拗口吧)。

除下面将要提到的SurfaceHolder.Callback外,SurfaceHolder还提供了很多重要的方法,其中最重要的就是:
1.        abstract void addCallback(SurfaceHolder.Callbackcallback)
为SurfaceHolder添加一个SurfaceHolder.Callback回调接口。
2.        abstract Canvas lockCanvas()
获取一个Canvas对象,并锁定之。所得到的Canvas对象,其实就是Surface中一个成员。
3.        abstract Canvas lockCanvas(Rectdirty)
同上。但只锁定dirty所指定的矩形区域,因此效率更高。
4.        abstract void unlockCanvasAndPost(Canvascanvas)
当修改Surface中的数据完成后,释放同步锁,并提交改变,然后将新的数据进行展示,同时Surface中相关数据会被丢失。
5.      public abstract void setType (int type)
         设置Surface的类型,接收如下的参数:
SURFACE_TYPE_NORMAL:用RAM缓存原生数据的普通Surface
SURFACE_TYPE_HARDWARE:适用于DMA(Direct memory access )引擎和硬件加速的Surface
SURFACE_TYPE_GPU:适用于GPU加速的Surface
SURFACE_TYPE_PUSH_BUFFERS:表明该Surface不包含原生数据,Surface用到的数据由其他对象提供,在Camera图像预览中就使用该类型的Surface,有Camera负责提供给预览Surface数据,这样图像预览会比较流畅。如果设置这种类型则就不能调用lockCanvas来获取Canvas对象了。需要注意的是,在高版本的Android SDK中,setType这个方法已经被depreciated了。
2、3、4中的同步锁机制的目的,就是为了在绘制的过程中,Surface中的数据不会被改变。

从设计模式的高度来看,Surface、SurfaceView和SurfaceHolder实质上就是广为人知的MVC,即Model-View-Controller。Model就是模型的意思,或者说是数据模型,或者更简单地说就是数据,也就是这里的Surface;View即视图,代表用户交互界面,也就是这里的SurfaceView;SurfaceHolder很明显可以理解为MVC中的Controller(控制器)。这样看起来三者之间的关系就清楚了很多。

四、SurfaceHolder.Callback
前面已经讲到SurfaceHolder是一个接口,它通过回到方法的方式,让我们可以感知到Surface的创建、销毁或者改变。其实这一点是通过其内部的静态子接口SurfaceHolder.Callback来实现的。SurfaceHolder.Callback中定义了三个接口方法:
1.     abstract void surfaceChanged(SurfaceHolderholder, int format, int width, int height)
当surface发生任何结构性的变化时(格式或者大小),该方法就会被立即调用。
2.      abstract void surfaceCreated(SurfaceHolderholder)
当surface对象创建后,该方法就会被立即调用。
3.      abstract void surfaceDestroyed(SurfaceHolderholder)
当surface对象在将要销毁前,该方法会被立即调用。

在Android SDK文档中,关于SurfaceView的描述里面,有一段这样的话:
One of the purposes of this class is to provide a surface in which a secondarythread can render into the screen. If you are going to use it this way, youneed to be aware of some threading semantics:

-        All SurfaceView and SurfaceHolder.Callbackmethods will be called from the thread running the SurfaceView's window(typically the main thread of the application). They thus need to correctlysynchronize with any state that is also touched by the drawing thread.
-      You must ensure that the drawingthread only touches the underlying Surface while it is valid -- betweenSurfaceHolder.Callback.surfaceCreated() andSurfaceHolder.Callback.surfaceDestroyed().
这段话很重要,大致意思如下:
这个类的目的之一,就是提供一个可以用另外一个线程(第二个线程)进行屏幕渲染的surface(译注:即UI线程和绘制线程可以分离)。如果你打算这样使用,那么应当注意一些线程方面的语义:
-           所有SurfaceView和SurfaceHolder.Callback中声明的方法,必须在运行SurfaceView窗口中的线程中调用(典型地,就是应用的主线程。译注:即UI线程),因为它们需要正确地将同时被绘制线程访问的各种状态进行同步。
-           必须保证,只有在背后的Surface有效的时候 – 在SurfaceHolder.Callback.surfaceCreated()和 SurfaceHolder.Callback.surfaceDestroyed()这两个方法调用之间,访问它。

2012年8月15日 星期三

Android:利用AnimationDrawable建立頁框動畫

於專案的res檔中建立drawable或是anim資料夾

利用XML建立動畫資源檔,之後再將它呼叫進來使用比較簡單

res/anum/frame.xml
<?xml version="1.0" encoding="utf-8"?>
<animation-list
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="true">

    <item
        android:drawable="@drawable/anim1"
        android:duration="50"/>
    <item
        android:drawable="@drawable/anim2"
        android:duration="50"/>
    <item
        android:drawable="@drawable/anim3"
        android:duration="50"/>
     <item
        android:drawable="@drawable/ic_action_search"
        android:duration="50"/>
</animation-list>

主程式

package com.example.frameanim;

import ...

public class MainActivity extends Activity {
//宣告AnimationDrawable物件變數
AnimationDrawable ad;
//宣告ImageView,動畫會顯示在上面
ImageView iv;
Button but1;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
but1 = (Button) findViewById(R.id.button1);
iv = (ImageView) findViewById(R.id.imageView1);
        //取得資源物件,再取得動畫資源檔,取回來之後必須轉型回與變數相同的型別
ad=(AnimationDrawable) this.getResources().getDrawable(R.anim.frame);
        //將AnimationDrawable物件設定給ImageView準備播放
iv.setImageDrawable(ad);
}

@Override
//觸碰螢幕的事件處理
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
if (event.getAction() == MotionEvent.ACTION_DOWN) {
//true為止播一次,false為循環播放
ad.setOneShot(false);
ad.start();
}
return true;
}

public void but1_onclick(View v) {
if (!ad.isRunning()) {
ad.setOneShot(false);
ad.start();
but1.setText("stop");
} else {
ad.stop();
but1.setText("start");
}

}

}


2012年8月9日 星期四

Android:ItemizedOverlay為地圖標上地標


package com.example.mapitemizedoverlay;

import ...

public class MainActivity extends MapActivity {
MapView map;
//因為內部類別會用到,只好把目標地理變數變成全域變數
GeoPoint ntuGeo;
//宣告內部類別變數,此類別繼承ItemizedOverlay,因此代表一個Overlay(圖層)
HelloItemizedLayer mylayer;
//宣告一個List容器,用來裝MapView上的Overlay
List<Overlay> mapoverlay;
   
@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
       
        map=(MapView)findViewById(R.id.mapview);
        MapController  con=map.getController();
        con.setZoom(18);
        map.setBuiltInZoomControls(true);
       
        double x=25.019943,y=121.542353;
        ntuGeo=new GeoPoint((int)(x*1e6), (int)(y*1e6));
        con.animateTo(ntuGeo);
       //Access the overlay list. 取得MapView上維護那些Overlay的列表
        mapoverlay=map.getOverlays();
        //建立Drawable物件,取得其圖檔資源
        Drawable draNtu=this.getResources().getDrawable(R.drawable.ic_launcher);
        //設定邊界
        draNtu.setBounds(0, 0, draNtu.getMinimumWidth(), draNtu.getMinimumHeight());
        //建立內部類別變數物件,建構參數為一個Drawable物件,而預設的建構子方法變會將圖形給繪製出。如底下匿名類別中。類別建構子所示
        mylayer=new HelloItemizedLayer(draNtu);
        //將自訂的類別也就是Overlay(圖層)給加進裝Overlay的List中
        mapoverlay.add(mylayer);
    }

 
@Override
protected boolean isRouteDisplayed() {
// TODO Auto-generated method stub
return false;
}


//內部類別,繼承 ItemizedOverlay
private class HelloItemizedLayer extends ItemizedOverlay<OverlayItem>{
//建構一個裝OverlayItem(圖標)的列表,負責儲存多個圖標,雖然本例只用一個
List<OverlayItem> items=new ArrayList<OverlayItem>();
//建構子,負責將由主程式傳來的圖標設定給繪製出來
public HelloItemizedLayer(Drawable arg0) {
super(arg0);
// TODO Auto-generated constructor stub
//加進一個OverlayItem物件,伴隨著(地點座標物件,標題字串,說明字串)
items.add(new OverlayItem(ntuGeo, "台大(標題)", "台灣大學"));
//此方法負責ItemizedOverlay建構背後所需動作,一定要加!!
populate();

}

@Override
//由populate()呼叫,他會決定哪些圖標會出現在地圖上,並且該圖標物件從items中取
          出
//參數index代表取出物件之索引
protected OverlayItem createItem(int index) {
// TODO Auto-generated method stub
return items.get(index);
}

@Override
//一樣由populate()呼叫,決定要取出幾個圖標物件
public int size() {
// TODO Auto-generated method stub
return items.size();
}

@Override
//使用者點選圖標的反應,在這邊是Show出一個對話欄
protected boolean onTap(int index) {
// TODO Auto-generated method stub
new AlertDialog.Builder( MainActivity.this)
//取得圖標物件的Title
.setTitle(items.get(index).getTitle())
//取得圖標物件的內容說明
.setMessage(items.get(index).getSnippet())
.show();

return true;
}

}
   

}

2012年8月8日 星期三

Android:Tween動畫效果,並結合循環加速器

必須先於專案中res建立anim資料夾,有關動畫敘述的XML檔都放在裡面
循環加速器XML檔

<?xml version="1.0" encoding="utf-8"?>
<!-- 循環加速器 ,android:cycles="XX"可設定循環幾次-->
<cycleInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
    android:cycles="7"

/>

動畫敘述的XML檔

<?xml version="1.0" encoding="utf-8"?>
<!-- set元素可將多種動畫特效結合在一起同時運作
 android:interpolator="@anim/cycle7用來將加速器給導入
 主程式建立動畫物件時,就是以參考此XML檔來建立

此外,如果要讓有順序的播放,則可以利用不同先後順序的set(裡面放動畫元素,ex:scale translate...)來達到 -->
 <set xmlns:android="http://schemas.android.com/apk/res/android"
     android:interpolator="@anim/cycle7">
 
     <scale
        android:fromXScale="1.6"
        android:toXScale="0.6"
        android:fromYScale="1.6"
        android:toYScale="0.6"
        android:pivotX="50%"
        android:pivotY="50%"
        android:duration="3000"
     
        />
      <translate
        android:fromYDelta="0"
        android:fromXDelta="0"
        android:toXDelta="1000"
        android:toYDelta="0"
        android:duration="3000"
       />
   
    </set>



主程式


package com.example.mytween2;

import ...

public class MainActivity extends Activity {
//宣告動畫物件
Animation ani;
ImageView iv;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
     
        iv=(ImageView)findViewById(R.id.imageView1);
        //建立動畫物件
        ani=AnimationUtils.loadAnimation(this, R.anim.translate_cycle_interpolator);
     
     
    }

    public void but1_onclick(View v){
    //將動畫物件設給ImageView,並啟動
    iv.setAnimation(ani);
    iv.startAnimation(ani);
    }
 
}

2012年8月6日 星期一

Android:使用Canvas以及ShapeDrawable繪圖

XML File


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/LinearLayout1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
<!--自建繼承View類別之元件,命名方法為套件名稱.類別名稱,
   圖形會繪製在此處 -->
    <com.example.CustomDrawableView1
        android:id="@+id/CustomDrawableView1"
        android:layout_width="match_parent"
        android:layout_height="420dp" />

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="變更色彩" />

</LinearLayout>

自建繼承View的Class


package com.example;

import ...
//建立一個類別,並繼承View,原先用來設定為程式畫面
//但是本例還是將成果希望可以利用XML排版
public class CustomDrawableView1 extends View {
//宣告一個Drawable子類別ShapeDrawable變數,用來繪製幾何圖形用
ShapeDrawable sd1;
    //自建繼承View類別之建構子,參數部分傳入兩個變數,Context以及AttributeSet
//使用AttributeSet參數後,我們在主程式中連接的XML檔中加入自建類別的元件
//就可以控制將欲產生的幾何圖形產生在該元件中
public CustomDrawableView1(Context context, AttributeSet attr) {
super(context, attr);
// TODO Auto-generated constructor stub
//宣告設定左上點座標以及長寬用的變數
int x1 = 10;
int y1 = 10;
int w1 = 150;
int h1 = 300;
//建立ShapeDrawable物件,產生的物件用來繪製出橢圓形
sd1 = new ShapeDrawable(new OvalShape());
//設定邊界
sd1.setBounds(x1, y1, x1 + w1, y1 + h1);

}

@Override
//繼承View後所產生的CallBack方法
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
int r = (int) (256 * Math.random());
int g = (int) (256 * Math.random());
int b = (int) (256 * Math.random());
//取得畫筆繪製顏色
sd1.getPaint().setARGB(0xff, r, g, b);
//在Canvas畫布上繪製出指定圖形
sd1.draw(canvas);

}

}

主程式

package com.example;

import ...

public class MainActivity extends Activity {
//宣告自建類別變數
CustomDrawableView1 customDrawableView;
Button but1;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findView();
but1.setOnClickListener(new OnClickListener() {

public void onClick(View view) {
// TODO Auto-generated method stub
customDrawableView.invalidate();// 清掉畫面,會自動再呼叫callback方法onDraw()重新繪製
}

});
}

private void findView() {
// TODO Auto-generated method stub
               //在主程式中取得自建類別之物件,來源源自於XML中的一個元件
customDrawableView = (CustomDrawableView1) findViewById(R.id.CustomDrawableView1);
but1 = (Button) findViewById(R.id.button1);

}

}


Android:MapView使用GoogleMap

在建立專案時,BuildSDK要選GoogleAPIs版本的


<Manifest>
開啟網路權限以及地圖之程式庫
<uses-permission android:name="android.permission.INTERNET"/>
<uses-library android:name="com.google.android.maps"/>


<XML>

 <com.google.android.maps.MapView
                android:id="@+id/map"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:apiKey="0lDmUngY4dnrsMtVjU12ySKKSMU-y2G0edVeXXX"
                <--讓地圖可以滑動-->
                android:clickable="true" >


            </com.google.android.maps.MapView>


<JAVA Code>

package com.example.maptest;


import android.annotation.SuppressLint;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Spinner;


import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;


//必須繼承MapActivity並實作指定方法isRouteDisplayed()
public class MainActivity extends MapActivity {
//MapView變數
MapView map;
Spinner spi1,spi2;
String[]name={"台灣大學","清華大學","交通大學","成功大學"};
String[][]loc={{"25.019943","121.542353"},{"24.795621","120.998153"},{"24.791704","121.003341"}
,{"23.000875","120.218017"}};
String[]type={"街道圖","衛星圖"};


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        spi1=(Spinner)findViewById(R.id.spinner1);
        spi2=(Spinner)findViewById(R.id.spinner2);
        setAdapter();
     
        map=(MapView)findViewById(R.id.map);
        //使用MapView變數.getController(),取得MapController物件
        final MapController con=map.getController();
        //設定地圖縮放比例
        con.setZoom(18);
        //加上使用者自行地圖縮放功能
        map.setBuiltInZoomControls(true);
     
        spi1.setOnItemSelectedListener(new OnItemSelectedListener(){


public void onItemSelected(AdapterView<?> arg0, View arg1,
int location, long arg3) {
// TODO Auto-generated method stub
switch(location){
case 0:
startMap(0);
break;
case 1:
startMap(1);
break;
case 2:
startMap(2);
break;
case 3:
startMap(3);
break;
}
}


private void startMap(int i) {
// TODO Auto-generated method stub
//設定地理位置經緯度,Double型別
double x=Double.parseDouble(loc[i][0]);
double y=Double.parseDouble(loc[i][1]);
//宣告GeoPoint變數物件,建構參數必須傳入經緯度,
//並將地理位置座標乘上100,0000或是1e6,並轉回int型別
GeoPoint geo=new GeoPoint((int)(x*1e6), (int)(y*1e6));
//將GeoPoint傳給MapController,並顯示。
                con.animateTo(geo);
}


public void onNothingSelected(AdapterView<?> arg0) {
// TODO Auto-generated method stub

}
       
        });
     
     
        spi2.setOnItemSelectedListener(new OnItemSelectedListener(){


public void onItemSelected(AdapterView<?> arg0, View arg1,
int location, long arg3) {
// TODO Auto-generated method stub
switch(location){
case 0:
//MapView物件變數可使用setSatellite(boolean)來呈現衛星地圖
map.setSatellite(false);
break;
case 1:
map.setSatellite(true);
break;
}
}


public void onNothingSelected(AdapterView<?> arg0) {
// TODO Auto-generated method stub

}
       
        });
    }


 


@SuppressLint("NewApi")
private void setAdapter() {
// TODO Auto-generated method stub
ArrayAdapter ada1=new ArrayAdapter(this, android.R.layout.simple_spinner_item);
ada1.addAll(name);
spi1.setAdapter(ada1);

ArrayAdapter ada2=new ArrayAdapter(this, android.R.layout.simple_spinner_item);
ada2.addAll(type);
spi2.setAdapter(ada2);
}






@Override
protected boolean isRouteDisplayed() {
// TODO Auto-generated method stub
return false;
}


 
}