總網頁瀏覽量

顯示具有 android 標籤的文章。 顯示所有文章
顯示具有 android 標籤的文章。 顯示所有文章

2013年4月22日 星期一

播放音樂、背景音樂

MediaPlayer 不支援多個音樂同時播放
SoundPool 音樂容量只允許10M





private MediaPlayer player;


1.從內部儲存空間播放


player.setDataSource("/mnt/sdcard/slashwater.aif");
無法播放aif
// player.setDataSource("/mnt/sdcard/morzart.mid");

player.prepare();
player.start();


2.從網路播放


3.從res/raw播放

Android一些理論筆記

Activity:


1.每個activity是一個螢幕
關於activity生命週期查閱lifecycle

2.activity有兩種宣告方式:

第一種透過XML設定檔宣告:
activity設定檔位於res/layout底下
一個xml相當於一個view容器

第二種自訂一個繼承View的類別
之後再MainActivity實體化並setContentView
MySelfeView mcv = new MySelfeView(this);
setContentView(mcv);//設定目前螢幕


Service元件

1.
Service 沒有提供與用戶進行互動的展示層,Service是執行在後台的元件
Service通常由Activity或其他的Context物件來啟動
Service通常在Activity生命週期結束仍會運行
每個Service應該在AndroidMainifest.xm.進行宣告

2.
Service啟動方式有兩種:
第一透過startService(Intent)啟動。

透過startService(Intent)呼叫的service生命週期:跟Activity差不多
onCreate() onStart() onDestroy()

第二透過bindService方法啟動,然後Service會跟呼叫Service的物件(Context,就是Activity)綁住
當被綁定的Context被銷毀時 Service也會連帶停止執行
生命週期大致如下順序
onCreate()  onBind()  onUnbind() /onDestory()



Broadcast Receiver元件:

負責接收廣播訊息並對訊息做出反應的元件
應用:當電池電量過低、或信號過低時 系統發廣播進行通知


Content Provider元件


Service

Service:
播歌、下載檔案、電池快耗盡通知、mail來電通知

Service通常會啟動另外一個thread處理較花時間的程序


一、Notification,通知服務的功能實作(有部分method是deprecation)

1.在MianActivity宣告intent並startService

Intent it = new Intent(this,MyService.class);
it.putExtra("Name", "Morzart");
startService(it);


2.新增class繼承extends Service
extends Service

3.
務必記得去Mainifest.xml
註冊Service不然是不會執行Service的

4.
override    onStartCommand(Intent intent)。
//接收啟動Service的Activity Inent物件
//如果沒有特別事情,可以省略

public int onStartCommand(Intent intent, int flags, int startId) {
String name = intent.getStringExtra("Name");
Log.i("YYP",name);
//Toast.makeText(this, name, Toast.LENGTH_SHORT).show();
return super.onStartCommand(intent, flags, startId);
}




宣告
private Timer timer;

在onCreate(){
timer= new Timer();
ttimer.scheduel(new MyNotice(),10000);

}
//我們會發現當啟動Service之後會先onCreate 接著再執行onStartCommand


//接著宣告thread處理service要做的事情
//本例子是在state bar顯示通知
//使用者按下通知時候會跳到另一個頁面

private class MyNotice extends TimerTask{

public void run(){
NotificationManager mgr= (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
//notificationManager負責整個系統通知控制
//NotificationManager屬於系統管理類別因此要從getSystemService取得notification服務
@SuppressWarnings("deprecation")
Notification notice = new Notification(R.drawable.ic_launcher,"重要通知",System.currentTimeMillis());
//notification 物件此物件名稱是"重要通知"
//(顯示在state Bar的icon,顯示再狀態bar的文字,顯示的時戳)
//System.currentTimeMillis()取得現在時間


notice.flags=Notification.FLAG_AUTO_CANCEL;
//按一下service後notification會Service列表消失
// notice.flags=Notification.FLAG_FOREGROUND_SERVICE;
//Service為常駐狀態,常駐在service列表
// notice.flags=Notification.FLAG_ONLY_ALERT_ONCE;
//Service為常駐狀態,常駐在service列表

//flag表用來標記notification是何種類型任務


Intent it = new Intent(this,Page2.class);
//按下Service通知 進入page2
PendingIntent pit = PendingIntent.getActivity(this, 1, it, 0);
// PendingItent pit = PendingIntent.getActivities(context, requestCode, intents, flags);
//pending:即將發生的。
//顧名思義是宣告暫時存放的intent
//PendingIntent pit = PendingIntent.getBroadcast(this,1, it, 0);
//PendingIntent pit = PendingIntent.getService(this,1, it, 0);


notice.setLatestEventInfo(this, "重要訊息", "好好學習天天向上", pit);
//deprecated
//notice.setLatestEventInfo(context, contentTitle, contentText, contentIntent)
//當使用者點下該notification之後會去執行pit的intent

mgr.notify(1, notice);
//指定要顯示的notification物件以及識別ID
}
}



最後要停止Service,要宣告intent物件
再呼叫stopService()

private void toStop(){
Intent it = new Intent(this,MyService.class);
stopService(it);
//執行到這,會呼叫Serivce的onDestroy()
//雖然stop了 但是service仍會在service列表裡
}



二、廣播訊息、接收廣播訊息

在Service的.java檔
實作發送廣播訊息,以TimerTask實作之


timer.schedule(new MyTask(), 0,500);



private class MyTask extends TimerTask{

@Override
public void run() {

//Toast.makeText(this, "service start", Toast.LENGTH_SHORT).show();
//要Handler

int temp=(int)(Math.random()*49+1);
Intent it = new Intent("MyAction");
//Intent(String Action)  給intent貼標籤"MyAction"
it.putExtra("value", temp);//想要傳遞的東西放進去
sendBroadcast(it);//廣播出去由BroadcastReceiver接收
}
}

在MainActivity裡面:
實作接收訊息

宣告
private class MyReceiver extends BroadcastReceiver{


@Override
public void onReceive(Context arg0, Intent intent) {
int value = intent.getIntExtra("value", -1);
//int value = intent.getIntExtra("", defaultValue)

tv.setText(""+value);
}



}


宣告:

private MyReceiver receiver;

receiver = new MyReceiver();

IntentFilter filter = new IntentFilter("MyAction");
//只接收"MyAction"的intent廣播

//IntentFilter filter = new IntentFilter("MyAction2");
//如果這裡改為"MyAction2"會收不到MyAction的intent廣播


registerReceiver(receiver,filter);


2013年4月20日 星期六

簽名、手記製作

程式的目的:
將手機螢幕當作便條紙可以在上面畫圖。


一、畫圖實作


圖構成的邏輯:
畫圖由多條線構成一張圖
而兩個以上的點構成一條線

實作:
1.宣告LinkedList 裡面存放多條線(多個HashMap物件)
2.宣告HashMap代表一個線條,HashMap存放線條的顏色、粗細,以及構成線條的座標
3.HashMap裡面其中一欄位存放LinkedList存放多個座標,每一個座標以HashMap姿態放進LinkedList中
(一條線由多個座標連起來所構成)
因此實作LinkedList為容器存放多個座標,

接著利用GestureDetector以及onTouchEvent onDraw onDown來實作將HashMap放進LinkedList
或者取出的動作啦


ScrollView:ScrollView也是layout一種,只限定vertical的方向。當scrollview裡面的元件超過螢幕顯示範圍,可以滾動方式取得元件。不可以放在LinearLayout之內。

自訂View:新增.java類別,類別必須繼承(extends View)。
且必須有建構式,本例子的建構式接收Context,AttributeSet

所謂Context就是指向parent對象的指標(或參考),受到parent控制
通常Context指向Activity
Context可以理解成程式(物件)所在的環境(狀態)
Context裡面存放Activity各式各樣的資訊


首先,準備好painel view
public class MainPanel extends View


在建構式宣告畫筆Paint
以及手勢偵測物件,告知gesturedetector所在context以及listener

在建構式外面宣告傾聽者類別繼承手勢傾聽者
new GestureDetector(context,MyListenter)
private void MyListener extends SimpleOnGestureListener{}


GestureListener要override OnDown 以及onScroll method
MainPanel(View) 要override onTouchEvent 以及onDraw method


範例程式:


public class MainPanel extends View {
private Paint p;// 畫筆
private GestureDetector gd;
private Context context;
private HashMap<String, Object> line;//
private LinkedList<HashMap<String, Object>> lines, redos;
private int size;// 筆畫粗細
private boolean isInit;
private boolean isDrawing;

public MainPanel(Context c, AttributeSet attrs) {
super(c, attrs);
context = c;// 從mainActivity來的?

//Log.i("context",context.toString());
//context指的是MainActivity@416a87d0

size = 10;
p = new Paint();
p.setColor(Color.MAGENTA);
p.setStrokeWidth(size);

gd = new GestureDetector(context, new MyListener());
// 手勢偵測物件,偵測碰觸螢幕的事件

setBackgroundColor(Color.TRANSPARENT);// View的顏色與parent一樣

}

private class MyListener extends SimpleOnGestureListener {

@Override
public boolean onDown(MotionEvent e) {
//onTouchEvent return gd.onTouchEvent(event)且action_down 會觸發這裡
//Log.i("Sing","onDown");

//onDown表示線條起點

float x = e.getX(), y = e.getY();
//手指碰到的座標

line.put("color", p.getColor());
line.put("size", (int)p.getStrokeWidth());

LinkedList<HashMap<String,Float>> lxy = new LinkedList();
HashMap<String,Float> xy = new HashMap();
xy.put("x", x);
xy.put("y", y);

lxy.add(xy);//HashMap放進Linklist
//將起始座標放入lxy[0]

line.put("line", lxy);//起始座標
// return super.onDown(e);
return true;
//retrun true會持續觸發touchevent 以及onScroll
}

// @Override
// public boolean onScroll(MotionEvent e1, MotionEvent e2,
// float distanceX, float distanceY) {
// Log.i("Sing","onScroll");
//
// return super.onScroll(e1, e2, distanceX, distanceY);
// }

}//GuestureDetector

//初始化
private void init(){
lines = new LinkedList<HashMap<String,Object>>();
redos= new LinkedList<HashMap<String,Object>>();

isDrawing = false;
isInit =true;

}


@Override
protected void onDraw(Canvas canvas) {
Log.i("Sing","onDraw");
super.onDraw(canvas);

if(!isInit) init();
//初始化,因為onDraw相當於View的main,
//但是有時會自動執行多次 所以就用init()初始化


//第一次進入這裡不會在View上畫出東西
//第一次進入畫面不會跑進foreachloop
for(HashMap<String,Object> line : lines){

// p.setColor(Color.BLACK);
// p.setStrokeWidth(3);
//所以這邊可以用條件判斷,當line裡面lxy的index符合某條件時
//更改線條顏色

p.setColor((Integer)line.get("color"));
//Log.i("Conan"," "+(Integer)line.get("color"));
p.setStrokeWidth((Integer)line.get("size"));
// Log.i("Conan"," "+(Integer)line.get("size"));

LinkedList<HashMap<String,Float>> lxy = (LinkedList)line.get("line");
//第一次treace程式碼:line存放兩個座標了

for(int i=1; i<lxy.size();i++){
HashMap<String,Float> xy0 = lxy.get(i-1);
//取得linkedlist 裡面物件 ,節點1
HashMap<String,Float> xy1 = lxy.get(i);
//節點2
canvas.drawLine(xy0.get("x"), xy0.get("y"), xy1.get("x"), xy1.get("y"), p);
// 畫線

}


}

}
@Override
public boolean onTouchEvent(MotionEvent event) {
// Log.i("Sing","onTouchEvent");

if(!isDrawing){

//之前尚未開始畫圖,進入這裡進行初始化
//第二次treace:新增第二條線
line = new HashMap();
lines.add(line);//line是一條線,lines放多條線的LinkedList
//第二次trace到這,在lines[1]加上line HashMap物件
//還沒設定線條顏色與粗細
// Log.i("YYP", "" + lines.size());

//lines放n條線
isDrawing = true;
return gd.onTouchEvent(event);//第一次onTouchEvent後跳去onDown
}
else if(isDrawing && event.getAction()==MotionEvent.ACTION_UP)
{//正在畫圖  手指離開螢幕
isDrawing = false ;
return false;
}
else
{//isDrawing = true  event.getAction()==MotionEvent.Action_Move
//正在畫圖
LinkedList<HashMap<String,Float>> lxy = (LinkedList)line.get("line");
//這裡的line跟onDwon的line是同一個
//第二次trace到這邊:line裡面已經有兩個座標,
HashMap<String,Float> xy = new HashMap();
xy.put("x", event.getX());//線的終點
xy.put("y", event.getY());
//第二次trace到這邊:增加第三座標
lxy.add(xy);
//第一次trace將座標放入lxy[1]
//第二次trace到這 ,座標放入lxy[2]
postInvalidate();//呼叫ondraw
return gd.onTouchEvent(event);

}

// String str=event.toString();
// Log.i("Sing",str);
// int action = event.getAction();
//    cordinateX = (int) event.getX();
//    cordinateY = (int) event.getY();
//    switch (action) {
//    // 按下那一刻的動作
//    case MotionEvent.ACTION_DOWN:
// Log.v("Sing", "ACTION_DOWN");
// break;
//    // 移動的動作
//    case MotionEvent.ACTION_MOVE:
// Log.v("Sing", "ACTION_MOVE");
// break;
//    // 抬起的動作
//    case MotionEvent.ACTION_UP:
// Log.v("Sing", "ACTION_UP");
//// postInvalidate();//好像開啟另一條thread呼叫onDraw吧?
// break;  
//    }
//    return super.onTouchEvent(event);
//    return true;
}
}




二、開新檔案的功能:


以上完成之後,手指在螢幕上畫圖表示
將線條座標一一塞入LinkedList裡面。
開新檔案就是將LinkedList裡面的物件(線條、座標)清空
最快的方法重新new一個LinkedList物件,讓line指向新的物件
這樣就達到開新檔案了。



在MainPanel裡面宣告
void doInit(){
isInit=false;
postInvalidate;
//執行上面的onDraw
}

在MainActivity
宣告view變數
private MainPanel mp;
mp= (MainPanel)findViewById(R.id.mp);
//若沒寫這行會出現nullPointException

將mp.doInit();寫在btnOnClick事件裡。


三、復原、重做

以一個線條為單位復原重作。



void undo() {
//復原表示將LinkedList的元素逐一刪掉
if (!lines.isEmpty()) {
//lines.removeLast();
//
// lines裡面將線條移除
redos.add(lines.removeLast());
              //刪掉的元素放進redo
postInvalidate();
}
}
void redo(){

if((redos!=null)&&(redos.size()>0)){

lines.add(redos.removeLast());
//將redo的東西重新放入lines
postInvalidate();
}
}


四、自訂線條顏色(color picker實作)

取用別人寫好的code當作自己的函式庫libray
引用別人寫好的函式庫

1.
首先下載
android color picker
整個project程式碼

2. import android-color-picker

3.import之後在android-color-picker專案點右鍵,選properties
之後選擇"android"     之後將"is Libray"打勾   之後點apply並關閉視窗


4.回到原本project
在原本project點右鍵跟上面一樣選properties 選android
之後點add
將android-color-picker加入
這樣一來可以將自己/別人寫的code當作函式庫引用了

private void colorPicker(){
// initialColor is the initially-selected color to be shown in the rectangle on the left of the arrow.
// for example, 0xff000000 is black, 0xff0000ff is blue. Please be aware of the initial 0xff which is the alpha.
AmbilWarnaDialog dialog = new AmbilWarnaDialog(this,mp.getColor, new OnAmbilWarnaListener() {
        @Override
        public void onOk(AmbilWarnaDialog dialog, int color) {
                // color is the color selected by the user.
mp.setColor(color);        
}
                
        @Override
        public void onCancel(AmbilWarnaDialog dialog) {
                // cancel was selected by the user
        }
});
}

相關步驟以及整個code下載來源如下:
https://code.google.com/p/android-color-picker/





五、儲存檔案


首先去mainifest.xml設定權限
android.permission.WRITE_EXTERNAL_STORAGE
允許程式可以將資料儲存到手機上面去

接著利用MainPanel extends View物件的setDrawingCacheEnabled(true)

mp.setDrawingCacheEnabled(true);
讓onDraw的資訊可以快取在記憶體
不寫這行,下面程式會出現NotFoundException之類的字眼
Bitmap物件在記憶體中抓取不到資料


private void save(){}
FileOutputStream fout;
try{
fout = new FileOutputStream("/mnt/sdcard/name.png");
//指定輸出到內部儲存空間

Bitmap bmp = mp.getDrawingCache();
//Bitmap物件 接收快取記憶體裡的資料
bmp.compress(Compress.PNG,100,fout);
//輸出格式  輸出品質 輸出目的
fout.flush();
fout.close();


Toast.makeText(this, "saveOK", Toast.LENGTH_SHORT).show();

}catch(Exception e){
Log.i("Exception",e.toString());


}


六、自訂線條大小


SeekBar實作

private SeekBar seekbar;
seekbar = (SeekBar)findViewById(R.id.seekbar);
seekbar.setProgress(mp.getSize());
//設定seekbar初始值
seekbar.setMax(10);

//設定seekbar最大值

 seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener(){

@Override
public void onProgressChanged(SeekBar arg0, int progress, boolean fromUser) {
//progress:seekbar的值
mp.setSize(progress);
}

@Override
public void onStartTrackingTouch(SeekBar arg0) {

}

@Override
public void onStopTrackingTouch(SeekBar seekBar) {

}});





int getSize(){

return (int)p.getStrokeWidth();

}

void setSize(int size){
p.setStrokeWidth(size);


}



七、自訂歡迎畫面



Application Node新增Activity
在Activity底下新增
intent filter將其他activity的intent filter刪掉
這樣可以依照自己意思指定起始畫面了



PS:



private Resource res;
res = getResources();//從Context取得Resource物件
//Resource物件可以在.java檔案取得該專案的xml資源設定值



Toast.makeText(this, res.getString(R.string.btn_save), Toast.LENGTH_SHORT).show();
//re.getString(R.string.btn_save);  印出string.xml中所對應的字串
去看values/string.xml有這樣標籤
<string name="btn_save">Save</string>







2013年4月18日 星期四

手機、平板電腦座標系統

將裝置橫放我們會發現有些裝置橫向(左右方向)的座標軸是x軸,有些是y軸
通常手機橫放的左右方向是y軸

共通點是Y軸方向是照相機鏡頭的方向



Private Display display;

display= getWindowManager().getDefaultDisplay()


display.getRotation();


當getRotation()為1時候表示使用手機

若為零表示使用平板電腦

2013年4月15日 星期一

android自訂View觸控事件筆記

一、原始狀況:

自訂繼承View的類別。
override onTouchEvent與onDraw

執行程式會先跑onDraw
碰觸螢幕會觸發onTouchEvent事件
(onTouchEvent 回傳super.onTouchEvent(event)



二、
將onTouchEvent的retrun super.onTouchEvent(event);
改為return true

執行程式碰觸View會一直觸發onTouchEvent事件
onTouchEvent事件分成三種動作(action)
可以藉由event.getAction();
得知目前使用者的onTouchEvent事件的action
分別為Action_Down、Action_Move、Action_Up
第一個例子onTouchEvent只會偵測到Action_Down
如果onTouchEvent改為return true
則可以偵測到Action_Down、Action_Move、Action_UP


三、加上GuestureDetector手勢偵查物件

宣告GuestureDetector物件並宣告SimpleOnGestureListener子類別,
override onDown 與Scroll 與Fling


private GestureDetector gd;
gd = new GestureDetector(context,MyListener());
//context是指activity要看view放在哪個activity,MyListener是傾聽者



private class MyListener extends SimpleOnGestureListener{
//宣告傾聽者 繼承自SimpleOnGestureListener

@Override
public boolean onDown(MotionEvent e) {
Log.i("Sing","onDown");

return super.onDown(e);
}

@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
Log.i("Sing","onScroll");
return super.onScroll(e1, e2, distanceX, distanceY);
}
}

1.
將onTouchEvent傳回值
改成gd.onTouchEvent(event)
//GestureDetector偵測有沒有發生onTouchEvent事件

執行程式會執行onDraw
按下螢幕會執行onTouchEvent
緊接著執行onDown。
因為GestureDetector偵測到onTouchEvent觸發onDown

2.承上
將onDown的return super.onDown(e);改為return true;
再執行程式
按下螢幕(不滑動)會發現
程式會先onTouchEvent之後onDwon接著就是連續執行onToucheEvent
而沒有執行onDown

由此可知道
當onTouchEvent 的 action 為down時候
會觸發Listener 的ondown


如果手指在螢幕上滑動一段距離
會交錯著先觸發onTouchEvent之後觸發onScroll

onDraw → onTouchEvent→ onDown → onTouchEvent→ onScroll →onTouchEvent→ onScroll


3.若沒加上postInvalidate()是不會再呼叫onDraw的

2013年4月14日 星期日

SensorManager

SensorManager是手機平板電腦特有的功能


本文先說明加速度感測器:
加速度感測器顯示手機受到加速度的方向
分成X Y Z三個方向

將手機平躺著那麼X 與Y加速度趨近於零
Z加速度為9.8m/s^2

如果將手機直立Y加速度為9.8m/s^2
將手機橫立X軸加速度為9..8m/s^2

只是單純旋轉手機,不出力那麼所受的合力為9.8m/s^2




public class MainActivity extends Activity {
private SensorManager mgr;
private TextView info;
private Sensor sensor;
private MyListener listener;
private int nowz = -500, nowx, initx;
private float maxx;
private boolean isPull;
private int iscast = 0;

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

info = (TextView) findViewById(R.id.info);

mgr = (SensorManager) getSystemService(SENSOR_SERVICE);
List<Sensor> list = mgr.getSensorList(Sensor.TYPE_ALL);

// for (Sensor sensor: list){
// info.append(sensor.getName() + ":" +
// sensor.getMaximumRange() + ":" +
// sensor.getResolution() + "\n"
// );
// }

//
sensor = mgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
//
// sensor = mgr.getDefaultSensor(Sensor.TYPE_ORIENTATION);

// sensor = mgr.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
// sensor = mgr.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
// sensor = mgr.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
listener = new MyListener();

}

@Override
protected void onResume() {
super.onResume();
mgr.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_NORMAL);
}

@Override
protected void onPause() {
super.onPause();
mgr.unregisterListener(listener);
}

private class MyListener implements SensorEventListener {

@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO Auto-generated method stub

}

@Override
public void onSensorChanged(SensorEvent event) {
float[] data = event.values;
float x = data[0] * 10, z = data[2] * 100;
// info.setText(data[0]+"\n"+data[2]);

if (z < nowz) {


if (iscast == 0) {
Log.i("log", "以拉釣竿");
nowz = 990;
iscast = 1;
}

}

if (z > nowz) {
if (iscast == 1) {
if (x < 0) {

maxx = (Math.abs(1-x/10))*5;
Log.i("log", "z"+z + " x"+x);
info.setText("" + maxx);
iscast = 3;
}
}
maxx = 0;
initx = -20;
nowz = -500;

}

// if (z < nowz) {
// nowz = (int) z * 10;
// initx = (int) x;
// isPull = true;
// } else {
// isPull = false;
// if (x < initx) {
// int dx = (int) (Math.abs(x - initx));
// if (dx > maxx) {
// maxx = dx;
// info.setText("Power:" + maxx);
// }
// } else {
// nowz = 999;
// maxx = 0;
// }
// }
}}}




SensorManager.getOrientation();

Dialog(AlertDialog,ProgressDialog,自訂dialog,自訂Toast,View)

本文是AlertDialgo的實作
基本上當作SOP處理
記得實作AlertDialog基本架構


AlertDialog是一個類別,裡面包住Builder類別
AlertDialog是Dialog子類別。

本例子
全都用Button來觸動showDialog method


首先在Activity宣告AlertDialog的物件。
private AlertDialog alert;


一、true與false對話框:

private void showDialog1(){

AlertDialog.Builder builder = new AlertDialog.Builder(this);
//宣告AlertDialog裡的Builder物件
////this 是指Activity也就是對話框顯示所在的Activity,this為context型態

builder.setTitle("這是對話框標題");
builder.setMessage("這是對話框內容。是否刪除資料?");
builder.setCancelable(false);
//設定鎖住返回鍵,false表示禁止按下返回鍵,只能按"確定"與"取消"

//alertDidalog的"確認"按鈕,傳遞參數,按鈕的內容以及註冊傾聽者。實作傾聽者匿名內部類別。
builder.setPositiveButton("是", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(MainActivity.this,"資料已刪除",Toast.LENGTH_SHORT).show();
//由於Toast包在內部類別裡面所以this前面要加上MainActivity
//記得要加上show()
}
});

//alertDidalog的"取消"按鈕,其餘同上
builder.setNegativeButton("否", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(MainActivity.this, "取消刪除", Toast.LENGTH_SHORT).show();
}
});

//最後務必記得要create() 跟show()
alert=builder.create();//create傳回Builder型態
alert.show();//.show() 傳回Dialog型態

}



二、列表式(list)對話框:按下button顯示多個項目,點其中一個項目,告知使用者所選的項目。
核心method :setItems(CharSequence[],DialogInterface.OnClickListener);

String items={"香蕉","芭樂","手榴彈","書"};


private void showDialog2(){
AlertDialog.Builder builder = new AlertDialog.Builder(this);

builder.setTitle("選擇一個喜歡的項目吧");
builder.setCancelable(true);

builder.setItems(items,new DialogInterface.OnClickListener() {
//傳入字串陣列items
@Override
public void onClick(DialogInterface dialog, int which) {
//which表示使用者所點選的項目
Toast.makeText(MainActivity.this, "你選擇了"+items[which], Toast.LENGTH_SHORT).show();
}
});
alert=builder.create();
alert.show();

}



三、自訂對話框:利用xml自訂對話框,與Dialog物件結合。Dialog是AlertDialog父類別。


private Dialog dialog;


private void showDialog3(){
dialog = new Dialog(this);
//對話框物件,this是context物件,表示對話框所在activity
dialog.setContentView(R.layout.newreg);//配接對話框
dialog.setTitle("註冊新帳號");

Button ok , cancel;
// ok=(Button)findViewById(R.id.ok);
//必須指定是哪個Activity、Dialog在findViewById,否則會出現NullPointerException,找不到對應物件。
ok = (Button)dialog.findViewById(R.id.ok);
//使用dialog下的findviewbyid
cancel = (Button)dialog.findViewById(R.id.cancel);
username = (EditText)dialog.findViewById(R.id.username);
passwd = (EditText)dialog.findViewById(R.id.passwd);

ok.setOnClickListener(new OnClickListener(){

@Override
public void onClick(View v) {
String uname = username.getText().toString();
String password=passwd.getText().toString();

Toast.makeText(MainActivity.this, uname+":"+password, Toast.LENGTH_SHORT).show();
dialog.dismiss();//解散(關閉)對話框
}});

cancel.setOnClickListener(new OnClickListener(){

@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "cancel", Toast.LENGTH_SHORT)
.show();
dialog.dismiss();
}});
dialog.show();//別忘了要show()
}




四、ProgressDialog:
實作下載檔案時常會出現的對話框
宣告ProgressDialog物件
利用執行緒計算count、Handler處理長條bar的運作

private ProgressDialog progress;
private MyHandler handler = new MyHandler();

//private Handler handler;
//打錯,造成NullPointerException
//記得要new MyHandler();否則也會造成NullPointerException





private void showDialog4(){

progress = new ProgressDialog(this);
progress.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
//progress.setProgressStyle(ProgressDialog.STYLE_SPINNER);
//Spinner表示畫圓圈,Horizontal表示長條bar
progress.setCancelable(false);
progress.setMessage("Loading.....");
progress.setMax(123);//長條bar最大長度


new MyThread().start();

progress.show();

}

private class MyThread extends Thread{
@Override
public void run() {
for(int i=0;i<=123;i++){
//改成i+=2  長條bar只會跑到122就停止

handler.sendEmptyMessage(i);
//忘了打這行造成,progressDialog只開啟視窗卻沒有跑

//progress.setProgress(i);
//其實可以直接在thread處理也有同樣效果,但忘了為何在Handler處理會比較好

try {
Thread.sleep(60);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}


}

progress.dismiss();
}
}


private class MyHandler extends Handler{

@Override
public void handleMessage(Message msg)
{
progress.setProgress(msg.what);//傳入參數設定長條bar的值
if(msg.what==progress.getMax()){
progress.dismiss();

}
}

}


五、自訂View並將View鑲嵌進Toast,變成自訂Toast

利用context提供的method getLayoutInflater建立自訂view
LayoutIflater inflater= getLayoutInflater()



private void showMyToast(int status , String info){

LayoutInflater inflater = getLayoutInflater();
//Activity的method
//將自己設計的layout轉成View

View layout = inflater.inflate(R.layout.mytoast, (ViewGroup)findViewById(R.id.root));
//R.id.root是xml中layout的id
//將xml檔mytoast裡面的ViewGroup(Layout及其元件)轉成View
//setContentView(layoutID)會直接將layout貼上畫面


ImageView img = (ImageView)layout.findViewById(R.id.img);
TextView msg= (TextView)layout.findViewById(R.id.msg);

//注意Img跟msg是屬於變數layout裡面的
//所以是layout之中findview,不是MainActivity findview


switch(status){

case 1:

img.setImageResource(R.drawable.warning);
break;

case 2:
img.setImageResource(R.drawable.warning);
break;

default:
img.setImageResource(R.drawable.warning);
break;


}

msg.setText(info);
//TextView mag秀出文字

Toast toast = new Toast(this);

toast.setView(layout);//將自訂view放入toast
toast.setGravity(Gravity.CENTER_VERTICAL, 0, 100);
//(,xoffset,yoffset)
//設置toast的位置

toast.setDuration(Toast.LENGTH_SHORT);
toast.show();

}


六、另一種自訂View

另一種自訂View是將java鑲嵌到layout檔案
在java檔案中寫class繼承view
在layout寫
<com.example.signature.MainPanel  

 />

2013年4月11日 星期四

android 資料儲存data storage

SharedPreference:


儲存基本型態的資料
如果應用程式使用的資料量不大
那麼可以考慮用SharedPreference

應用:
遊戲的紀錄
聲音開與關
等程式相關狀態


用SharedPreference創立的資料只限於該應用程式使用
當然可以在getSharedPreferences設定公開檔案給其他應用程式使用
SharedPreferences sp = getSharedPreferences("YYP.dat",MODE_PRIVATE);

以XML格式將資料存放到/data/data/com.example.fishingmapmaker/shared_prefs/之下

如果在實體手機操作那麼
/data必須是root權限才可以讀取





public class MainActivity extends Activity {

private Button click1, click2, click3;
private TextView tv, tv2, tv3;
private EditText iodata1, iodata3;

private SharedPreferences sp;
//宣告sharedpreference物件
private SharedPreferences.Editor editor;
//宣告sharedpreference編輯器

private String username;
private int stage;
private boolean sound;


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

sp = getSharedPreferences("YYPdata", MODE_PRIVATE);
         //getSharedPreferences是context裡面的method
                //建立sharedpreference儲存資料的檔案   並宣告存取權限為只限於本程式閱讀

editor= sp.edit();
//調出sharedpreference編輯器

click1 = (Button)findViewById(R.id.click1);
tv = (TextView)findViewById(R.id.tv);

click2 = (Button)findViewById(R.id.click2);
tv2 = (TextView)findViewById(R.id.tv2);
iodata1 = (EditText)findViewById(R.id.iodata1);

click3 = (Button)findViewById(R.id.click3);
tv3 = (TextView)findViewById(R.id.tv3);
iodata3 = (EditText)findViewById(R.id.iodata3);



click1.setOnClickListener(new OnClickListener(){

@Override
public void onClick(View arg0) {
savePreference();
}
});
readPreference();
click2.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
saveInnerData();
}
});

readInnerData();

click3.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
saveSDData();
}
});

readSDData();


}


private void savePreference(){
editor.putBoolean("sound", false);//每個資料型態都有對應的method
editor.putString("username", "Brad");//存進檔案
editor.putInt("stage", 118);

editor.commit();//務必要寫這行,才會將資料存進檔案
readPreference();

}


private void readPreference(){
username = sp.getString("username", "nobody");//讀取sharedpreference的儲存內容
stage=sp.getInt("stage", 0);
sound=sp.getBoolean("sound", true);

tv.setText("UserName: " +  username + "\n" +
"Sound: " + (sound?"On":"Off") + "\n" +
"Stage: " + stage);

}

private void saveInnerData(){
String data = iodata1.getText().toString();//EditView的輸入文字記得要在末端加上toString(),因為getText()是View型態

try{
FileOutputStream fout

FileOutputStream fout = openFileOutput("YYP.dat",MODE_PRIVATE);
FileOutputStream fout= new FileOutputStream("YYP.dat");//錯誤寫法 filenotfound
//在純翠java 會預設純到專案跟目錄 

fout.write(data.getBytes());//因為檔案皆為字串所以要將字串型別轉成byte型式

fout.flush();
fout.close();
Toast.makeText(this, "save OK", Toast.LENGTH_SHORT).show();
readInnerData();

}catch(IOException e){

Toast.makeText(this,"IO Exception", Toast.LENGTH_SHORT).show();
}

}
private void readInnerData(){

try{
// FileInputStream fin = new FileInputStream("YYP.dat");//出現fileNotFoundException
FileInputStream fin = openFileInput("YYP.dat");
BufferedReader reader = new BufferedReader(new InputStreamReader(fin));
String line;
while((line=reader.readLine())!=null){
tv.append(line+"\n");


}

fin.close();
}catch(IOException e){

Toast.makeText(this,"save ok",Toast.LENGTH_SHORT ).show();
}



}


//讀取寫入SDcard

private void saveSDData(){
String data = iodata3.getText().toString();
try{
FileWriter writer = new FileWriter("/mnt/sdcard/brad.dat");

writer.write(data);
writer.flush();
writer.close();

}catch(IOException e){

e.printStackTrace();

}

}


private void readSDData(){
try{
BufferedReader reader = new BufferedReader(new FileReader("/mnt/sdcard/brad.dat"));
String line;
while((line=reader.readLine())!=null){
tv3.append(line+"\n");

}
reader.close();
}catch(Exception e){


}

}


}





InnerStorage:

inner storage儲存在/data/data/com.example/files/之下


SDcardStorage
將資料儲存在SDcard

2013年4月10日 星期三

android Tiemrtask

除了extends Thread 、implement Runnable之外
也可以用timertask物件啟動執行緒

並且可以採用Button按下去會執行多個執行緒


在一般java swing可以直接在thread 的run()中直接操控UI
但是在android不行
必須裡用handler傳遞message操控UI


private Timer timer;
pirvate MyHandler handler;
timer.schedule(timertask,什麼時候執行,執行時間長度);





private void Click3(){
timer = new Timer();
// timer.schedule(task, when, period)
timer.schedule(new MyTask(),0,1000);
//第零秒後執行my task物件,持續執行一千秒,每秒執行一次
}

private class MyTask extends TimerTask{
int i;
@Override
public void run() {
Log.i("YYP","i = "+i++);
// tv.setText("i = "+i++);
//在android裡面thread不可以直接操控UI否則會出現
//04-10 23:31:45.967: W/dalvikvm(399): threadid=7: thread exiting with uncaught exception (group=0x4001d800) 04-10 23:31:46.009: E/AndroidRuntime(399): FATAL EXCEPTION: Timer-0 //04-10 23:31:46.009: E/AndroidRuntime(399): android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. 

//要操控UI必須用handler物件傳遞Message
handler.sendEmptyMessage(i++);
//handler.sendEmtyMessage(what);
}
}

//handler必須宣告,UI所在的activity之中。
private class MyHandler extends Handler{

//reciev Message
@Override
public void handleMessage(Message msg) {
tv.setText("i="+msg.what);
}


}

//activity結束時,取消timer物件執行緒
//否則activity結束後timer物件仍會持續執行
public void finish(){
timer.cancel();
timer=null;
super.finish();


}

2013年3月23日 星期六

ListView

Adapter:
連接後端資料與前端View的配接器




SimpleAdapter:可以自由擴充listItem格式
ArrayAdapter:listitem只能顯示一行



ListView讓手機頁面秀出垂直條列式項目。

約有三種方式:
一、

1.
首先在xml貼上<ListView>標籤,
<ListView
    android:id="@+id/lv"
        android:layout_width="fill_parent"
      android:layout_height="fill_parent"
  ></ListView>
預覽頁面會是下面這樣:

2.定義Item長相:
Item也是layout一種,一般新增一個xml來做Item版面配置。
假如我們希望Item長相如下:
有Icon,有title以及說明內容。


那麼新增一個item.xml,用來描繪Item的長相
上圖用兩個LinearLayout以及一個ImageView以及兩個TextView實作。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"   
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    >
    <ImageView
        android:id="@+id/item_img"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_weight="1"
  android:src="@drawable/TVClose"  //圖片所屬資料夾以及檔名
        />
    <LinearLayout
     android:layout_width="fill_parent"
     android:layout_height="wrap_content"
     android:layout_weight="4"
     android:orientation="vertical"
        >
        <TextView
            android:id="@+id/item_title"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="TVclose"
      android:textSize="24dip"
      android:textStyle="bold"
            />
        <TextView
            android:id="@+id/item_content"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="電視收播圖像"
      android:textSize="12dip"
            />
        </LinearLayout>
</LinearLayout>


在drawable放圖片檔案:
另外要在res資料夾裏頭,新增資料夾drawable,裡面放想要呈現在listview的圖片,放在res的檔案android會自動配置參考地址(int 型態)。在java要取用時可以打R.drawable.圖片檔名。

在.java宣告
private ListView lv;
lv=(ListView)findViewbyId(R.id.lv)

常見作法用ArrayList以及HashMap配合和泛型實作ListView
ArrayList表示ListView容器本身,HashMap表示一個Item。
ArrayList可以裝多個HashMap。
HashMap可以裝Item的資訊、圖檔

 ArrayList<HashMap<String,Object>> list = new ArrayList<HashMap<String,Object>>();
//宣告ArrayList,這個ArrayList可以裝下多個HashMap

//以HashMap實作ListView的Item
//String:是key、index,也可以屬性來比喻他,方便開發者更好寫程式。Object是資料,可以用值來比喻。
HashMap<String,Object> data1 = new HashMap<String,Object>();
data1.put("item_img",R.drawable.freezer );//存放圖檔
data1.put("item_title", "Freezer");//Item圖檔在listview的名字
data1.put("item_content", "佛利沙,過去在宇宙為人人聞之色變的惡魔");
//Item說明
list.add(data1);//將Item塞入listview(將HashMap塞入Arraylist物件)

HashMap<String,Object> data4 = new HashMap<String,Object>();
data4.put("item_img", R.drawable.tvclose);
data4.put("item_title", "TVclose");
data4.put("item_content", "電視收播,以前電視台休息收播會出現影像");
list.add(data4);


//上面已經宣告listview設定,尚未告訴android
//main_activity.java的item與item.xml怎樣連接
//也沒告訴.java的listview與xml的listview連接
//底下實作之

String[] from = {"item_img","item_title","item_content"};
//HashMap的key,為了底下SimpleAdapter使用

int[] to = {R.id.item_img,R.id.item_title,R.id.item_content};
//Item.xml的標籤id
//注意int陣列中元素所代表的物件要正確對應到String的每個字串所代表的物件,例如R.id.item_title在item.xml代表TextView物件,而String[] from的"item_title"在activity.java裡面代表HashMap的字串,所以順序擺放要
正確,否則無法正確顯示listview

如果將順序改成如下,那麼顯示的listView如圖:
//int[] to = {R.id.item_content,R.id.item_title,R.id.item_img};


SimpleAdapter adapter = new SimpleAdapter(this, list,R.layout.item, from, to);
//資料與lItem的配接器,參數缺一不可,android才會幫我們接上對應的
//SimpleAdapter(哪個.java的item(context),哪個xml的item,HashMap的key,Item.xml版面配置單一選項編號)

lv.setAdapter(adapter);
//設置listview的配接器,item要秀在哪個listview




二、

1.
xml放一個<ListView />


    <ListView
        android:id="@android:id/list"
       @android:id/list意思是調用android系統內建的id(資源)
//而且list只能給ListView用,如果用android:id其他的項目則會發生錯誤
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:drawSelectorOnTop="false"
         />


2.
extends ListActivity

3.
制定listview內容
String[] items = {"Aibo","Brad","Clock","David","Enjoy"};

setListAdapter(new ArrayAdapter(this,android.R.layout.XXXX,items));
//ArrayAdapter(context,listview格式,listview(裡面的textView)內容)
//setListAdapter


String[] items ={"Sherry", "Dorara", "Black", "Int", "Five"};
Button button1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);


//list項目
//setListAdapter(new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,items));
//ArrayAdapter(context , 格式TextViewResourceId(android有內建xml檔案?各式各樣格式,  ,項目內容String[] items);
//普通格式


//setListAdapter(new ArrayAdapter<String>(this,android.R.layout.simple_list_item_single_choice,items));
//radiocheck模式
//setListAdapter(new ArrayAdapter<String>(this,android.R.layout.simple_list_item_checked,items));
//打勾圖示
setListAdapter(new ArrayAdapter<String>(this,android.R.layout.simple_list_item_activated_1,items));
//項目反白

//getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
//getListView()從Activity取得ListView物件
//複選模式
//如果不打上這行,預設為Choice_mode_none
//選擇項目
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
//單選模式


button1=(Button)findViewById(R.id.button1);


}

//callback method
public void onListItemClick(ListView parent,View v, int position, long id){
       button1.setText(items[position]);
   
   }





三、整個activity視為ListView


1.準備一個XML描述ListView單一項目

<!-- <Button
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   />   -->
//若加上這個,每一項目都有button  
   //setOnItemClickListener失效

    <TextView
        android:id="@+id/item_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="6dp"
        android:layout_marginBottom="6dp"
        android:textSize="24sp"
        android:textStyle="bold|italic"
/>

2.新增一個Activity extends ListActivity

3.

宣告陣列存放ListView的項目內容

//例如Listview每一項目有TextView則宣告字串陣列
String[] item = {"one","two","three"};


//如果ListView項目有圖片就宣告 int陣列
int[] photos={R.drawable.photo1,R..drawable.photo2,R.drawable.photo3};


4.
宣告ArrayList為ListView容器
HashMap代表ListView的每一筆紀錄
一筆紀錄有多個屬性(欄位)
(一筆紀錄有textview以及image)


ArrayList<HashMap<String,String>> data = new ArrayList();




5.

String[] from = {"item_title"};
//item的key名稱,
//目的讓adapter做配接用


int[] to ={R.id.item_title};
//對應到描述listview單一項目的Xml檔案,裡面的元件(wedgit)的ID。
 //對應textview的id
//如果xml有TextView以及ImageView元件,元件id分別是R.id.item_title,R.id.pictur
//則宣告int[] to = {R.id.item_title,R.id.picture}



6.將內容塞入HashMap

for(String item:items){

HashMap<String,String> dd = new HashMap();
dd.put(from[0], item);
//將每一筆紀錄第from[0]欄位存放資料內容
data.add(dd);
}



7.將資料與ListView配接


adapter = new SimpleAdapter(this,data,R.layout.activity_main,from,to);
//this 的 data 放在 R.layout.activity_main,hashmap的key(就是from)要與xml的欄位(即to)對應



8.View物件使用配接器

lv=getListView();
//將整個Activity視為listview
//原本lv= (ListView)findViewById(R.id.lv);
lv.setAdapter(adapter);






private ListView lv;
private SimpleAdapter adapter;
private String[] items = {
"LinearLayout", "RelativeLayout",
"TableLayout", "ListView",
"Gallery"
};

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


String[] from = {"item_title"};
//item的key
int[] to ={R.id.item_title};
//對應到listItem每項目裡面的元件。    對應textview的id



ArrayList<HashMap<String,String>> data = new ArrayList();

for(String item:items){
HashMap<String,String> dd = new HashMap();
dd.put(from[0], item);
data.add(dd);
}
adapter = new SimpleAdapter(this,data,R.layout.activity_main,from,to);

lv=getListView();
//將整個Activity視為listview
//原本lv= (ListView)findViewById(R.id.lv);


lv.setAdapter(adapter);

// lv.setOnClickListener(new OnClickListener(){
//
// @Override
// public void onClick(View v) {
// // TODO Auto-generated method stub
//
// }});

lv.setOnItemClickListener(new OnItemClickListener(){

@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int index,
long arg3) {

doJob(index);

}});



}


private void doJob(int index )
{ switch(index)
{
case 0:
Toast.makeText(this, "LinearLayout", Toast.LENGTH_SHORT).show();
break;
case 1:
Toast.makeText(this, "RelativeLayout", Toast.LENGTH_SHORT).show();
break;
}
}