2014年12月24日 星期三

Android Camera AutoFocus list delete picture 拍照 自動對焦 列出照片 刪除照片

拍照功能參考在 Android 裡使用 Camera 照相
並加入自動對焦與列出照片並可刪除該照片檔案

1.AndroidManifest.xml加入權限


    

    
    

    
        
            
                

                
            
        
        
        
        
        
    


2.建立java檔與layout檔

TakeAPhotoActivity.java 拍照
package tw.android;

import android.app.Activity;
import android.content.Intent;
import android.hardware.Camera;
import android.hardware.Camera.AutoFocusCallback;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;

public class TakeAPhotoActivity extends Activity {

 private static final String TAG = "TakeAPhotoActivity";
 private SurfaceView sv;
 // private ImageView iv;
 private Button takePhotoBtn, hidePhotoBtn, autoFocus,picList;
 // 相機 callback
 private CameraCallback cc;
 // 快門 callback
 private ShCallback sc;
 // 處理 raw data callback
 private RawCallback rc;
 // 處理 jpg callback
 private JpgCallback jc;

 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.take_a_photo_activity);

  this.sv = (SurfaceView) this.findViewById(R.id.sv);
  this.takePhotoBtn = (Button) this.findViewById(R.id.takePhotoBtn);
  this.hidePhotoBtn = (Button) this.findViewById(R.id.hidePhotoBtn);
  this.autoFocus = (Button) this.findViewById(R.id.autoFocus);
  this.picList= (Button) this.findViewById(R.id.picList);
  
  this.cc = new CameraCallback();
  this.sc = new ShCallback();
  this.rc = new RawCallback();
  this.jc = new JpgCallback(this);

  Log.d(TAG, "設定預覽視窗...");
  SurfaceHolder sh = this.sv.getHolder();
  sh.addCallback(this.cc);
  sh.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

  Log.d(TAG, "設定拍照頁面...");
  this.hidePhotoBtn.setVisibility(View.GONE);

  // 按鈕外按自動對焦
  autoFocus.setOnClickListener(new Button.OnClickListener() {

   @Override
   public void onClick(View v) {
    cc.getCarema().autoFocus(new AutoFocusCallback() {
     @Override
     public void onAutoFocus(boolean success, Camera camera) {

     }
    });
   }
  });
  //前往照片列表
  picList.setOnClickListener(new Button.OnClickListener() {

   @Override
   public void onClick(View v) {
    goToPicList();
   }
  });
  
 }

 public void takePhoto(View v) {
  Log.d(TAG, "拍照...");
  // 需要三個 callback:快門、處理 raw data、處理 jpg
  // 拍照時自動對焦
  this.cc.getCarema().autoFocus(new AutoFocusCallback() {
   @Override
   public void onAutoFocus(boolean success, Camera camera) {
    if (success) {
     camera.takePicture(sc, rc, jc);
    }
   }
  });
 }

 public void hidePhoto(View v) {
  Log.d(TAG, "設定拍照頁面...");
  this.takePhotoBtn.setVisibility(View.VISIBLE);// 顯示拍照按鈕
  this.hidePhotoBtn.setVisibility(View.GONE); // 隱藏重拍按鈕

  Log.d(TAG, "回到拍照功能,需重新啟動預覽...");
  this.cc.getCarema().startPreview();
 }

 public void showPhoto(String picPath) {
  Log.d(TAG, "取得照片路徑:" + picPath);
  Log.d(TAG, "設定照片頁面...");
  this.takePhotoBtn.setVisibility(View.GONE);
  this.hidePhotoBtn.setVisibility(View.VISIBLE);

 }

 //前往照片列表
  public void goToPicList(){
   Intent intent=new Intent(this,PhotoList.class);
   startActivity(intent);
  }

}

CameraCallback.java
package tw.android;

import java.io.IOException;

import android.hardware.Camera;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;


public class CameraCallback implements Callback {

    private static final String TAG = "CameraCallback";
    private Camera carema;

    public Camera getCarema() {
        return this.carema;
    }

    public void surfaceCreated(SurfaceHolder holder) {
        Log.d(TAG, "啟動相機...");
        this.carema = Camera.open();
        try {
            Log.d(TAG, "設定預覽視窗");
            this.carema.setPreviewDisplay(holder);
        }
        catch (IOException e) {
            Log.e(TAG, e.getMessage(), e);
        }
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {
        Log.d(TAG, "開始預覽...");
        this.carema.setDisplayOrientation(90); //相機旋轉90度
        this.carema.startPreview();
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        Log.d(TAG, "停止預覽...");
        this.carema.stopPreview();
        Log.d(TAG, "釋放相機資源...");
        this.carema.release();
        this.carema = null;
    }

 
}

JpgCallback.java
package tw.android;

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

import android.annotation.SuppressLint;
import android.content.SharedPreferences;
import android.hardware.Camera;
import android.hardware.Camera.PictureCallback;
import android.os.Environment;
import android.util.Log;

public class JpgCallback implements PictureCallback {

    private static final String TAG = "JpgCallback";
    private String picPath;
    private TakeAPhotoActivity act;
    int number;
    public JpgCallback(TakeAPhotoActivity act) {
        super();
        this.act = act;
    }

    @Override
    public void onPictureTaken(byte[] data, Camera camera) {
        Log.d(TAG, "處理 JPG 資料,輸出 jpg 檔...");
        /*-- 2015-09-11 相機旋轉90度後,照片需要跟著旋轉90度 --*/
        Bitmap srcBmp, dstBmp;
        srcBmp= BitmapFactory.decodeByteArray(data, 0, data.length);
        Matrix matrix=new Matrix();
        matrix.reset();
        matrix.postRotate(90f);
        dstBmp= Bitmap.createBitmap(srcBmp, 0, 0, srcBmp.getWidth(), srcBmp.getHeight(), matrix, true);
        /*-- 2015-09-11 --*/
        FileOutputStream os = null;
        try {
            File pic = this.createPicFile();
            os = new FileOutputStream(pic);
            dstBmp.compress(Bitmap.CompressFormat.JPEG, 100, os); //2015-09-11 Bitmap 轉回 byte[]
            os.write(data);
        }
        catch (IOException e) {
            Log.e(TAG, e.getMessage(), e);
        }
        finally {
            if (os != null) {
                try {
                    os.close();
                }
                catch (IOException e) {
                }
            }
        }
        Log.d(TAG, "輸出 JPG 完成");
        // 顯示照片
        this.act.showPhoto(this.picPath);
    }

    @SuppressLint("DefaultLocale")
 private File createPicFile() {
        File sdDir = Environment.getExternalStorageDirectory();
        Log.d(TAG, "sdDir" + sdDir);
        File picDir = new File(sdDir, "takePic"); //建立放置照片資料夾
        if (!picDir.exists()) {
            picDir.mkdir();
        }
        long ctm =System.currentTimeMillis(); //照片名
        
        /* SharedPreferences */
        try {
   SharedPreferences preferencesGet = this.act
     .getSharedPreferences("takePic",
       android.content.Context.MODE_PRIVATE);
   number=preferencesGet.getInt("number", 0); //照片數量
   
  } catch (Exception e) {

  }
        
  SharedPreferences preferencesSave = this.act
    .getSharedPreferences("takePic",
      android.content.Context.MODE_PRIVATE);
  SharedPreferences.Editor editor = preferencesSave.edit();
  
  Log.d(TAG,number+"");
  number++; //照片數量+1
  editor.putInt("number", number);
  editor.putLong(Integer.toString(number), ctm); //儲存照片名
  editor.commit();
  
  
        String fileName = String.format("%d.jpg", ctm);
        File pic = new File(picDir, fileName);
        this.picPath = pic.getAbsolutePath();
        Log.d(TAG, "照片路徑:" + this.picPath);
        return pic;
    }
}
RawCallback.java
package tw.android;

import android.hardware.Camera;
import android.hardware.Camera.PictureCallback;
import android.util.Log;

public class RawCallback implements PictureCallback {

    private static final String TAG = "RawCallback";

    @Override
    public void onPictureTaken(byte[] data, Camera camera) {
        Log.d(TAG, "處理 Raw data...");
    }

}

ShCallback.java
package tw.android;

import android.hardware.Camera.ShutterCallback;
import android.util.Log;

public class ShCallback implements ShutterCallback {

    private static final String TAG = "ShCallback";

    @Override
    public void onShutter() {
        Log.d(TAG, "啟動快門...");
    }
}

PhotoList.java 列出照片
package tw.android;

import java.io.File;

import android.app.Activity;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TableRow.LayoutParams;

public class PhotoList extends Activity {

 private Button[] detelPic;
 private String[] dataName;

private int number;

 private int id;

 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.photo_list);
  
  setTable();
 }


 // 排版
 public void setTable() {

  try {
   SharedPreferences preferencesGet = getApplicationContext()
     .getSharedPreferences("takePic",
       android.content.Context.MODE_PRIVATE);
   number=preferencesGet.getInt("number", 0);//取出照片數量
   dataName = new String[number];
   Log.i("number", number+"");
   for(int i = 0; i < number; i++){ 
    dataName[i]=String.valueOf(preferencesGet.getLong(Integer.toString(i+1), 0)); //放入照片名稱
    Log.i("dataName[i]", dataName[i]+"");
   }

  } catch (Exception e) {

  }
  
  
  TableLayout t1 = (TableLayout) findViewById(R.id.tableSet);
  t1.removeAllViews();
  TableRow.LayoutParams tP = new TableRow.LayoutParams(
    LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 1f);

  tP.setMargins(0, 0, 0, 20);

  detelPic = new Button[number];
  for (int i = 0; i < number; i++) { // 列
   TableRow row = new TableRow(this);
   ImageView iv = new ImageView(this);
   String picPath = "/storage/emulated/0/takePic/" + dataName[i]
     + ".jpg";
   Uri uri = Uri.fromFile(new File(picPath));
   iv.setLayoutParams(tP);
   iv.setImageURI(uri);
   row.addView(iv, 0);

   // 刪除button
   detelPic[i] = new Button(this);
   detelPic[i].setText("刪除");
   detelPic[i].setId(i);
   detelPic[i].setOnClickListener(dp); // 動作
   row.addView(detelPic[i], 1);

   t1.addView(row);
  }

 }

 private OnClickListener dp = new OnClickListener() {
  public void onClick(View v) {

   id = v.getId();
   // 刪除照片
   File file = new File("/storage/emulated/0/takePic/" + dataName[id]
     + ".jpg");
   file.delete();

   
   setTable();//重整
  }
 };

}

take_a_photo_activity.xml //拍照layout


    
  
    
 
    

    

    


photo_list.xml //照相列表 layout


    

        
        
    


檔案下載:
https://github.com/terryyamg/AndroidCamera
參考來源:
1.http://cw1057.blogspot.tw/2011/12/android-camera_09.html
2.http://stackoverflow.com/questions/8058122/where-to-put-autofocus-in-the-class-android-camera
3.http://stackoverflow.com/questions/5486529/delete-file-from-internal-storage
4.http://stackoverflow.com/questions/10660598/android-camera-preview-orientation-in-portrait-mode 
(2015-09-11 update)
5. http://rincliu.com/blog/2013/11/18/camera/
6.http://bingtian.iteye.com/blog/642128

2014年12月8日 星期一

Android NFC tag writer 寫入 訊息

NFC感應TAG,將訊息寫入TAG中
1.AndroidManifest.xml
加入權限
與最低版本
android:minsdkversion="17" android:targetsdkversion="17"
2.activity_main.xml



    

        

        
    

    

        

        
    

    


3.MainActivity.java
import java.io.IOException;
import nl.paulus.nfctagwriter.R;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.Ndef;
import android.nfc.tech.NdefFormatable;
import android.os.Bundle;
import android.os.Parcelable;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

/**
 * Activity to write NFC tags with own mimetype and ID Based on the excellent
 * tutorial by Jesse Chen
 * http://www.jessechen.net/blog/how-to-nfc-on-the-android-platform/
 */
public class MainActivity extends Activity {

 boolean mWriteMode = false;
 private NfcAdapter mNfcAdapter;
 private PendingIntent mNfcPendingIntent;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  ((Button) findViewById(R.id.button))
    .setOnClickListener(new OnClickListener() {

     @Override
     public void onClick(View v) {
      mNfcAdapter = NfcAdapter
        .getDefaultAdapter(MainActivity.this);
      mNfcPendingIntent = PendingIntent.getActivity(
        MainActivity.this,
        0,
        new Intent(MainActivity.this,
          MainActivity.class)
          .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),
        0);
      //開啟寫入
      enableTagWriteMode();

      new AlertDialog.Builder(MainActivity.this)
        .setTitle("感應tag寫入")
        .setOnCancelListener(
          new DialogInterface.OnCancelListener() {
           @Override
           public void onCancel(
             DialogInterface dialog) {
            //關閉寫入
            disableTagWriteMode();
           }

          }).create().show();
     }
    });
 }

 private void enableTagWriteMode() {
  mWriteMode = true;
  IntentFilter tagDetected = new IntentFilter(
    NfcAdapter.ACTION_TAG_DISCOVERED);
  IntentFilter[] mWriteTagFilters = new IntentFilter[] { tagDetected };
  mNfcAdapter.enableForegroundDispatch(this, mNfcPendingIntent,
    mWriteTagFilters, null);
 }

 private void disableTagWriteMode() {
  mWriteMode = false;
  mNfcAdapter.disableForegroundDispatch(this);
 }

 @SuppressLint("NewApi")
 @Override
 protected void onNewIntent(Intent intent) {
  // Tag writing mode
  if (mWriteMode
    && NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) {
   Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
   NdefRecord record = NdefRecord.createMime( //需要Android 4.1(API Level 16)
     ((TextView) findViewById(R.id.mime)).getText().toString(),
     ((TextView) findViewById(R.id.value)).getText().toString()
       .getBytes());
   NdefMessage message = new NdefMessage(new NdefRecord[] { record });
   if (writeTag(message, detectedTag)) {
    Toast.makeText(this, "成功寫入!",
      Toast.LENGTH_LONG).show();
   }
  }
  
  //讀取寫入訊息 看是否有寫入
  
        try {
         Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
      NdefMessage[] msgs = new NdefMessage[rawMsgs.length];
      for (int i = 0; i < rawMsgs.length; i++) {
                msgs[i] = (NdefMessage) rawMsgs[i];
            }

            NdefMessage msg = msgs[0];
         String msgstring = new String(msg.getRecords()[0].getPayload(), "UTF-8");
         Toast.makeText(getApplicationContext(), msgstring, Toast.LENGTH_LONG).show();
   
        } catch (Exception e) {
            e.printStackTrace();
        }
 }

 /* 檢查訊息 */
 public boolean writeTag(NdefMessage message, Tag tag) {
  int size = message.toByteArray().length;
  try {
   Ndef ndef = Ndef.get(tag);
   if (ndef != null) {
    ndef.connect();
    if (!ndef.isWritable()) {
     Toast.makeText(getApplicationContext(),
       "Error: tag not writable", Toast.LENGTH_SHORT)
       .show();
     return false;
    }
    if (ndef.getMaxSize() < size) {
     Toast.makeText(getApplicationContext(),
       "Error: tag too small", Toast.LENGTH_SHORT).show();
     return false;
    }
    ndef.writeNdefMessage(message);
    return true;
   } else {
    NdefFormatable format = NdefFormatable.get(tag);
    if (format != null) {
     try {
      format.connect();
      format.format(message);
      return true;
     } catch (IOException e) {
      return false;
     }
    } else {
     return false;
    }
   }
  } catch (Exception e) {
   return false;
  }
 }
} 
參考來源:
https://github.com/balloob/Android-NFC-Tag-Writer 
http://stackoverflow.com/questions/7917567/strange-character-on-android-ndef-record-payload 

2014年11月25日 星期二

Android NFC tag 近距離無線通訊

需有NFC功能手機與感應卡(悠遊卡、學生證之類的)
首先在權限上將minSdkVersion修改為10並加入

1.AndroidManifest.xml



2.layout的activity_main.xml需有一個TextView顯示

3.MainActivity.java
public class Inventory extends Activity {
 
 /* NFC */
 // list of NFC technologies detected:
 private final String[][] techList = new String[][] { new String[] {
   NfcA.class.getName(), NfcB.class.getName(), NfcF.class.getName(),
   NfcV.class.getName(), IsoDep.class.getName(),
   MifareClassic.class.getName(), MifareUltralight.class.getName(),
   Ndef.class.getName() } };

 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.inventory);

 }

 /* NFC */
 @Override
 protected void onResume() {
  super.onResume();
  // creating pending intent:
  PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
    new Intent(this, getClass())
      .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
  // creating intent receiver for NFC events:
  IntentFilter filter = new IntentFilter();
  filter.addAction(NfcAdapter.ACTION_TAG_DISCOVERED);
  filter.addAction(NfcAdapter.ACTION_NDEF_DISCOVERED);
  filter.addAction(NfcAdapter.ACTION_TECH_DISCOVERED);
  // enabling foreground dispatch for getting intent from NFC event:
  NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
  nfcAdapter.enableForegroundDispatch(this, pendingIntent,
    new IntentFilter[] { filter }, this.techList);
 }

 @Override
 protected void onPause() {
  super.onPause();
  // disabling foreground dispatch:
  NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
  nfcAdapter.disableForegroundDispatch(this);
 }
 //將tag到的ID顯示出來
 @Override
 protected void onNewIntent(Intent intent) {
  if (intent.getAction().equals(NfcAdapter.ACTION_TAG_DISCOVERED)) {
   ((TextView) findViewById(R.id.tag)).setText("Tag ID(16-14):"
     + ByteArrayToHexString(intent
       .getByteArrayExtra(NfcAdapter.EXTRA_ID))
     + "\n Tag ID (16-8):"
     + getHex(intent.getByteArrayExtra(NfcAdapter.EXTRA_ID))
     + "\n Tag ID (dec):"
     + getDec(intent.getByteArrayExtra(NfcAdapter.EXTRA_ID))
     + "\n ID (reversed):"
     + getReversed(intent.getByteArrayExtra(NfcAdapter.EXTRA_ID)));
  }
 }
 //16進位14碼
 private String ByteArrayToHexString(byte[] inarray) {
  int i, j, in;
  String[] hex = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A",
    "B", "C", "D", "E", "F" };
  String out = "";
  for (j = 0; j < inarray.length; ++j) {
   in = (int) inarray[j] & 0xff;
   i = (in >> 4) & 0x0f;
   out += hex[i];
   i = in & 0x0f;
   out += hex[i];
  }
  return out;
 }
 //16進位8碼
 private String getHex(byte[] bytes) {
  StringBuilder sb = new StringBuilder();
  for (int i = bytes.length - 1; i >= 0; --i) {
   int b = bytes[i] & 0xff;
   if (b < 0x10)
    sb.append('0');
   sb.append(Integer.toHexString(b));
   if (i > 0) {
    sb.append(" ");
   }
  }
  return sb.toString();
 }
 //12進位10碼
 private long getDec(byte[] bytes) {
  long result = 0;
  long factor = 1;
  for (int i = 0; i < bytes.length; ++i) {
   long value = bytes[i] & 0xffl;
   result += value * factor;
   factor *= 256l;
  }
  return result;
 }
 //12進位10碼反轉
 private long getReversed(byte[] bytes) {
  long result = 0;
  long factor = 1;
  for (int i = bytes.length - 1; i >= 0; --i) {
   long value = bytes[i] & 0xffl;
   result += value * factor;
   factor *= 256l;
  }
  return result;
 }
} 
參考連結
https://gist.github.com/luixal/5768921
https://github.com/nadam/nfc-reader
 

2014年11月20日 星期四

Android SQLite db檔案匯入與基本指令 SQLite data copy to app and instruction

假設已經有一個SQLite檔,要將其匯入app
1.首先在專案res資料夾建立一個raw資料夾,將SQLite檔案放入此資料夾
2.接著建立一個DBManager.java檔案


public class DBManager {

 private final int BUFFER_SIZE = 400000;
 public static final String DB_NAME = "db2.db"; // 保存的資料庫檔案名
 public static final String PACKAGE_NAME = "tw.android";
 public static final String DB_PATH = "/data"
   + Environment.getDataDirectory().getAbsolutePath() + "/"
   + PACKAGE_NAME;// 在手機裡存放資料庫的位置(/data/data/tw.android/db2.db)

 private SQLiteDatabase database;
 private Context context;

 public DBManager(Context coNtext) {
  this.context = coNtext;
 }

 public SQLiteDatabase getDatabase() {
  return database;
 }

 public void setDatabase(SQLiteDatabase database) {
  this.database = database;
 }

 public void openDatabase() {
  this.database = this.openDatabase(DB_PATH + "/" + DB_NAME);
 }

 private SQLiteDatabase openDatabase(String dbfile) {

  try {
   if (!(new File(dbfile).exists())) {
    Log.i("have db2????????", "no");
    // 判斷資料庫檔案是否存在,若不存在則執行導入,否則直接打開資料庫
    InputStream is = this.context.getResources().openRawResource(
      R.raw.db2); // 欲導入的資料庫
    FileOutputStream fos = new FileOutputStream(dbfile);
    byte[] buffer = new byte[BUFFER_SIZE];
    int count = 0;
    while ((count = is.read(buffer)) > 0) {
     fos.write(buffer, 0, count);
    }
    fos.close();
    is.close();
   }
   SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(dbfile,
     null);
   return db;

  } catch (FileNotFoundException e) {
   Log.e("Database", "File not found");
   e.printStackTrace();
  } catch (IOException e) {
   Log.e("Database", "IO exception");
   e.printStackTrace();
  }
  return null;
 }

 public void closeDatabase() {
  this.database.close();

 }
}
3.主要檔案Main.java搭配基本SQLite指令
public class Main extends Activity {

 private SQLiteDatabase db;

 private EditText mEdtName, mEdtAddr, mEdtUpdate1, mEdtUpdate2, mEdtDelete,
   mEdtSQL;

 private TextView mTextList1, mTextList2;

 private Button mBtnAdd, mBtnUpdate, mBtnDelete, mBtnSQL;

 public DBManager dbHelper;

 /** Called when the activity is first created. */
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);

  setupViewComponent();

  dbHelper = new DBManager(this);
  dbHelper.openDatabase();
  db = dbHelper.getDatabase();
  queryList();
 }

 private void setupViewComponent() {
  mTextList1 = (TextView) findViewById(R.id.textList1);
  mTextList2 = (TextView) findViewById(R.id.textList2);

  mEdtName = (EditText) findViewById(R.id.edtName);
  mEdtAddr = (EditText) findViewById(R.id.edtAddr);
  mEdtUpdate1 = (EditText) findViewById(R.id.edtUpdate1);
  mEdtUpdate2 = (EditText) findViewById(R.id.edtUpdate2);
  mEdtDelete = (EditText) findViewById(R.id.edtDelete);
  mEdtSQL = (EditText) findViewById(R.id.edtSQL);

  mBtnAdd = (Button) findViewById(R.id.btnAdd);
  mBtnUpdate = (Button) findViewById(R.id.btnUpdate);
  mBtnDelete = (Button) findViewById(R.id.btnDelete);
  mBtnSQL = (Button) findViewById(R.id.btnSQL);

  mBtnAdd.setOnClickListener(onClickBtnAdd);
  mBtnUpdate.setOnClickListener(onClickBtnUpdate);
  mBtnDelete.setOnClickListener(onClickBtnDelete);
  mBtnSQL.setOnClickListener(onClickBtnSQL);

 }

 /* 新增 */
 private Button.OnClickListener onClickBtnAdd = new Button.OnClickListener() {
  @Override
  public void onClick(View v) {
   // TODO Auto-generated method stub

   /* 方法1 直接下指令 */
   if (mEdtName.getText().toString().isEmpty() == false) { // name有值
    db.execSQL("insert into user('name') values ('"
      + mEdtName.getText().toString() + "')");
   }
   if (mEdtAddr.getText().toString().isEmpty() == false) { // address有值
    db.execSQL("insert into location('address') values ('"
      + mEdtAddr.getText().toString() + "')");
   }

   /* 方法2 */
   // if(mEdtName.getText().toString().isEmpty() == false){ // name有值
   // ContentValues newRow = new ContentValues();
   // newRow.put("name", mEdtName.getText().toString());
   // db.insert("user", null, newRow);
   // }
   // if (mEdtAddr.getText().toString().isEmpty() == false) {
   // //address有值
   // ContentValues newRow = new ContentValues();
   // newRow.put("address", mEdtAddr.getText().toString());
   // db.insert("location", null, newRow);
   // }
   queryList();
  }
 };

 /* 修改 */
 private Button.OnClickListener onClickBtnUpdate = new Button.OnClickListener() {
  @Override
  public void onClick(View v) {
   // TODO Auto-generated method stub

   /* 方法1 直接下指令 */
   db.execSQL("UPDATE location SET address='"
     + mEdtUpdate2.getText().toString() + "' WHERE address='"
     + mEdtUpdate1.getText().toString() + "'");

   /* 方法2 */
   // ContentValues newRow3 = new ContentValues();
   // newRow3.put("address", mEdtUpdate2.getText().toString());
   // db.update("location", newRow3,
   // "address='"+mEdtUpdate1.getText().toString()+"'", null);

   queryList();
  }
 };

 /* 刪除 */
 private Button.OnClickListener onClickBtnDelete = new Button.OnClickListener() {
  @Override
  public void onClick(View v) {
   // TODO Auto-generated method stub

   /* 方法1 直接下指令 */
   db.execSQL("DELETE FROM location WHERE address='"
     + mEdtDelete.getText().toString() + "'");

   /* 方法2 */
   // db.delete("location",
   // "address='"+mEdtDelete.getText().toString()+"'", null);

   queryList();
  }
 };

 private Button.OnClickListener onClickBtnSQL = new Button.OnClickListener() {
  @Override
  public void onClick(View v) {
   // TODO Auto-generated method stub

   Cursor cursor = db.rawQuery(mEdtSQL.getText().toString(), null); // 搜尋用rawQuery
   cursor.moveToFirst();
   mTextList1.setText(cursor.getString(0) + cursor.getString(1));
   while (cursor.moveToNext())
    mTextList1.append("\n" + cursor.getString(0) + "    "
      + cursor.getString(1));
  }
 };

 /* 搜尋 */
 void queryList() {

  /* 方法1 直接下指令 */
  String queryUser = "SELECT * FROM user";
  Cursor cursor1 = db.rawQuery(queryUser, null); // 搜尋用rawQuery
  String queryLocation = "SELECT * FROM location";
  Cursor cursor2 = db.rawQuery(queryLocation, null); // 搜尋用rawQuery
  /* 方法2 */
  // Cursor cursor1 = db.query("user", new String[] { "Id", "name"},
  // null, null, null, null, null);
  // Cursor cursor2 = db.query("location", new String[] { "Id",
  // "address"},
  // null, null, null, null, null);

  cursor1.moveToFirst();
  mTextList1
    .setText(cursor1.getString(0) + "    " + cursor1.getString(1));
  while (cursor1.moveToNext())
   mTextList1.append("\n" + cursor1.getString(0) + "    "
     + cursor1.getString(1));

  cursor2.moveToFirst();
  mTextList2
    .setText(cursor2.getString(0) + "    " + cursor2.getString(1));
  while (cursor2.moveToNext())
   mTextList2.append("\n" + cursor2.getString(0) + "    "
     + cursor2.getString(1));
 }

 /* 離開程式 */
 public boolean onKeyDown(int keycode, KeyEvent event) {
  if (keycode == KeyEvent.KEYCODE_BACK) {
   // 關閉資料庫
   dbHelper.closeDatabase();
   finish();
   return true;
  }
  return super.onKeyDown(keycode, event);
 }

 @Override
 public void onDestroy() {
  super.onDestroy();
  // Kill myself
  android.os.Process.killProcess(android.os.Process.myPid());
 }

}
4.main.xml配置


    

        

        
    

    

        

        
    

    

    

        

        

        

        
    

   

    

        

        

        
    

    

    

        

        
    

    

    

        

            

                

                
           
        
    


5.完成如圖


SQLite db2.db 檔案下載
參考來源:
http://fecbob.pixnet.net/blog/post/43610863
http://hscc.cs.nctu.edu.tw/~lincyu/Android/Chapter11.pdf

2014年11月3日 星期一

Android RatingBar Customize 自製 評分

Android內建評分星星系統,可自行製作圖案代替星星
1.activity_main.xml
android:numStars="5" ----- 總數量
android:rating="2.5" ----- 初始值
android:stepSize="0.5" " -----評分單位
style="?android:attr/ratingBarStyleIndicator" ----- 指標(無法拉動)
style="?android:attr/ratingBarStyleSmall" ----- 小指標
style="@style/foodRatingBar" ----- 自製圖案
 




                

                

                

                

                

                

               
            
2.在res -> value -> styles.xml檔案內加上

3.在res -> drawable-hdpi 建立三個檔案
food_ratingbar_full.xml
food_ratingbar_full_empty.xml
food_ratingbar_full_filled.xml
並將圖片

下載至drawable-hdpi此資料夾內
food_ratingbar_full.xml

    
    
    


food_ratingbar_full_empty.xml


    
    
    
    


food_ratingbar_full_filled.xml


 
    
 
    
 
    
 
    
 

4.MainActivity.java
public class MainActivity extends FragmentActivity {

 private RatingBar ratingBar4;
 private TextView txtRatingValue;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  addListenerOnRatingBar();
 }

 //滑動直接顯示分數
 public void addListenerOnRatingBar() {
   
  ratingBar4 = (RatingBar) findViewById(R.id.ratingBar4);
  txtRatingValue = (TextView) findViewById(R.id.txtRatingValue);
  
  //if rating value is changed,
  //display the current rating value in the result (textview) automatically
  ratingBar4.setOnRatingBarChangeListener(new OnRatingBarChangeListener() {
   public void onRatingChanged(RatingBar ratingBar, float rating,
    boolean fromUser) {
  
    txtRatingValue.setText(String.valueOf(rating));
  
   }
  });
   }
}


參考來源:
http://www.mkyong.com/android/android-rating-bar-example/
http://kozyr.zydako.net/2010/05/23/pretty-ratingbar/
https://github.com/kozyr/foody-memories

2014年10月1日 星期三

Android automatic send e-mail 自動發送e-mail

也就是不需經過使用者即可發送email,是寄件者(開發者)寄送給其他收件者的方法
1.安裝jar檔
mail.jar
activation.jar
additionnal.jar
 將這三個檔案下載到專案的libs資料夾上,接著在eclips重新整理專案,在這三個檔案按右鍵->Bulid Path-> Add to Build Path 完成
 2.在src新增建立資料夾com,在資料夾com裡面再建立一個資料夾provider,接著再eclips裡此資料夾下建立 JSSEProvider.java
 
/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/**
 * @author Alexander Y. Kleymenov
 * @version $Revision$
 */


import java.security.AccessController;
import java.security.Provider;

public final class JSSEProvider extends Provider {

    public JSSEProvider() {
        super("HarmonyJSSE", 1.0, "Harmony JSSE Provider");
        AccessController.doPrivileged(new java.security.PrivilegedAction() {
            public Void run() {
                put("SSLContext.TLS",
                        "org.apache.harmony.xnet.provider.jsse.SSLContextImpl");
                put("Alg.Alias.SSLContext.TLSv1", "TLS");
                put("KeyManagerFactory.X509",
                        "org.apache.harmony.xnet.provider.jsse.KeyManagerFactoryImpl");
                put("TrustManagerFactory.X509",
                        "org.apache.harmony.xnet.provider.jsse.TrustManagerFactoryImpl");
                return null;
            }
        });
    }
}
3.在自己的專案src資料夾下建立GMailSender.java
import javax.activation.DataHandler;   
import javax.activation.DataSource;   
import javax.mail.Message;   
import javax.mail.PasswordAuthentication;   
import javax.mail.Session;   
import javax.mail.Transport;   
import javax.mail.internet.InternetAddress;   
import javax.mail.internet.MimeMessage;   

import android.util.Log;

import java.io.ByteArrayInputStream;   
import java.io.IOException;   
import java.io.InputStream;   
import java.io.OutputStream;   
import java.security.Security;   
import java.util.Properties;   

public class GMailSender extends javax.mail.Authenticator {   
    private String mailhost = "smtp.gmail.com";   
    private String user;   
    private String password;   
    private Session session;   

    static {   
        Security.addProvider(new com.provider.JSSEProvider());   
    }  

    public GMailSender(String user, String password) {   
        this.user = user;   
        this.password = password;   
        Log.i("user::::::::", user);
        Log.i("password::::::::", password);
        Properties props = new Properties();   
        props.setProperty("mail.transport.protocol", "smtp");   
        props.setProperty("mail.host", mailhost);   
        props.put("mail.smtp.auth", "true");   
        props.put("mail.smtp.port", "465");   
        props.put("mail.smtp.socketFactory.port", "465");   
        props.put("mail.smtp.socketFactory.class",   
                "javax.net.ssl.SSLSocketFactory");   
        props.put("mail.smtp.socketFactory.fallback", "false");   
        props.setProperty("mail.smtp.quitwait", "false");   

        session = Session.getDefaultInstance(props, this);   
    }   

    protected PasswordAuthentication getPasswordAuthentication() {   
        return new PasswordAuthentication(user, password);   
    }   

    public synchronized void sendMail(String subject, String body, String sender, String recipients) throws Exception {   
        try{
          Log.i("subject::::::::", subject);
             Log.i("body::::::::", body);
             Log.i("sender::::::::", sender);
             Log.i("recipients::::::::", recipients);
        MimeMessage message = new MimeMessage(session);   
        DataHandler handler = new DataHandler(new ByteArrayDataSource(body.getBytes(), "text/plain"));   
        message.setSender(new InternetAddress(sender));   
        message.setSubject(subject);   
        message.setDataHandler(handler);  

        /*******2015.09.29 附加圖片*******/
        // create multipart
        Multipart multipart = new MimeMultipart();

        // create bodypart with image and set content-id
        MimeBodyPart messageBodyPart = new MimeBodyPart();
        File testImage = new File("/storage/emulated/0/Download/", "test.png"); //手機檔案位置,檔案名稱
        DataSource source = new FileDataSource(testImage);
        messageBodyPart.setDataHandler(new DataHandler(source));
        messageBodyPart.setFileName("image.png");
        messageBodyPart.setDisposition(MimeBodyPart.INLINE);
        messageBodyPart.setHeader("Content-ID","");
        multipart.addBodyPart(messageBodyPart);

        // create bodypart with html content and reference to the content-id
        messageBodyPart = new MimeBodyPart();
        String htmlText = "";
        messageBodyPart.setContent(htmlText, "text/html");
        multipart.addBodyPart(messageBodyPart);

        // add multipart to message
        message.setContent(multipart);
        
        /***************參考來源 http://goo.gl/OAZn5q ****************/

 
        if (recipients.indexOf(',') > 0)   
            message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(recipients));   
        else  
            message.setRecipient(Message.RecipientType.TO, new InternetAddress(recipients));   
        Transport.send(message);   
        }catch(Exception e){
          Log.i("eeeeee::::::::", e+"");
        }
    }   

    public class ByteArrayDataSource implements DataSource {   
        private byte[] data;   
        private String type;   

        public ByteArrayDataSource(byte[] data, String type) {   
            super();   
            this.data = data;   
            this.type = type;   
        }   

        public ByteArrayDataSource(byte[] data) {   
            super();   
            this.data = data;   
        }   

        public void setType(String type) {   
            this.type = type;   
        }   

        public String getContentType() {   
            if (type == null)   
                return "application/octet-stream";   
            else  
                return type;   
        }   

        public InputStream getInputStream() throws IOException {   
            return new ByteArrayInputStream(data);   
        }   

        public String getName() {   
            return "ByteArrayDataSource";   
        }   

        public OutputStream getOutputStream() throws IOException {   
            throw new IOException("Not Supported");   
        }   
    }   
}  
4.接著在想觸發的檔案上加上此段code
try {   
                    GMailSender sender = new GMailSender("username@gmail.com", "password"); //寄件者(開發方)帳號與密碼
                    sender.sendMail("This is Subject",   //信件標題
                            "This is Body",   //信件內容
                            "username@gmail.com",   //寄件者
                            "user@yahoo.com");   //收件者
                } catch (Exception e) {   
                    Log.e("SendMail", e.getMessage(), e);   
                } 
5.可能發生問題在 Log.i("eeeeee::::::::", e+"");
A.android.os.NetworkOnMainThreadExceptionMainActivity.java加上此段
/* 加入StrictMode避免發生 android.os.NetworkOnMainThreadException */
  StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
    .detectDiskReads().detectDiskWrites().detectNetwork()
    .penaltyLog().build());
B.javax.mail.AuthenticationFailedException 此為無法登入帳號,可能是帳號密碼錯誤,或是帳號未設定 此時GMail會收到信,點選連結變更設定
https://www.google.com/settings/security/lesssecureapps

 啟動IMAP
 完成
 參考資料:
http://stackoverflow.com/questions/2020088/sending-email-in-android-using-javamail-api-without-using-the-default-built-in-a/2033124#2033124

http://stackoverflow.com/questions/1334802/how-can-i-use-external-jars-in-an-android-project/6859020#6859020

http://bbs.csdn.net/topics/310000387

http://stackoverflow.com/questions/7398382/embedd-inline-images-using-java-mail-in-android

檔案位置:https://github.com/terryyamg/AutoEmail

2014年9月16日 星期二

Android Action Bar search 搜尋

前陣子製作側邊欄,參考[Android]使用 Navigation Drawer 製作側選單(1)[Android]使用 Navigation Drawer 製作側選單(2) ,由於上方有個ActionBar,在這裡面介紹的是按鈕,由於想用類似搜尋功能,於是稍作修改,主要修改地方為"Action Button 建立及點選事件"
1.在資料夾res->menu->main.xml修改



2.在資料夾res->layout->建立search_layout.xml 一個編輯,一個按鈕


    
    

    


3.MainActivity.java修改
 private EditText searchName;
 private String searchObject;
 private Button searchButton;
 // ================================================================================
 // Action Button 建立及點選事件
 // ================================================================================
 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
  getMenuInflater().inflate(R.menu.main, menu);
  View v = (View) menu.findItem(R.id.action_search).getActionView();
  //文字編輯部分
  searchName = (EditText) v.findViewById(R.id.search);
  searchName.addTextChangedListener(new TextWatcher() {
   @Override
   public void afterTextChanged(Editable s) {
    // TODO Auto-generated method stub
   }

   @Override
   public void beforeTextChanged(CharSequence s, int start, int count,
     int after) {
    // TODO Auto-generated method stub
   }

   @Override
   public void onTextChanged(CharSequence s, int start, int before,
     int count) {
    // TODO Auto-generated method stub
    try {
     searchObject = searchName.getText().toString(); // 取得輸入文字
    } catch (Exception e) {

    }

   }
  });
  //送出部分
  searchButton = (Button) v.findViewById(R.id.searchGo);
  searchButton.setOnClickListener(new Button.OnClickListener() {
   @Override
   public void onClick(View v) {
    // TODO Auto-generated method stub
    goSearch(); //要做甚麼
   }
  });

  return true;
 }

 @Override
 public boolean onOptionsItemSelected(MenuItem item) {

  // home
  if (drawerToggle.onOptionsItemSelected(item)) {
   return true;
  }

  // action buttons
  switch (item.getItemId()) {
  //用不到,刪掉
  default:
   break;
  }

  return super.onOptionsItemSelected(item);
 }

 void goSearch() {
  if (searchObject == null) {
  //搜尋空值,不做事
  } else {
   Intent intent = new Intent(this, xxxx.class); //前進至xxxx頁面
   intent.putExtra("searchName", searchObject); //傳值
   startActivity(intent); //啟動出發
  }
 }




參考來源:http://blog.stylingandroid.com/basic-actionbar-part5/
http://wptrafficanalyzer.in/blog/adding-custom-action-view-to-action-bar-in-android/

2014年8月21日 星期四

Android Typeface 字型

更改app上的文字字型
首先要下載中文字型
http://lms.ltu.edu.tw/sys/read_attach.php?id=675313
http://lms.ltu.edu.tw/sys/read_attach.php?id=675314
另外提供一個英文字型網站(英文字型多,中文字少)
http://www.fontsaddict.com/

下載完畢後,解壓縮,選擇想要的字型檔(.ttf)
然後再專案裡的assets建立fonts資料夾
想要的字型檔放入fonts資料夾




使用 Typeface 指令即可修改字型
1.activity_main.xml



2.MainActivity.java
 private TextView output;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  /* 字型 */
  Typeface fontch = Typeface.createFromAsset(getAssets(), "fonts/wt034.ttf");
  output = (TextView) findViewById(R.id.output);
  output.setTypeface(fontch);

 }
參考來源:http://key.chtouch.com/ContentView.aspx?P=216
http://abgne.tw/android/android-code-snippets/android-using-custom-fonts.html

2014年8月15日 星期五

Android ViewPager 前導 說明頁

使用app時會出現前導說明頁
整體流程如下圖
首先要修改先前開機畫面SplashScreen.java檔啟動位置
1.SplashScreen.java
 Intent i = new Intent(SplashScreen.this, Leading.class); //啟動完開機畫面SplashScreen.java後跳轉Leading.java
2.加入Leading.java在AndroidManifest.xml

        
        
3.建立leading.xml,lay1.xml,lay2.xml,lay3.xml
 leading.xml 下方的圓點


    

    

        

        

        
    


lay1.xml 說明頁的第一張圖pic1


    


lay2.xml 說明頁的第二張圖pic2


    



lay3.xml 說明頁的第三張圖pic3+開始體驗Button


    

    


4.Leading.java 說明頁面程式碼
import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.Button;
import android.widget.TextView;

public class Leading extends Activity {
 private ViewPager myViewPager; // 頁卡內容
 private List list; // 存放頁卡
 private TextView dot1, dot2, dot3; // 這些點都是文字
 private Button startButton; // 按鈕,開始體驗

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  requestWindowFeature(Window.FEATURE_NO_TITLE);
  setContentView(R.layout.leading);
  initDot();
  initViewPager();
 }

 private void initDot() {
  dot1 = (TextView) this.findViewById(R.id.textView1); // 這些點都是文字
  dot2 = (TextView) this.findViewById(R.id.textView2);
  dot3 = (TextView) this.findViewById(R.id.textView3);
 }

 private void initViewPager() {
  myViewPager = (ViewPager) this.findViewById(R.id.viewPager);
  list = new ArrayList();

  LayoutInflater inflater = getLayoutInflater();

  View view = inflater.inflate(R.layout.lay3, null); // 只是為了等下findviewbuid而獨立拿出來賦給view

  list.add(inflater.inflate(R.layout.lay1, null));
  list.add(inflater.inflate(R.layout.lay2, null));
  list.add(view);
  try {
   myViewPager.setAdapter(new MyPagerAdapter(list));

   myViewPager.setOnPageChangeListener(new MyPagerChangeListener());
  } catch (NullPointerException e) {
  }
  startButton = (Button) view.findViewById(R.id.start); // 與上面對應,獲取這個按鈕

  startButton.setOnClickListener(new OnClickListener() {

   @Override
   public void onClick(View v) {
    Intent intent = new Intent(Leading.this, MainActivity.class); //按下按鈕後跳轉至MainActivity.java

    startActivity(intent);

   }
  });
 }

 class MyPagerAdapter extends PagerAdapter {
  public List mListViews;

  public MyPagerAdapter(List mListViews) {
   this.mListViews = mListViews;
  }

  @Override
  public void destroyItem(View arg0, int arg1, Object arg2) {
   ((ViewPager) arg0).removeView(mListViews.get(arg1));
  }

  @Override
  public void finishUpdate(View arg0) {
  }

  @Override
  public int getCount() {
   return mListViews.size();
  }

  @Override
  public Object instantiateItem(View arg0, int arg1) {
   ((ViewPager) arg0).addView(mListViews.get(arg1), 0);
   return mListViews.get(arg1);
  }

  @Override
  public boolean isViewFromObject(View arg0, Object arg1) {
   return arg0 == (arg1);
  }

  @Override
  public void restoreState(Parcelable arg0, ClassLoader arg1) {
  }

  @Override
  public Parcelable saveState() {
   return null;
  }

  @Override
  public void startUpdate(View arg0) {
  }
 }

 class MyPagerChangeListener implements OnPageChangeListener {

  @Override
  public void onPageSelected(int arg0) {
   // TODO Auto-generated method stub
   switch (arg0) { // 設置點的顏色
   case 0:
    dot1.setTextColor(Color.WHITE);
    dot2.setTextColor(Color.BLACK);
    dot3.setTextColor(Color.BLACK);
    break;

   case 1:
    dot1.setTextColor(Color.BLACK);
    dot2.setTextColor(Color.WHITE);
    dot3.setTextColor(Color.BLACK);
    break;

   case 2:
    dot1.setTextColor(Color.BLACK);
    dot2.setTextColor(Color.BLACK);
    dot3.setTextColor(Color.WHITE);
    break;

   }
  }

  @Override
  public void onPageScrollStateChanged(int arg0) {
   // TODO Auto-generated method stub

  }

  @Override
  public void onPageScrolled(int arg0, float arg1, int arg2) {
   // TODO Auto-generated method stub

  }

 }
}

參考來源:http://blog.segmentfault.com/zhongbaitu/1190000000398209

2014年8月4日 星期一

Android 上架至 google play 問題

基本上參考此連結http://xyz.cinc.biz/2013/06/android-app.html
寫得很清楚,但步驟3完會出現strings.xml錯誤的訊息

找了一下

點選eclipse的 "Window" > "Preferences" > "Android" > "Lint Error Checking"
找到 Id欄 是"MissingTranslation" 點選後Erroe修改成Warning就可以了

參考連結:
http://stackoverflow.com/questions/11443996/lint-how-to-ignore-key-is-not-translated-in-language-errors

2014年7月18日 星期五

Android share 分享功能

分享至facebook,line之類的
1.activity_main.xml
建立一個分享按鈕

2.MainActivity.java
 private Button shareButton;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  shareButton = (Button) findViewById(R.id.shareButton);
  shareButton.setOnClickListener(new Button.OnClickListener() {
   public void onClick(View v) {
    shareDialog();
   }
  });
 }

 // 分享app
 void shareDialog() {
  
  String shareText = "與你分享的快樂勝過獨自擁有"; //
  //Uri imageUri = Uri.parse("android.resource://" + getPackageName() + "/drawable/" + "ic_launcher"); //分享圖片至gmail、twitter可,line、facebook不行
  //Log.i("imageUri:", imageUri + "");
   Intent shareIntent = new Intent();
   shareIntent.setAction(Intent.ACTION_SEND);
   shareIntent.setType("text/plain"); //文字檔類型
   shareIntent.putExtra(Intent.EXTRA_TEXT, shareText); //傳送文字
   shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
   //shareIntent.setType("image/png"); //圖片png檔類型
  // shareIntent.putExtra(Intent.EXTRA_STREAM, imageUri); //傳送圖片
   startActivity(Intent.createChooser(shareIntent, "分享"));
 }
參考來源:
http://hscc.cs.nctu.edu.tw/~lincyu/Android/ShareText.pdf
http://stackoverflow.com/questions/20333186/how-to-share-imagetext-using-action-send-in-android

2014年7月9日 星期三

Android google map 結合 spinner 下拉式選單

利用下拉式選單選定某個項目後,直接在google map上移到定點
1.activity_main.xml 在地圖下方加入下拉式選單




2.MainActivity.java 以基隆市、台北市85度c為例
private float coordinate[][][] = {
   { { 0, (float) 25.1288160, (float) 121.7404860 },
     { 0, (float) 25.1286310, (float) 121.7594120 },
     { 0, (float) 25.0972380, (float) 121.7122780 } },
   { { 1, (float) 25.0574640, (float) 121.5554840 },
     { 1, (float) 24.9836380, (float) 121.5673320 },
     { 1, (float) 25.0310780, (float) 121.5522090 } },
    }; //設定定點座標
private String[] type = new String[] { "基隆市", "台北市" }; //第一個選單
 private String[] locationName = new String[] { "85度c基隆孝一店", "85度c基隆東信店","85度c七堵明德店" }; //初始選單
 private String[][] type2 = new String[][] {
   { "85度c基隆孝一店", "85度c基隆東信店", "85度c七堵明德店" },
   { "85度c台北光復北店", "85度c木柵木新店", "85度c台北安和店" } };//第二個選單
 private Spinner sp1;// 第一個下拉選單
 private Spinner sp2;// 第二個下拉選單
 private Context context;
 ArrayAdapter adapter;
 ArrayAdapter adapter2;
@Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  context = this;
  // 程式剛啟動時載入第一個下拉選單
  adapter = new ArrayAdapter(this,
    android.R.layout.simple_spinner_item, type);
  adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
  sp1 = (Spinner) findViewById(R.id.type);
  sp1.setAdapter(adapter); //將adapter加入sp1選單
  sp1.setOnItemSelectedListener(selectListener); //第一個選單監聽動作

  // 程式剛啟動時載入第一個下拉選單,所以第二選單要載入第一個選單的子項目
  adapter2 = new ArrayAdapter(this,
    android.R.layout.simple_spinner_item, locationName);
  adapter2.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
  sp2 = (Spinner) findViewById(R.id.type2);
  sp2.setAdapter(adapter2); //將adapter加入sp2選單
  sp2.setOnItemSelectedListener(selectListener2); //第二個選單監聽動作
 }

 // 第一個下拉選單監聽動作
 private OnItemSelectedListener selectListener = new OnItemSelectedListener() {
  public void onItemSelected(AdapterView parent, View v, int position,
    long id) {
   // 讀取第一個下拉選單是選擇第幾個
   int pos = sp1.getSelectedItemPosition();
   // 重新產生新的Adapter,用的是二維陣列type2[pos]
   adapter2 = new ArrayAdapter(context,
     android.R.layout.simple_spinner_item, type2[pos]);
   // 載入第二個下拉選單Spinner
   sp2.setAdapter(adapter2);

  }

  public void onNothingSelected(AdapterView arg0) {

  }

 };

 // 第二個下拉選單監聽動作
 private OnItemSelectedListener selectListener2 = new OnItemSelectedListener() {
  public void onItemSelected(AdapterView parent, View v, int position,
    long id) {
   setMapLocation(); //移動到選定項目位置的動作
  }

  public void onNothingSelected(AdapterView arg0) {

  }

 };

 public void setMapLocation() {
  try {
   int iSelect1 = sp1.getSelectedItemPosition(); //第一個下拉選單被選到的第幾個項目
   int iSelect2 = sp2.getSelectedItemPosition(); //第二個下拉選單被選到的第幾個項目
   double dLat = coordinate[iSelect1][iSelect2][1]; // 抓取座標,南北緯
   double dLon = coordinate[iSelect1][iSelect2][2]; // 抓取座標,東西經
   LatLng gg = new LatLng(dLat, dLon); //將座標位置輸入設定
   map.moveCamera(CameraUpdateFactory.newLatLng(gg)); //移動到定點
   map.animateCamera(CameraUpdateFactory.zoomTo(15)); //縮放
  } catch (NullPointerException e) {
   Log.i("myLocation", "NullPointException");
  }
 }
參考來源: http://jim690701.blogspot.tw/2012/07/androidspinner_15.html

2014年7月4日 星期五

Android ClassCastException 幽靈錯誤

小技巧
當LogCat出現
Caused by: java.lang.ClassCastException: xxxx cannot be cast to xxxx
這種錯誤時,可以先執行eclips上的Project->clean...
就可以移除這些幽靈的錯誤
參考來源: http://stackoverflow.com/questions/12465551/android-widget-textview-cannot-be-cast-to-android-widget-button

2014年6月25日 星期三

Android Button UI 按鈕 美化

改變基本灰色樣式按鈕

1.首先要在drawable-hdpi建立一個xml檔
 選擇Android XML File->Next
選擇Drawable->命名->選擇selector
 btn_black.xml加入程式碼
這裡舉一個黑色的按鈕,其他顏色請參考來源網站

    
        
         
            
            
            
        
    
    
        
            
            
            
            
        
    

2.在你的strings.xml裡加入

3.activity_main.xml
"@drawable/btn_black"->btn_black.xml名稱
"@style/ButtonText"->strings.xml裡style name="ButtonText"名稱
參考來源: http://androidtutorials4beginners.blogspot.tw/2013/04/android-colored-gradient-buttons-using.html

2014年6月24日 星期二

Android google map addMarker setOnMarkerClickListener 標記後動作

使用google map標記功能addMarker後,需要增加動作可以使用OnMarkerClickListener和OnMarkerDragListene
例如:點取標記後,跳去其他頁面。
前置作業:有使用google map,有使用addMarker
1.MainActivity.java
public class MainActivity extends FragmentActivity implements OnMarkerClickListener{ //需引用OnMarkerClickListener
 private GoogleMap map;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 map = ((SupportMapFragment) getSupportFragmentManager()
    .findFragmentById(R.id.map)).getMap();
  
  LatLng p1 = new LatLng(22.6297370, 120.3278820);
  if (map != null) {
   map.setOnMarkerClickListener(this); //點取Marker動作
   // google mark
   map.addMarker(new MarkerOptions()
     .position(p1)
     .title("多那之高雄中正門市")
     .snippet("咖啡.烘培"));
  }

 }

 /* 點選marker顯示 */
 @Override
   public boolean onMarkerClick(Marker marker) {
  /* 此處增加點取後想要的動作*/
  Toast.makeText(getApplicationContext(),"Marker Clicked: " + marker.getTitle(), Toast.LENGTH_LONG).show(); //顯示點取addMarker的標題
     return false;
   }

}

2014年6月19日 星期四

Android QRcode

掃描QRcode 讓資訊顯示出來
需要安裝QRDroid
1.activity_main.xml

            
2.MainActivity.java
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);

  info = (TextView) findViewById(R.id.info);
  scanner = (Button) findViewById(R.id.scanner);
  scanner.setOnClickListener(new Button.OnClickListener() {
   @Override
   public void onClick(View v) {

    // TODO Auto-generated method stub
    Intent iScaner = new Intent("la.droid.qr.scan"); // 使用QRDroid的掃描功能
    iScaner.putExtra("la.droid.qr.complete", true); // 完整回傳,不截掉scheme
    try {
     // 開啟 QRDroid App 的掃描功能,等待 call back onActivityResult()
     startActivityForResult(iScaner, 0);
    } catch (ActivityNotFoundException ex) {
     // 若沒安裝 QRDroid,則開啟 Google Play商店,並顯示 QRDroid App
     Intent intent = new Intent(Intent.ACTION_VIEW, Uri
       .parse("market://details?id=la.droid.qr"));
     startActivity(intent);
    }
   }
  });
 }

 @Override
 protected void onActivityResult(int requestCode, int resultCode, Intent data) { //不需要建立onActivityResult
  super.onActivityResult(requestCode, resultCode, data);

  if (0 == requestCode && null != data && data.getExtras() != null) {
   // 掃描結果存放在 key 為 la.droid.qr.result 的值中
   String result = data.getExtras().getString("la.droid.qr.result");

   info.setText(result); // 將結果顯示在 TextVeiw 中
  }
 }
3.建立QRcode的網站 http://www.quickmark.com.tw/Cht/qrcode-datamatrix-generator/default.asp?qrText
4.掃描圖片




參考來源:
http://elviselle.blogspot.tw/2013/06/android-app-qr-code.html

2014年6月16日 星期一

Android shortcut 桌面 捷徑

自動建立桌面捷徑的方法,使用shortcut
首先要宣告
1.AndroidManifest.xml

2.MainActivity.java
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  addShortcut();
 }
/* 建立桌面捷徑 */
 private void addShortcut() {
  Intent shortcutIntent = new Intent(getApplicationContext(),
    MainActivity.class); // 啟動捷徑入口,一般用MainActivity,有使用其他入口則填入相對名稱,ex:有使用SplashScreen
  shortcutIntent.setAction(Intent.ACTION_MAIN);
  Intent addIntent = new Intent();
  addIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent); // shortcutIntent送入
  addIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME,
    getString(R.string.app_name)); // 捷徑app名稱
  addIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
    Intent.ShortcutIconResource.fromContext(
      getApplicationContext(),// 捷徑app圖
      R.drawable.ic_launcher));
  addIntent.putExtra("duplicate", false); // 只創建一次
  addIntent.setAction("com.android.launcher.action.INSTALL_SHORTCUT"); // 安裝
  getApplicationContext().sendBroadcast(addIntent); // 送出廣播
 }
參考來源:
http://stackoverflow.com/questions/22829647/programmatically-add-the-widget-to-home-screen-in-android
http://stackoverflow.com/questions/21542409/home-screen-app-shortcut-is-not-working-android-app-isnt-installed

2014年6月12日 星期四

Android Apk Download Install Update 下載安裝 更新 APP

大陸更新app方法從網路上找到這篇http://mft.iteye.com/blog/1686524
基本上就是把apk檔跟一個文件檔(版本資訊)放在雲端上面,但就卡在版本資訊,上面連結沒有寫這部分,於是我找到了要用json,這裡卡超久,最後找到此連結http://blog.it4fun.net/archives/112 
,接著出現問題2,android改版後網路要求變嚴格,會出現android.os.NetworkOnMainThreadException問題,解決此參考http://goo.gl/yFbGdR
,接著還有問題3 ,java.lang.string cannot be converted to jsonarray,解決方法http://goo.gl/Qv0mym
就是不能用POST,要用GET。

1.MainActivity.java
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.os.StrictMode;
import android.util.Log;
import android.widget.Toast;

public class MainActivity extends Activity {
 /* update */
 protected static final int UPDATA_CLIENT = 0;
 protected static final int CHECK_UPDATE = 1;
 protected static final int DOWN_ERROR = 0;
 /** Called when the activity is first created. */
 private int serverVersion = 1; // 現在版本
 private int newServerVersion; // 最新版本
 private String downLoadApkUrl = "https://dl.dropbox.com/s/vahfdayyluk151z/Android.apk"; // 放置最新檔案網址

 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  /* update */
  /* 加入StrictMode避免發生 android.os.NetworkOnMainThreadException */
  StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
    .detectDiskReads().detectDiskWrites().detectNetwork()
    .penaltyLog().build());
  StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
    .detectLeakedSqlLiteObjects().detectLeakedClosableObjects()
    .penaltyLog().penaltyDeath().build());

  JSONArray obj = getJson("http://terryyamg.github.io/myweb/update_verson.json"); // 更新版本文件檔位置
  Log.i("obj:", obj + "");
  try {
   for (int i = 0; i < obj.length(); i++) {
    JSONObject data = obj.getJSONObject(i);
    newServerVersion = Integer.parseInt(data.getString("code")); // code為名稱,抓出來newServerVersion為值
   }
  } catch (JSONException e) {

  } catch (NullPointerException e) {
   Log.i("data", "NullPointException");
  }

  new Thread(new Runnable() {
   public void run() {
    try {
     Message msg = new Message();
     msg.what = CHECK_UPDATE;
     handler.sendMessage(msg);

    } catch (NumberFormatException e) {
     // TODO Auto-generated catch block

    } catch (Exception e) {
     // TODO Auto-generated catch block

    }

   }
  }).start();
 }

 /* update */
 public static JSONArray getJson(String url) {
  InputStream is = null;
  String result = "";
  // 若線上資料為陣列,則使用JSONArray
  JSONArray jsonArray = null;
  // 若線上資料為單筆資料,則使用JSONObject
  // JSONObject jsonObj = null;
  // 透過HTTP連線取得回應
  try {
   HttpClient httpclient = new DefaultHttpClient(); // for port 80
   HttpGet httppost = new HttpGet(url); // 要用Get,用Post會出現
             // java.lang.string cannot
             // be converted to jsonarray
   HttpResponse response = httpclient.execute(httppost);
   Log.i("response:", response + ""); // 沒有值會catch錯誤,加入前面StrictMode就可以
   HttpEntity entity = response.getEntity();
   Log.i("entity:", entity + "");
   is = entity.getContent();
   Log.i("is:", is + "");
  } catch (Exception e) {
   e.printStackTrace();
  }
  // 讀取回應
  try {
   BufferedReader reader = new BufferedReader(new InputStreamReader(
     is, "utf8"), 9999999); // 99999為傳流大小,若資料很大,可自行調整
   StringBuilder sb = new StringBuilder();
   String line = null;
   while ((line = reader.readLine()) != null) {
    // 逐行取得資料
    sb.append(line + "\n");
   }
   is.close();
   result = sb.toString();
   Log.i("result:", result + ""); // LogCat會印出json ex:[{"code":"1"}]
  } catch (Exception e) {
   e.printStackTrace();
  }
  // 轉換文字為JSONArray
  try {
   jsonArray = new JSONArray(result);
  } catch (JSONException e) {
   e.printStackTrace();
  }
  return jsonArray;
 }

 public void showUpdateDialog() {

  @SuppressWarnings("unused")
  AlertDialog alert = new AlertDialog.Builder(MainActivity.this)
    .setTitle("更新提示").setIcon(android.R.drawable.ic_dialog_info)
    .setMessage("最新優惠出來啦,快來下載更新")
    .setPositiveButton("下載", new DialogInterface.OnClickListener() {

     public void onClick(DialogInterface dialog, int which) {
      dialog.dismiss(); // 關閉對話框
      downLoadApk();
     }

    }).show();

 }

 protected void downLoadApk() {
  final ProgressDialog pd; // 進度條對話框
  pd = new ProgressDialog(this);
  pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
  pd.setMessage("正在下載更新");
  pd.show();
  new Thread() {
   @Override
   public void run() {
    try {
     File file = getFileFromServer(downLoadApkUrl, pd);
     sleep(3000);
     installApk(file);
     pd.dismiss(); // 結束進度條對話框
    } catch (Exception e) {
     pd.dismiss();
     Message msg = new Message();
     msg.what = DOWN_ERROR;
     handler.sendMessage(msg);
     e.printStackTrace();
    }
   }
  }.start();
 }

 public static File getFileFromServer(String path, ProgressDialog pd)
   throws Exception {
  /* 如果相等的話表示當前的SDcard掛載在手機上並且是可用的 */
  if (Environment.getExternalStorageState().equals(
    Environment.MEDIA_MOUNTED)) {
   URL url = new URL(path);
   HttpURLConnection conn = (HttpURLConnection) url.openConnection();
   conn.setConnectTimeout(5000);
   pd.setMax(conn.getContentLength()); // 獲取副本文件大小
   InputStream is = conn.getInputStream();
   File file = new File(Environment.getExternalStorageDirectory(),
     "update.apk");
   FileOutputStream fos = new FileOutputStream(file);
   BufferedInputStream bis = new BufferedInputStream(is);
   byte[] buffer = new byte[1024];
   int len;
   int total = 0;
   while ((len = bis.read(buffer)) != -1) {
    fos.write(buffer, 0, len);
    total += len;
    pd.setProgress(total); // 獲取當前下載量
   }
   fos.close();
   bis.close();
   is.close();
   return file;
  } else {
   return null;
  }
 }

 /* 安裝APK */
 protected void installApk(File file) {
  Intent intent = new Intent();
  intent.setAction(Intent.ACTION_VIEW); // 執行動作
  intent.setDataAndType(Uri.fromFile(file),
    "application/vnd.android.package-archive"); // 執行的數據類型
  intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); //2015.05.28 update 安裝完成後啟動app
  startActivity(intent);
 }

 Handler handler = new Handler() {

  @Override
  public void handleMessage(Message msg) {
   // TODO Auto-generated method stub
   super.handleMessage(msg);
   switch (msg.what) {
   case DOWN_ERROR:
    // 下載APK失敗
    Toast.makeText(getApplicationContext(), "下載新版本失敗", 1).show();
    break;
   case CHECK_UPDATE:
    // 檢查更新

    if (serverVersion == 0) {
     serverVersion = newServerVersion;
    }

    if (serverVersion != newServerVersion) {
     Log.i("serverVersion:", serverVersion + ""); // 顯示現在版本
     Log.i("newServerVersion:", newServerVersion + ""); // 顯示最新版本
     showUpdateDialog(); // 執行更新
    }
    break;
   }
  }
 };
}
2.AndroidManifest.xml
加入使用網路權限

3.update_verson.json
[{"code":"1"}]