2010年11月28日 星期日

[NBA] NBA 2k11 CHEATS & CODES



Cheat : Codes

Enter the following codes by choosing "Features," > "Extras" > "Codes."

2ksports - Unlock the 2k Sports Team
2kchina - Unlock the 2k China Team
Payrespect - Unlock the ABA Ball
nba2k - Unlock the NBA2k Development Team
vcteam - Unlock the VC Squad
icanbe23 - MJ: Creating A Legend

2010年11月26日 星期五

[Android] 如何使用Google Translate API 和 Spinner 製作翻譯機



因為秉持Open Source的精神 , 所以Google 發佈了許多有趣的API給開發者拿來發揮創意 , 今天在網路上找到了Google Translate API 想先簡單的搭配 Spinner 做一個翻譯機出來試試 , 由於這個API不是存在Android 的jar檔內 , 所以我們必須先下載Google Translate 的jar檔 , 接著透過Eclipse就可以import jar檔到我們的程式當中了 , 首先到這裡去下載jar檔 , 接著在Eclipse中的 Package Explorer 中選取開發的專案 > Propertires > Java Build Path > 右邊的 Libabries > Add External JARs , 成功之後就可以發現多出一個圖示: Referenced Libabries , 裡面有剛剛加入的jar檔 , 接著試程式碼的部分




package com.android;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.Button;

import com.google.api.translate.Language;
import com.google.api.translate.Translate;

public class GoogleTranslateSpinner extends Activity implements OnItemSelectedListener , Button.OnClickListener
{
 /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        AAdapter = new ArrayAdapter(this,android.R.layout.simple_spinner_item,LanguageNameArray);
        AAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        //ArrayAdapter設定下拉式清單內容的layout格式
        //android. 開頭的範本是由Android提供的
        
        SpinnerFrom = (Spinner) findViewById(R.id.SpinnerFrom);
        SpinnerFrom.setAdapter(AAdapter);
        SpinnerFrom.setOnItemSelectedListener(this);
        SpinnerTo = (Spinner) findViewById(R.id.SpinnerTo);        
        SpinnerTo.setAdapter(AAdapter);
        SpinnerTo.setOnItemSelectedListener(this);        
        
        EditTextFrom = (EditText) findViewById(R.id.EditTextFrom);
        EditTextTo = (EditText) findViewById(R.id.EditTextTo);
        
        Button = (Button) findViewById(R.id.Button);
        Button.setOnClickListener(this);
        
        Translate.setHttpReferrer("http://shung007.blogspot.com/");
        //Translate.setHttpReferrer(" Enter the URL of your site here");
        //必要!!! 否則程式會出現Exception
    }
    
    Spinner SpinnerFrom , SpinnerTo;
    EditText EditTextFrom , EditTextTo;
    Button Button;
    
    int positionFrom , positionTo;
    
    ArrayAdapter AAdapter;
    
    public String[] LanguageNameArray = new String[]{
            "繁體中文", "CHINESE_SIMPLIFIED", "GERMAN", "ENGLISH",
            "JAPANESE", "FRENCH"
        };
    //作為顯示在Spinner裡的清單內容
    
    public Language[] LanguageCode = new Language[]{
        Language.CHINESE_TRADITIONAL, Language.CHINESE_SIMPLIFIED,
        Language.GERMAN, Language.ENGLISH, Language.JAPANESE, Language.FRENCH      
    };
    //相對應的LanguageCode , 作為之後傳遞的引數用    

 @Override
 public void onItemSelected(AdapterView parent, View view, int position,
   long arg)
 {  
  if(parent.equals(SpinnerFrom)) positionFrom = position;
  else positionTo = position;
  //判斷並記錄使用者選擇了哪個語言翻譯
 }

 @Override
 public void onNothingSelected(AdapterView arg0)
 {
  // TODO Auto-generated method stub  
 }

 @Override
 public void onClick(View ButtonView)
 {
  //按下Translate按鈕之後 , 執行翻譯的動作
  try
  {
   EditTextTo.setText(Translate.execute(EditTextFrom.getText().toString(), LanguageCode[positionFrom], LanguageCode[positionTo]));
  } catch (Exception e)
  {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }    
}




值得一提的是他支持翻譯的語言相當多 , 不只我程式中列出的六個 , 從他的Language.java可以看得出來還支持哪些語言 , 另外 , 其實這個api需要透過網路把你輸入的內容傳送到資料庫進行翻譯 , 所以別忘了設定網路權限!!!!

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



最後是Layout的部分













 














整個程式很精簡但是很容易就達到我們的要求 , 因為最複雜的部分google都幫我們解決了 , 真好!




相關文章


[Android] 如何使用 TextToSpeech (TTS)

2010年11月24日 星期三

[Android] 人臉偵測 FaceDetector



今天在找資料的時候無意間發現這個Class : android.media.FaceDetector 覺得相當有趣 , 馬上放下手邊的事情尋找關於FaceDetector的資料 , 找了那麼多資料所以在這邊做個統整 , 首先這邊有關於FaceDetector的Source Code  , 想進一步研究的人可以去參考 , 不過關於API Doc的描述 , 進行偵測的圖片必須是RGB565的格式 , 再來是實作程式碼的部分




import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.media.FaceDetector;
import android.media.FaceDetector.Face;
import android.os.Bundle;
import android.view.View;

 
public class FaceDetector_Test extends Activity
{
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(new FaceView(this));
    }  
    
    public static class FaceView extends View
    {
  int imageWidth, imageHeight;
        int numberOfFace = 10;
        //自訂能偵測出的臉孔數目
        FaceDetector myFaceDetect;
        FaceDetector.Face[] myFace;
        float myEyesDistance;
        int numberOfFaceDetected;
 
        Bitmap myBitmap;
        
        public FaceView(Context context)
        {
   super(context);
   // TODO Auto-generated constructor stub
   
   BitmapFactory.Options BFO = new BitmapFactory.Options();
            BFO.inPreferredConfig = Bitmap.Config.RGB_565;
            //由於FaceDetector只能處理RGB565格式的圖片 , 所以我們要對
            //圖片進行前處理的動作
            
            myBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.play, BFO);
            //從getResources()中的物件找出在res/drawable裡的圖片
            //並以RGB565處理解譯成Bitmap物件
            
            imageWidth = myBitmap.getWidth();
            imageHeight = myBitmap.getHeight();            
            myFaceDetect = new FaceDetector(imageWidth, imageHeight, numberOfFace);
            //Note that the width of the image must be even.
            //Doc中有描述上面這句 , 不過不是很清楚用意
            
            myFace = new FaceDetector.Face[numberOfFace];
            //宣告一個FaceDetector.Face陣列 , 準備儲存所偵測到的臉孔位置資訊
            //所以陣列容量必定要和之前自訂的臉孔可偵測數目一樣
            
            numberOfFaceDetected = myFaceDetect.findFaces(myBitmap, myFace);
        }
        
        @Override
        protected void onDraw(Canvas canvas)
        {
            // TODO Auto-generated method stub
 
            canvas.drawBitmap(myBitmap, 0, 0, null);
 
            Paint myPaint = new Paint();
            myPaint.setColor(Color.GREEN);
            myPaint.setStyle(Paint.Style.STROKE);
            myPaint.setStrokeWidth(3);
 
            for(int i=0; i < numberOfFaceDetected; i++)
            {
             //將每個偵測出來的臉孔當畫上框框 
                Face face = myFace[i];
                PointF myMidPoint = new PointF();
                face.getMidPoint(myMidPoint);
                //兩眼中心點的point
                myEyesDistance = face.eyesDistance();
                //兩眼之間的距離
                canvas.drawRect(
                        (int)(myMidPoint.x - myEyesDistance),
                        (int)(myMidPoint.y - myEyesDistance),
                        (int)(myMidPoint.x + myEyesDistance),
                        (int)(myMidPoint.y + myEyesDistance),
                        myPaint);
                //如果用兩眼的距離能抓到臉長寬的比例
                //畫出的框框大概就可以框住臉了
            }            
        }
    }
}

 戴眼鏡的照片而且成功的結果

 失敗的結果....臉太紅? 眼睛太假?

最後把網路上一些開發者的建議做一個整理:
1. 其實看完程式碼大概可以能知道他是靠偵測眼睛來判斷人臉的位置 , 其他五官的細節就沒辦法偵測到了 , 所以雙眼必須同時出現在照片上才可以進行偵測,並且太陽眼鏡/眼鏡會影響檢測的效果。
2. 對於要偵測圖片的Pixels建議不要小於100x100 pixels否則會偵測不到 , 但是也不要太大 , 否則偵測得過程太久還有可能出現加載圖片例外的狀況 , 大概小於800x800 pixels效果不錯
3. 可以考慮開一個獨立的Thread來處理
4. 經過我的測試結果 , 如果說放了一張多人團體照的話 , 但是建構子中的 int maxFaces 參數設的很小例如"2" , 我猜測此時API會取他所分析出可能是人臉中 , Rank最高的前兩名作回報 , 所以把 maxFaces 調高一點可以讓團體照偵測的結果準很多 , 對於單人照其實沒太大差別。

下面是關於findFaces function的API Doc , 我特別把一段話標了起來

public int findFaces (Bitmap bitmap, Face[] faces)

Since: API Level 1
Finds all the faces found in a given Bitmap. The supplied array is populated with 
FaceDetector.Faces for each face found. The bitmap must be in 565 format (for now).
Parameters
bitmapthe Bitmap graphic to be analyzed
facesan array in which to place all found FaceDetector.Faces. The array must be sized equal to themaxFaces value set at initialization


2010年11月23日 星期二

[Android] 如何使用Google Map 2.0 & 3.0



接續之前的文章 [Android Tips] 如何取得手機的Cell ID 和 Location Area Code , 在我們獲得Cell ID 和 Location Area Code 之後 , 轉譯成我們要的地理座標呢? 這時候需要連上Google Map API接收他轉譯好的經緯度資訊 , 網址為: http://www.google.com/glm/mmap 點進去看到錯誤訊息不要以為他壞掉了 , 其實關於如何夠過上面的網址把Cell ID 和 Location Area Code轉譯成經緯度的資訊  Wei-Meng Lee poor man 的文章說得很清處 , 流程大概就是連接上網頁POST丟入參數然後解析網頁回傳的資訊。


在成功的獲得經緯度資訊後 , 就可以拿他們去呼叫Google Map定位了 , 在Google Map 3.0推出之後 , 官網呼籲開發者把2.0的版本改成3.0的 , 3.0 載入速度更快 , 尤其是在行動裝置的瀏覽器上(但是我用模擬器測試反應倒是有點慢....)


其實這次3.0的作法是希望把API放在伺服器上(有別於2.0的原生API) , 這樣開發者就不必因為Map版本得更心要手動去更新Code , 所以Google想要開發者直接把Google Map的網頁嵌入在程式內 , 所以!!! 到了3.0的版本 , 裝載Google Map的容器換成是WebView了(有別於2.0使用MapView) , 而他們的官方網頁有簡單的教學 。


接下來是2.0的部分 , 其實也不用介紹太多 , 很多網站和書籍都有範例 , 像Google 的Andorid 開發者網站就有這兩篇文章Google Map View 和 Location and Maps , Google Map API 2.0 是需要申請金鑰的 , 關於這個步驟這個網站說明的很詳細




相關文章:

2010年11月22日 星期一

[Android] 如何取得手機的Cell ID 和 Location Area Code



甚麼是Cell ID 和 Location Area Code , 他們的用途是甚麼? 這篇文章說得很清楚了, 那在Android手機上該如何取得呢?其實步驟蠻簡單的 , 只要透過TelephonyManager即可 , 不過TelephonyManager要取得Instance , 一定要透過Context.getSystemService(Context.TELEPHONY_SERVICE).以下是簡單的代碼


if(TM.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM)
        {
         GCLocation = (GsmCellLocation) TM.getCellLocation();
         if (GCLocation != null)
         {
          int CellID = GCLocation.getCid();
                        int LAC = GCLocation.getLac();
                        Log.d("CellID",String.valueOf(CellID));
                        Log.d("LAC",String.valueOf(LAC));
                }
         else Log.d("無法獲得 GsmCellLocation 物件","");
        }
        else Log.d("這不是GSM手機","");


另外在AndoridManifest.xml中要加入一些權限


    
        
            
                
                
            
        
        
    
    
 


不過在模擬器上測試的時候回傳的 GCLocation 是 NULL , 畢竟是模擬器....
而這個opencillid網站提供了不少免費的資料 , 有了這些資料之後就算沒有GPS也可以定位了!!!



相關文章:
[Android Tips] 如何使用Google Map 2.0 & 3.0
[Android] 如何藉由Cell ID 和 Locatin Area Code 取得經緯度座標 (by Geolocation API)

2010年11月20日 星期六

[Android] 如何開啟Camera拍照 (Camera Capture)



這陣子在研究Camera錄影 , 但是網路上很多文章說模擬器似乎不能成功錄影 , 錄音倒是可以 , 所以退而求其次使用Camera來拍照 , 模擬器真的是一個模擬器....不能完全模擬出實機的情況 , 如果把code轉到實機上去跑 , 應該有很多要Debug的東西....下面是Layout佈局 , 沒啥特別的



   
   






如果要開啟Camera鏡頭的話 , 需要在AndroidManifest.xml中註冊屬性

    
        
            
                
                
            
        
    
  
 
  
 


接下來是程式碼的部分
package com.android;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PictureCallback;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.Window;
import android.widget.Button;

public class Camera_Capture extends Activity implements Button.OnClickListener , SurfaceHolder.Callback , Camera.ShutterCallback
{
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);       
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        //這行程式碼也可以隱藏Title Bar但是位置要放對
        setContentView(R.layout.main);
        
        this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        //強制讓螢幕的內容呈現橫向
        
        SurfaceView SV = (SurfaceView) findViewById(R.id.SurfaceView);
        surfaceHolder = SV.getHolder();
        surfaceHolder.addCallback(this);
        surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        
        captureButton = (Button) findViewById(R.id.captureButton);
        captureButton.setOnClickListener(this);
        
        TempFile = this.getFilesDir();
    }
    
    Button captureButton;    
    SurfaceHolder surfaceHolder;    
    
 Camera mCamera;
 Parameters mParameters;
 
 File TempFile;
 
 Camera.PictureCallback TakeJpeg = new Camera.PictureCallback()
 {
  @Override
  public void onPictureTaken(byte[] data, Camera camera)
  {
   // TODO Auto-generated method stub
   
            try {              
                 Bitmap mBitmap = BitmapFactory.decodeByteArray(data, 0, data.length);  
           File temp = File.createTempFile("PhotoTemp", ".jpg", TempFile);
           //在模擬器中開一個暫存檔 路徑為 //data/data/YourPackageName/files/
           //在DDMS視窗中可以看到
           
           BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(temp));  
                 mBitmap.compress(Bitmap.CompressFormat.JPEG, 80, bos);  
                 //採用壓縮轉檔方法
                 //format 只有JPEG , PNG 兩種
                 //quality Hint to the compressor, 0-100. 0 meaning compress for small size, 100 meaning compress for max quality. Some formats, like PNG which is lossless, will ignore the quality setting
                 //stream The outputstream to write the compressed data.
                                
                 bos.flush();
                 bos.close();
   } catch (IOException e)
   {    
    e.printStackTrace();
   }   
   mCamera.startPreview();
   //由於拍照完後 , PreView會停止
   //故重新啟動PreView
   
  }  
 };
 
 Camera.PictureCallback TakeRAW = new Camera.PictureCallback()
 {
  @Override
  public void onPictureTaken(byte[] data, Camera camera)
  {
   //raw: the callback for raw (uncompressed) image data, or null
   Log.d("onTakeRAW",""+data);
  }  
 };
 
 Camera.PictureCallback PostView = new Camera.PictureCallback()
 {
  @Override
  public void onPictureTaken(byte[] data, Camera camera)
  {
   //postview: callback with postview image data, may be null
   Log.d("onPostView",""+data);
  }  
 };
 
 @Override
 public void onShutter()
 {
  //Called as near as possible to the moment when a photo is captured from the sensor. 
  //This is a good opportunity to play a shutter sound or give other feedback of camera operation. 
  //This may be some time after the photo was triggered, but some time before the actual data is available.
     Log.d("onShutter()","onShutter()");
 }

 @Override
 public void onClick(View v)
 {  
  switch(v.getId())
  {
    case R.id.captureButton:
    mCamera.takePicture(this, TakeRAW, PostView ,TakeJpeg);  
    break;
  }
 }
 
 @Override
 public void surfaceCreated(SurfaceHolder holder)
 {  
  mCamera = Camera.open();
  
  try {            
            mParameters = mCamera.getParameters();
            mParameters.setPictureFormat(PixelFormat.JPEG);
            //mParameters.setPreviewSize(320, 480);
            //設定螢幕Preview大小 , 對模擬器拍出的圖片沒影響
            //mParameters.setPictureSize(320, 480);
            //設定圖片解析度大小
            mCamera.setParameters(mParameters);
            mCamera.setPreviewDisplay(holder);

        } catch (IOException exception)
        {
            mCamera.release();
            mCamera = null;            
        }
        mCamera.startPreview();
        
 }

 @Override
 public void surfaceChanged(SurfaceHolder holder, int format, int width,
   int height)
 {
  // TODO Auto-generated method stub
 } 

 @Override
 public void surfaceDestroyed(SurfaceHolder holder)
 {  
  mCamera.stopPreview();
        mCamera.release();
        mCamera = null;
 }
}


關於takePicture的函式說明 , 有三個PictureCallback的物件當作參數 , 然後針對這三個物件實作程式內容 , 不知道是否在同一個PictureCallback就能統一處理


以下是畫面截圖





然後不管你怎麼拍照 , 結果永遠是下面那張圖....







2010年11月16日 星期二

[Android] 關於螢幕方向翻轉 screenOrientation



通常在螢幕方向改變的時候 , 我們的Layout佈局應該也要跟著改變 , 才能讓使用者有良好的使用體驗和環境
所以通常我們會built兩個不同的Layout檔案因應螢幕方向的改變 , 而這篇文章要提的就是當使用者更改螢幕方向的時候時(橫轉直 , 直轉橫) , 該怎麼載入適當的Layout佈局?




Android 很貼心的幫開發者設計一個方法 , 在原本的res/資料夾下再建立兩個資料夾 , 名稱分別是layout-land , layout-port , 這原理和drawable-hdpi , drawable-ldpi....是一樣的 , 而layout-land裡面放置橫螢幕的Layout檔 , layout-port裡面放置直螢幕的Layout檔 , 當我們進入模擬器的時候 , 他就會根據螢幕的方向自動載入合適的Layout檔案 , 另外在模擬器中翻轉螢幕的方法是按下ctrl + F12 , 另外這邊有一個方法可以設定螢幕的方向






關於android:screenOrientation的屬性Android開發網站這邊有提到
但如果我們想根據螢幕的方向不同 , 而在onCreate的時候執行不同的程式碼該怎麼判斷呢? 這時候我們需要知道螢幕到底是直的還是橫的 , 這邊的一個小技巧是使用 this.getResources().getDisplayMetrics(); 來取得DisplayMetrics物件 , 比較螢幕長和寬來判斷螢幕方向 , 避免混淆 , 分別先把兩個layout資料夾移除 , 在原來的res/layout資料夾下 , 建立 landscape_main , portrait_main檔案 , 而程式碼如下

import android.app.Activity;
import android.os.Bundle;
import android.util.DisplayMetrics;

public class ScreenOrientationTest extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        
        dm = getResources().getDisplayMetrics();
        //取得螢幕顯示的資料
        ScreenWidth = dm.widthPixels;  
        ScreenHeight = dm.heightPixels; 
        //螢幕寬和高的Pixels
        
        if(ScreenHeight > ScreenWidth) setContentView(R.layout.portrait_main);
       
        else setContentView(R.layout.landscape_main);
    }
    
    DisplayMetrics dm;   
    int ScreenWidth , ScreenHeight;
}

這時候就可以根據螢幕方向的情況來執行不同的程式碼了




另外 , 根據Activity的life cycle的資料 , 當螢幕的方向改變和開關實體鍵盤時 , 都會重新執行onCreate()的部分 , 所以當onCreate()裡的程式碼需要initial許多物件的時候 , 翻轉螢幕的動作就會變得相當的耗費資源和拖累執行速度。

為了不讓每次的螢幕翻轉都重新跑一次onCreate() , 我們必須在AndroidManifest.xml裡activity屬性中做宣告 , 完整檔案如下


    
          
            
                
                
            
        
    
 

這麼一來當螢幕的方向改變或是開關實體鍵盤的時候 , activity就不會再執行onCreate()了 , 取而代之的是執行 public void onConfigurationChanged(Configuration newConfig) , 所以我們必須覆寫裡面的內容


import android.app.Activity;
import android.content.res.Configuration;
import android.os.Bundle;
import android.util.DisplayMetrics;

public class ScreenOrientationTest extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        
        dm = getResources().getDisplayMetrics();
        //取得螢幕顯示的資料
        ScreenWidth = dm.widthPixels;  
        ScreenHeight = dm.heightPixels; 
        //螢幕寬和高的Pixels
        
        if(ScreenHeight > ScreenWidth) setContentView(R.layout.portrait_main);
       
        else setContentView(R.layout.landscape_main);
    }
    
    DisplayMetrics dm;   
    int ScreenWidth , ScreenHeight;
    int oldOrientation = -1;
    //Configuration.ORIENTATION_LANDSCAPE Constant Value: 2
    //Configuration.ORIENTATION_PORTRAIT Constant Value: 1 
    
    @Override
    public void onConfigurationChanged(Configuration newConfig)
    {
         
     super.onConfigurationChanged(newConfig);
     
     //橫轉直會多跑一次 , 所以加上判斷過濾
     
     if(oldOrientation != newConfig.orientation)
     {
      if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE)
      {
       setContentView(R.layout.landscape_main);
      }             
         else
         {
          setContentView(R.layout.portrait_main);
         }
      oldOrientation = newConfig.orientation;
     }  
    }
}


接著就可以在判斷式內加上自己要的程式碼了!

[Android] 如何達到物件Overlay的效果並使用TouchMode拖移半透明的Button



如何讓兩個View達到Overlay的效果呢? 這篇要做的是讓一個Button overlay VideoView , 並且可以使用滑鼠移動Button(實機上是使用手指觸控移動) , 至於VideoView我們根據之前文章提到的方式來建構 , 

[Android] 如何使用VideoView播放影片 , 接下來我們要使用FrameLayout來達成物件Overlay的效果 , Layout如下












Layout檔案裡面使用了FrameLayout來使裡面的物件達到Overlay的效果 , 所以放了ViedoView和Button在裡面(擺放的順序是有關係的) , 但是只有如此的話會發現當我們移動Button走出一定範圍之後 , Button會消失 , 所以又放了一個LinearLayout , 再把Button擺了進去 , 想當然的LinearLayout必須是透明的 , 不然根本看不到VideoView的影像 , 所以在LinearLayout的屬性宣告上加了這行android:background="@+drawable/transparent_background , 所以我們必須事先宣告@+drawable/transparent_background , 他擺放的位置是在res/values/colors.xml 內容如下


    #e0000000
    
    #00000000
    


至於要移動Button , 要先實作OnTouchListener , 而且為了不要讓Button超出螢幕 , 我們還要取得顯示螢幕的長和寬的資料 , 程式碼如下

import android.app.Activity;
import android.net.Uri;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.MediaController;

public class CustomVideoViewTest extends Activity implements OnTouchListener
{
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
     super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        //隱藏 notification bar
        
        dm = getResources().getDisplayMetrics();
        //取得螢幕顯示的資料
        ScreenWidth = dm.widthPixels;  
        ScreenHeight = dm.heightPixels; 
        //螢幕寬和高的Pixels當作界線
        
        MoveButton = (Button) findViewById(R.id.Button);
        MoveButton.setOnTouchListener(this);
        
        CVView = (CustomVideoView)findViewById(R.id.VideoView);        
        MediaController controller = new MediaController(this); 
        CVView.setMediaController(controller); 
        //CVView.setVideoPath("android.resource://com.android/"+R.raw.video);
        //播放Local端的影音資源 , SD Card or res/raw 裡的影音檔
        CVView.setVideoURI(Uri.parse("http://www.dubblogs.cc:8751/Android/Test/Media/mp4/test.mp4"));
        //播放遠端的影音資源 , 影音串流
        CVView.requestFocus();
        CVView.start();
    }
    
    CustomVideoView CVView;
    DisplayMetrics dm;
    int lastX , lastY;
    int ScreenWidth , ScreenHeight;
    Button MoveButton;
    
    @Override
 public boolean onTouch(View view, MotionEvent event)
 {
  // TODO Auto-generated method stub
  
     int events = event.getAction();
     
     switch(events)
        {
         case MotionEvent.ACTION_DOWN:           
         
         lastX = (int)event.getRawX();
         lastY = (int)event.getRawY();
         
         break;
        
         //layout(Left,Top,Right,Bottom)
         //     Left position, relative to parent 
         //      Top position, relative to parent 
         //    Right position, relative to parent 
         //  Bottom position, relative to parent  
         
         case MotionEvent.ACTION_MOVE:
         int Movex=(int)event.getRawX()-lastX;
         int Movey=(int)event.getRawY()-lastY;           
         
         int Left=view.getLeft()+ Movex; 
         int Bottom=view.getBottom()+ Movey;
         int Right=view.getRight()+ Movex;
         int Top=view.getTop()+ Movey;
         
         if(Left<0)
         {
          Left=0;    
          Right=Left+view.getWidth();
         }
         
         if(Top<0)
         {
          Top=0;
          Bottom=Top+view.getHeight();
         }
         
         if(Right > ScreenWidth)
         {
         Right = ScreenWidth;
          Left=Right-view.getWidth();
         }
        
         if(Bottom > ScreenHeight-50)
         {
         Bottom = ScreenHeight-50;
            Top = Bottom - view.getHeight();
         }
         view.layout(Left, Top, Right, Bottom);
         lastX =(int)event.getRawX();
         lastY =(int)event.getRawY();
   view.postInvalidate();
                
         break; 
 
        }
     
     return false;
 }    
}

執行的效果如下




Button還可以加上背景圖 , 更棒的是Button的形狀會依照圖片做變更 , 只要稍作修改就可以






如果想對圖片的透明度的改變 , 只要加入下面那段程式碼


MoveButton = (Button) findViewById(R.id.Button);        
MoveButton.getBackground().setAlpha(100);

先讓他回傳一個Drawable物件 , 接著對他的透明度進行改變 setAlpha 的參數越小透明化的效果越明顯



public Drawable getBackground ()

Since: API Level 1
Gets the background drawable
Returns
  • The drawable used as the background for this view, if any.

public abstract void setAlpha (int alpha)

Since: API Level 1
Specify an alpha value for the drawable. 0 means fully transparent, and 255 means fully opaque.

2010年11月15日 星期一

[Android] 如何使用VideoView播放影片



在之前的文章[Android Tips] 如何在Layout檔案中使用自訂的View , 成功的使用了自訂的VideoView了 , 現在該讓他播放個影片來看看 , 根據Android的文件 , 能播放的影音檔案為3gp和mp4 , 程式碼如下



import android.app.Activity;
import android.net.Uri;
import android.os.Bundle;
import android.widget.MediaController;

public class CustomVideoViewTest extends Activity
{
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        CVView = (CustomVideoView)findViewById(R.id.VideoView);
        MediaController controller = new MediaController(this); 
        CVView.setMediaController(controller); 
        //CVView.setVideoPath("android.resource://com.android/"+R.raw.video);
        //播放Local端的影音資源 , SD Card or res/raw 裡的影音檔
        CVView.setVideoURI(Uri.parse("http://www.dubblogs.cc:8751/Android/Test/Media/mp4/test.mp4"));
        //播放遠端的影音資源 , 影音串流
        CVView.requestFocus();
        CVView.start();
    }    
    CustomVideoView CVView;
}

另外應用程式要連接網路需要加上
android:name="android.permission.INTERNET : Allows applications to open network sockets

    


執行結果如下






另外關於VideoView大小的問題





  

可以看到VideoView的寬和高屬性都已經設為"fill_parent"了 , 但事實上執行的結果並不會填滿整個parent layout , 如果想更改大小的話目前比較簡單的方法是寬設為"xxdip"來解決 , 不過只有更改寬的dip才會起作用 , 高的部分怎麼改都一樣 , 應該是有更好的方法來修改VideoView的寬和高。在網路上看到一篇文章有解決這個問題 , 照他的方法做程式碼如下


import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.VideoView;

public class CustomVideoView extends VideoView
{

 public CustomVideoView(Context context)
 {
  super(context);
  // TODO Auto-generated constructor stub
  Log.d("Go to", "Context context");
 }

 public CustomVideoView(Context context, AttributeSet attrs)
 {
  super(context, attrs);
  // TODO Auto-generated constructor stub
  mVideoWidth = 0;
     mVideoHeight = 0;
  Log.d("Go to", "Context context, AttributeSet attrs");
 } 
 //可以發現進入的是CustomVideoView(Context context, AttributeSet attrs)這個建構子
    //所以留著覆寫他 , 否則程式會強制關閉
 
 public void onMeasure (int widthMeasureSpec, int heightMeasureSpec)
 {
  int width = getDefaultSize(mVideoWidth, widthMeasureSpec);
        int height = getDefaultSize(mVideoHeight, heightMeasureSpec);
        setMeasuredDimension(width,height);
 }
 
 private int mVideoWidth;
    private int mVideoHeight;
}


接著使用之前文章的 [Android Tips] 如何隱藏Title Bar和Notification Bar , 執行成功了結果如下












2010年11月14日 星期日

[NBA] Stephen Curry



玩了2K11快一個月了發現有個球員很好用 , 速度快又準防守又好 , 就是他 Stephen Curry , 上一季就有聽過他的大名了 , 去年三分球大賽有備邀請參賽 , 年度新秀票選第二名 , 2009球季新秀第一隊From Davidson 大學 , 第一輪第七順位被勇士選走。 


之前只是覺得他投籃的姿勢很優美 , 然後覺得他的體型在NBA來說真的很像國中生....雖然長得也很像 , 名符其實的哇哇臉 , 玩了2K11之後越來越喜歡他了 , 查了一下才發現他是Dell Curry的兒子 , Dell Curry 也是一個射手 , 記得國中玩NBA LIVE 他還在暴龍隊 , 兒子也遺傳到老爸 , 是個能切能投的後衛 , 而且Stephen Curry還有一個弟弟 Seth Curry剛念大學也打校隊。

Stephen Curry在大學的球風比較像SG , 所以勇士隊選到他的時候 , 很多人懷疑他的位置會不會和隊上的Monta Ellis 重疊 , 後來老尼決定讓他們一起上場 , 不過剛進NBA的前一個月 Curry有一段低潮 , 是之後越打越好才有機會角逐新人王的 , 今年新的球季剛開始就受傷了 , 現在復出希望他有好的表現^^

現在來看看他去年球季的MIX






另外這個漂亮的假動作腳步




小時候的Stephen Curry和老爸Dell Curry在球場上





Stephen Curry - The Future (Video Mix)


Google Analytics