●補充說明--推播機制

1.不分android或ios,推播是推給系統,是GOOGLE 推給系統,先把要推播的內容給APP的SERVER,APP的SERVER會告訴GOOGLE SERVER要推給誰,GOOGLER SERVER再推給系統,再把APP叫出來

2.推播一定要有server,我們這邊用firebase,原理和運作方式如下
=>當安裝好app(ex:line)的時候會註冊帳號,會跟google說要一個推播token,要註冊這隻手機需要一個推播token,google就會回傳token,app會收到token
=>把token加上user id 傳到line 的server去,line要用資料庫存下來
=>以後要發訊息的時候,就要用token+訊息+app金鑰(key=>在google developer console申請的)
=>看到key就會知道是哪個app,看到token就會知道是哪支手機,這樣google就會送訊息過來
**同一支手機,不同app,token會一樣,所以token是綁手機
 
3.用firebase console做發送就沒有token問題,送推播就是一次送所有的人

 

●程式說明:

1.tools=>firebase
FCM firebase cloud messaging
 
2.cloud messaging
FCM firebase cloud messaging
 
3.SET UP FIREBASE CLOUD MESSAGING=>CONNECT
FCM firebase cloud messaging
 
連結時會跳出選擇授權goolge帳號,再回android studio設定Firebase project name
 
FCM firebase cloud messaging
 
完成後右下角有出現這個訊息
FCM firebase cloud messaging
 
 
4.點Add FCM to your app. FCM(firebase cloud message)的library=>加入gradle並且sync
FCM firebase cloud messaging
 
5.app每次啟動時跟goolge要一個token,並且註冊到firebase
=>先new一個service:myfirebaseService
FCM firebase cloud messaging
 
6.繼承FirebaseMessagingService,下面IBinder可以刪除
public class MyFirebaseService extends FirebaseMessagingService{
public MyFirebaseService() {
}
}
 
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
}
 
7.加入onMessageReceived
public class MyFirebaseService extends FirebaseInstanceIdService {
public MyFirebaseService() {
}
 
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
    // ...
 
 
    // TODO(developer): Handle FCM messages here.
    // Not getting messages here? See why this may be: https://goo.gl/39bRNJ
    Log.d(TAG, "From: " + remoteMessage.getFrom());
 
 
    // Check if message contains a data payload.
    if (remoteMessage.getData().size() > 0) {
        Log.d(TAG, "Message data payload: " + remoteMessage.getData());
 
 
    }
 
 
    // Check if message contains a notification payload.
    if (remoteMessage.getNotification() != null) {
        Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody());
    }
 
 
    // Also if you intend on generating your own notifications as a result of a received FCM
    // message, here is where that should be initiated. See sendNotification method below.
}
}
 
8.manifest加intent filter(已可收推播)
<intent-filter>
    <action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
 
 
 
9.先離開app,到firebase找到該專案=>左側Cloud Messaging
FCM firebase cloud messaging
 
11.輸入訊息=>審查=>發布
FCM firebase cloud messaging
FCM firebase cloud messaging
 
12.在手機收到
FCM firebase cloud messaging
 
13.完成畫面
FCM firebase cloud messaging

 

●程式參考(GitHub):使用Google Firebase Cloud Messaging接收推播訊息 

文章標籤

muchone 發表在 痞客邦 留言(0) 人氣()

●畫面預覽:

Screenshot_20190711-111047.jpg

 

●程式說明:

admob註冊帳號=google帳號,台灣也有其他掛廣告的廠商(VPON威朋)
google admob
 
 
 
(一)直接使用admob activity
1.開啟android,直接開admob activity
google admob
 
2.改values/strings中的廣告id
google admob
 
 
3.到admob找廣告id=>在admob新增應用程式,建立廣告單元,會產生廣告id,貼回android
google admob
 
**注意不可以自己點廣告,在測試模擬的時候可以使用測試id,就會發送測試廣告不會發送真正的廣告
 
4.接著照導入說明做,下載admob sdk=>在google play service platform中有(在 android sdk manager中),再遵照sdk整合指南,修改xml和ad unit id
google admob
 
5.要再加上gradle,在文件get started處找gradle,要加兩項
 
google admob
google admob
 
 
(二)使用firbase
1.android firebase =>admob
google admob
 
2.連線到firebase,照著步驟做,加gradle
google admob
 
3.strings加(測試用id)
<string name="banner_ad_unit_id">ca-app-pub-ca-app-pub-3940256099942544/6300978111</string>
 
4.xml加
 
<com.google.android.gms.ads.AdView
android:id="@+id/adView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true"
ads:adSize="BANNER"
ads:adUnitId="@string/banner_ad_unit_id">
</com.google.android.gms.ads.AdView>
 
5.mainactivity改
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private AdView mAdView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
 
mAdView = (AdView) findViewById(R.id.adView);
AdRequest adRequest = new AdRequest.Builder().build();
mAdView.loadAd(adRequest);
}
}
**在模擬器是測試廣告,但實機會是真實廣告,會提醒妳要輸入測試序號(有範例程式碼),如果輸入測試序號就會發給妳測試廣告可以點

 

**有時候建立好廣告後,廣告並沒有正常顯示,看logcast可以看到一段訊息:Ads:Ad fail to load : 0,出現這段訊息代表你的程式沒有問題,是因為廣告單元剛建立google server還沒有立刻提供適合的廣告,要等一段時間(時間不定)就會正常顯示

google admob

 

●程式參考(GitHub):Google Admob--在自己的app中增加google廣告單元

文章標籤

muchone 發表在 痞客邦 留言(1) 人氣()

7/11

I start to look for a job this morning.

Hope I can get a job offer before I go to Japan.

Howerver, no one is responding to my job application.

 

7/12

I got the opportunities for interviews yesterday.

However, I am worried I can't pass the job knowledge test. 

I discussed the Universal Studio Japan travel tips with my son last night.

 

7/13

I went to the night market near our home to shop our clothes last night.

The night market is my favorite place to shop.

There are two women's clothing store in the night market with lots of affordable and fashionable clothes. 

 

7/14

I've got a runny nose and I was worried that I've got a cold.

After I taking medicine, I felt sleepy.

Howerver, I haven't finished our Japan itinerary.

 

7/15

I went to the clothing store in Xingnan Night Market again.

I wanted to exchange the pants for different color.

However, I bought a jacket and a chiffon top again and cost only 250 NTD. 

 

7/16

I found that the USJ pass with special entry on the day I planned to USJ was sold out last night.

I had no choice but to rearrange our travel itinerary.

After buying all the e-tickets we need, I found the email I wrote was wrong and I was really nervous.

 

7/17

I scheduled two job interviews today.

I got the job offer after the second interview.

I was happy but worried I might not excel at this job.

 

7/18

There are so many things to prepare for going abroad that I worry something might be forgotten.

However, I am excited and look forward to going to Japan with my son.

This is his first time to traveling abroad.

 

7/19

I feel a bit nervous and excited.

Not only because I'll  travel to Japan soon, but also because I decide to quit when I return to Taiwan.

I don't know if I can adapt the new job.

 

7/20

We'll go to Japan tomorrow and all of us are so excited.

I started to pack our suitcase this afternoon and my son felt very bored because I couldn't play with him.

When I am in Japan, I'll stop writing in my English diary.

 

 


*look for a job:找工作

*respond to one's job application:sb找工作沒有回應

*get an opportuniy for interview:取得面試機會

*the job knowledge test:專業技能測驗

*travel tips:旅遊攻略

*affordable:價格便宜/實惠的

*runny nose:一直流鼻涕

*Xingnan Night Market:興南夜市

*chiffon top:雪紡上衣

*exchange for different color:(買了衣服以後)換顏色

*have no choice but to ...:別無選擇,只能...

*e-ticket:電子票

*schedule a job interview:安排一個面試

*excel at the job/work:勝任這份工作

*not only because...but also because...:不只因為...還因為...

*adapt the new job:適應新工作

*pack one's suitcase:收拾行李

*write in a diary:寫日記

 

文章標籤

muchone 發表在 痞客邦 留言(0) 人氣()

●程式說明:

1.開啟一個新的專案,可以直接開google map activity,title可以自己設定
 
2.開啟會跳出這個頁面,要取得google maps api key
GoogleMap
 
3.用模擬器測試時,會跳出google service要更新的訊息,然後按按鈕更新,但不要在這邊做更新會卡住
要到sdk manager=>勾選show package details=>下載google apis intel x86 atom system image
=>更新完以後要砍掉舊的模擬器,因為舊的模擬器是用舊版的模擬器影像檔做出來的
=>要重新安裝一次模擬器
=>目前版本不需要刪除模擬器就可以直接run
=>如果是用手機就可以直接更新
GoogleMap
 
4.用模擬器測試,左下角出現google icon代表google service更新成功
GoogleMap
 
5.解決api key的問題
a.複製網址貼到瀏覽器
GoogleMap
 
b.取得api key
 
API KEY
 
要建立憑證才能取得API金鑰
API KEY
c.把金鑰貼到google_maps_api.xml檔中,位置如下:
 
d.用模擬器跑可以出現地圖
Google Map
 
**注意上傳到github不要有金鑰資料
 
6.修改程式,在mainactivity中有如下程式碼:
public void onMapReady(GoogleMap googleMap) {
    mMap = googleMap;
 
    // Add a marker in Sydney and move the camera
    LatLng sydney = new LatLng(-34151);
    mMap.addMarker(new MarkerOptions().position(sydney)
    .title("Marker in Sydney"));
    mMap.moveCamera(CameraUpdateFactory.newLatLng(sydney));
}
**說明:
1.onMapReady:地圖載完之後要做的事
2.addMarker:加mark
3.moveCamera:移動相機到指定位置
 
7.調整內容
public void onMapReady(GoogleMap googleMap) {
    mMap = googleMap;
    LatLng TaipieEgg = new LatLng(25.0513481121.5486728);
    LatLng Hospital= new LatLng(25.0477974121.5486488);
    mMap.addMarker(new MarkerOptions().position(TaipieEgg).title("台北小巨蛋"));
    mMap.addMarker(new MarkerOptions().position(Hospital).title("台安醫院"));
    mMap.moveCamera(CameraUpdateFactory
   .newLatLngZoom(TaipieEgg,18));
}
**說明:
1.再加一個LatLng物件,多一個addMarker,可以同時顯示很多個marker
2.newLatLngZoom,可以設定放大倍率
3.可以查google maps api手冊中的MarkerOptions,裡面有很多方法可以用

 

*注意,在使用內建的Google Maps Activity可能會遇到下面這個問題

(出現訊息:inconvertible types; cannot cast 'android.support.v4.app.fragment' to 'com.google.android.gms.maps.SupportMapFragment' )

=>要把SupportFragmentManger轉為SupportMapFragment時,出現紅線顯示無法轉換type,這是因為在google paly-services-maps:17.0.0中,SupportMapFragmentnow 改為extends androidx.fragment.app.Fragment,而非android.support.v4.app.Fragment

Google Map

=>解決辦法是把play-services-maps改為16.0.1或是把專案整合為androidX版本,這邊是直接改play service版本

Google Map

 

●程式參考(GitHub):使用Android Studio內建Google Maps Activity建立Google Map

文章標籤

muchone 發表在 痞客邦 留言(1) 人氣()

●程式說明:
1.gradle要加:compile 'com.google.android.gms:play-services:11.0.4'
設定權限:<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
 
2.
 
public class MainActivity extends AppCompatActivity implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
 
GoogleApiClient mGoogleApiClient;
Location mLastLocation;
private String[] mPermissions = new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION};
private static final int MY_PERMISSIONS_REQUEST_READ_CONTACTS = 100; //permission RequestCode
 
@Override
 
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
 
alertPermissions(this);
 
}
 
@Override
public void onConnected(@Nullable Bundle bundle) {
 
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(this,
android.Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
return;
}
}
 
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(
mGoogleApiClient);
if (mLastLocation != null)
{
// 取到手機地點後的程式碼
Toast.makeText(this,"GPS Location:" + mLastLocation.getLatitude() + "," + mLastLocation.getLongitude(),Toast.LENGTH_SHORT).show();
// Log.d("GPS", "Location:" + mLastLocation.getLatitude() + "," + mLastLocation.getLongitude());
}
}
 
@Override
public void onConnectionSuspended(int i) {
 
}
 
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
 
}
 
 
public synchronized void buildGoogleApiClient() {
mGoogleApiClient = new GoogleApiClient.Builder(getBaseContext())
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
mGoogleApiClient.connect();
 
}

//***************************權限相關***********************************
 
//判斷sdk版本是否會彈跳出權限對話框
public boolean isPoupUpPermissions() {
//Build.VERSION.SDK_INT 取得目前sdk版本
//android 6.0=23 因為23以上才會跳出權限對話框
return Build.VERSION.SDK_INT >= 23;
}
 
//判斷是否已經有權限
public boolean hasPermissions(Context context, String permission) {
if (!isPoupUpPermissions()) { //sdk 23以下當作已經有取得權限
return true;
}
 
//確認權限是否為PERMISSION_GRANTED
if (ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
return false;
}
buildGoogleApiClient();
return true;
}
 
//請求權限(已擁有權限-PERMISSION_GRANTED ,無權限-PERMISSION_DENIED)
private void alertPermissions(Activity context) {
final ArrayList<String> unsatisfiedPermissions = new ArrayList<>(); //用來儲存未取得的權限
for (String permission : mPermissions) {
if (!hasPermissions(context, permission)) {
//如果某個permission檢查後為PackageManager.PERMISSION_DENIED,就加到unsatisfiedPermissions arrayListy中
unsatisfiedPermissions.add(permission);
}
 
}
 
//將unsatisfiedPermissions 轉成 string array,並且對array中的每個permission做request
 
if (unsatisfiedPermissions.size() != 0) {//當所有權限皆允許時unsatisfiedPermissions.size()=0 ,此時不需要requestPermissions
 
ActivityCompat.requestPermissions(context, unsatisfiedPermissions.toArray(new String[unsatisfiedPermissions.size()]), MY_PERMISSIONS_REQUEST_READ_CONTACTS);
 
}
}
}
 
 
●程式參考(GitHub):Android手機定位GPS(二)開啟APP取得手機現在GPS定位
文章標籤

muchone 發表在 痞客邦 留言(0) 人氣()

●畫面預覽:
 
 
●程式說明:
1.設定權限
粗略定位權限:<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
精細定位權限:<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
 
2.
public class MainActivity extends AppCompatActivity implements LocationListener {
 
    LocationManager lm;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        lm = (LocationManager) getSystemService(LOCATION_SERVICE);
    }
 
    public void click1(View view) {
 
//加了權限會跳出這段程式,這邊沒有寫可以強制開啟模擬器的權限設定        
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            
            return;
        }
        //四個參數:要用的定位來源、最短時間多久一次、最短距離多久一次、只要訂為一改變超過最短時間和最短距離就會呼叫listener
        lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
    }
 
    @Override //位置改變時
    public void onLocationChanged(Location location) {
        Log.d("GPS", "Location Change:" + location.getLongitude() + "," + location.getLatitude());
        Toast.makeText(this,"Location Change:" + location.getLongitude() + "," + location.getLatitude(),Toast.LENGTH_SHORT).show();
    }
 
    @Override
    public void onStatusChanged(String s, int i, Bundle bundle) {
 
    }
 
 
 
    @Override  //關閉定位
    public void onProviderEnabled(String s) {
 
    }
 
    @Override //開啟定位
    public void onProviderDisabled(String s) {
 
    }
 
}
**說明:
這個做在室外沒有問題,因為用gps定位多多少少會移動,但是在市內是用網路定位,沒有動就不會觸發此段程式
 
3.模擬器無法測試移動,可以點下列功能,send點一下就會改變位置,程式就可以抓到位置寫入log
get GPS location
 
文章標籤

muchone 發表在 痞客邦 留言(0) 人氣()

●補充說明--sqlite
 
(一)使用sqllite(輕量化資料庫,有基本的新增修改刪除查詢功能)
=>library占空間很小,跟sql不一樣,所以iphone和android都有內建sqllite
=>當資料量大的時候就使用sqllite,資料量小就用json+文字檔
=>資料庫中可以存很多資料表
=>資料庫兩個概念
a.資料是一筆一筆的,不能像excel一樣跨行,ex:如果一個人同時有兩筆電話資料,不能做成兩行,而是要再設計的時候新增一個電話2的欄位
b.每一筆資料都是由很多欄位組成的,所以設計資料表要先設計欄位
 
(二)把資料庫放到android中有兩個方法
1.先用sqlite manager設計好資料庫,在放到專案中再把資料庫從專案複製到內部儲存空間
=>優點:簡單方便
=>缺點:當app升級時,如果修改欄位放到內部儲存空間,不能蓋過使用者原有資料,所以當有調整資料庫欄位時,就要用程式碼新增修改欄位,但如果使用者沒有跟著版本進度同步更新,就會有問題,變成又得在另外寫程式來處理這種狀況,否則使用者資料會損毀
2.在android程式執行時才建資料庫
 
(三)要把sqllite的檔案放到android中,再把它包在app專案中放到手機裡
=>sqllite是binary檔(打開看起來像亂碼),有原始資料,所以不希望檔案被改變
=>在android中有兩個地方可以存放又不會被改變:
1.res/assets資料夾(自己建)
2.res/raw資料夾(自己建)
=>但程式執行不能直接用,要再寫程式copy至手機的儲存空間才能用

 

●程式說明 --兩個copy資料的方法,和一個讀取sqlLite的內容,最後讓使用者輸入的資料可以加入資料庫

public class MainActivity extends AppCompatActivity {
EditText name,tel,address,et,updatename;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
name=(EditText)findViewById(R.id.name);
tel=(EditText)findViewById(R.id.tel);
address=(EditText)findViewById(R.id.address);
et=(EditText)findViewById(R.id.et);
updatename=(EditText)findViewById(R.id.updateName);
}
//copy sqllite檔案到手機方法一
//將database檔案放在raw下
public void copy(View view) {
//要取得專案中的resources,就要用getResources(),回傳Resources物件
//要抓raw中的資料,要用openRawResource,回傳InputStream物件,再用一個InputStream變數接住
InputStream is=getResources().openRawResource(R.raw.student);
try {
//new一個FileOutputStream物件(FileOutputStream是OutputStream的子類別)
//輸出串流有很多種,網路資料串流,文字資料串流...
OutputStream os=new FileOutputStream(getFilesDir()+ File.separator+"student.sqlite");
//從inputStream讀,從OutputStream寫出去
int i=0;
//每讀一個byte進來就寫一個byte,當i=-1時讀不到就結束
while(i!=-1){
i=is.read();
os.write(i);
}
is.close();
os.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
 
} catch (IOException e) {
e.printStackTrace();
}
}
 
 
//copy sqlite檔案到手機方法二
//用java nio2的path(java7以後才有),用path或Files的method來做
public void copy2(View view) {
//因為path和Files要用api26以上測試,所以不能用android device monitor看檔案是否建立
//要用File+log來看
File f = new File(getFilesDir() + File.separator + "student2.sqlite");
//boolean exists ()
//Tests whether the file or directory denoted by this abstract pathname exists.
// return:true if and only if the file or directory denoted by this abstract pathname exists; false otherwise
//先偵測檔案是否存在,不存在才覆蓋檔案過去
if(!f.exists()){
InputStream is = getResources().openRawResource(R.raw.student);
//uri抓file的格式是file:///tmp/android.txt,而getFilesDir().getAbsolutePath()取出來前面會有一個斜線,所以file:後面只要兩條斜線
//create():URI create (String str)
//Creates a URI by parsing the given string.
URI uri = URI.create("file://" + getFilesDir().getAbsolutePath() + File.separator + "student2.sqlite");
//Path get (URI uri)
//Converts the given URI to a Path object.
Path p = Paths.get(uri);
try {
//long copy (InputStream in, Path target,CopyOption... options)
//Copies all bytes from an input stream to a file.
//CopyOption: options specifying how the copy should be done
//三個參數是inputStream,paht,常數
//StandardCopyOption:Defines the standard copy options.
//REPLACE_EXISTING:Replace an existing file if it exists.
Files.copy(is, p, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
e.printStackTrace();
}
}
Log.d("FILE", String.valueOf(f.exists()));
}
 
 
//讀取sqlLite檔
public void clickRead(View view) {
String path = getFilesDir().getAbsolutePath() + File.separator + "student2.sqlite"; //這邊是讀取student2.sqlite
//宣告db是在抓資料庫student2的資料
SQLiteDatabase db = SQLiteDatabase.openDatabase(path, null, 0);
//Cusor:This interface provides random read-write access to the result set returned by a database query.
//執行sql時,可以直接用execSQL,下SQL指令,或是用query,sqlLite內建的方法
//Query the given URL, returning a Cursor over the result set.有七個參數,後面五個可以用null,前兩個分別為table name,用字串陣列存的欄位名稱
//注意欄位名稱不可以和sql指令重複ex:add不可用
//宣告c是在抓資料庫裡一筆一筆的資料,cursor是代表指向第幾筆資料
Cursor c = db.query("phone", new String[] {"id", "name", "tel", "addr"}, null, null, null, null, null);
//因為cursor沒有設定的話會指向第一筆資料的上面空白出,所以設定移動到第一筆資料
c.moveToFirst();
//Log.d("COUNT", "NUMBER"+c.getCount());
StringBuilder sb=new StringBuilder();
do{
sb.append(String.valueOf(c.getInt(0)));
sb.append(":");
//Log.d("DB", String.valueOf(c.getInt(0)));//用log print id
for(int i=1;i<4;i++){
sb.append(c.getString(i)+",");
//Log.d("DB", c.getString(i)); //getString(i)取得第i筆的欄位資料
}
sb.append("\n");
et.setText(sb.toString());
//Log.d("DB", c.getString(1));
//moveToNext():Move the cursor to the next row.
}while (c.moveToNext());
 
}
 
 
//新增SQL lite資料
public void clicknew(View view) {
String path = getFilesDir().getAbsolutePath() + File.separator + "student2.sqlite";
SQLiteDatabase db = SQLiteDatabase.openDatabase(path, null, 0);
//ContentValues:This class is used to store a set of values that the ContentResolver can process.
//ContentValues也是map的概念,put()兩個參數,key和value,key就是欄位名稱
ContentValues cv = new ContentValues();
cv.put("name",name.getText().toString());
cv.put("tel",tel.getText().toString());
cv.put("addr",address.getText().toString());
//long insert (String table, String nullColumnHack, ContentValues values)=>因為插入的內容必須是一個ContentValues,所以前面要先把ContentValues建好
//Convenience method for inserting a row into the database.
db.insert("phone", null, cv);
db.close();
 
}
 
//刪除table欄位資料
public void delete(View view) {
String path = getFilesDir().getAbsolutePath() + File.separator + "student2.sqlite";
SQLiteDatabase db = SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READWRITE);
/*delete
int delete (String table,String whereClause, String[] whereArgs)
Convenience method for deleting rows in the database.
參數說明:
1.table name
2.要如何篩選資料,寫法:id=? 代表要把id都是?的資料都找出來,同時對應到下個參數為指定數值的資料
=>如果有兩個不同欄位寫法為id=? and tel=? ,同時陣列的參數就要變成new String[] {"2","2"},指定不同欄位的第幾筆資料
=>如果同一個欄位要刪除多筆資料寫法範例:id IN (?,?,?)
3.這個參數是陣列型態*/
//注意三四個參數絕對不能NULL,否則會把所有的資料全部刪除
db.delete("phone", "id IN (?,?)", new String[] {"2","5"}); //這邊刪除第二和第五筆資料
db.close();
}
 
 
//更新table欄位資料
public void update(View view) {
String path = getFilesDir().getAbsolutePath() + File.separator + "student2.sqlite";
SQLiteDatabase db = SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READWRITE);
ContentValues cv = new ContentValues();
cv.put("name", updatename.getText().toString());
//int update (String table, ContentValues values, String whereClause, String[] whereArgs)
//Convenience method for updating rows in the database.
//參數說明:1.資料表名稱 2.要更新的資料
//注意三四個參數絕對不能NULL,否則會把所有的資料全部更新
db.update("phone", cv, "id=?", new String[] {"1"}); //這邊更新第一筆資料的name欄位
db.close();
}
}
 
 
 
文章標籤

muchone 發表在 痞客邦 留言(0) 人氣()

7/1

Today is the first day of my son's swim class.

I thought he might not like the swim class. However, he enjoyed the class very much.

When I got home, my son sang the son "I miss you very bad" to me and that was so sweet.

 

7/2

My son started to learn to swim freestyle today.

After today's swim class, he felt tired and took a nap for more than 2 hours.

He hasn't taken a afternoon nap more than 2 hour for ages.

 

7/3

I had some difficulty maintaining the face detection app of our company.

However, I am ahead of schedule today.

I hope I can finish the project before I go to Japan.

 

7/4

My husband has been giving me the silent treatment recently.

I don't know how to make up with him and that always make me not sleep well.

I really hope I can figure out how to solve this problem.

 

7/5

Last night, my son woke up at 11:00 pm and then he couldn't fall asleep.

We thought he slept too long in the daytime.

Finally, I found the best way to help my son fall asleep is co-sleeping.

 

7/6

Again, I had difficulties in developing face recognition app of our company but our client was looking forward to receive the apk on Monday.

When I get off work, it was 1:30 am and I was too tired to eat dinner.

I could barely keep my eyes open when my son woke me up this morning.

 

7/7

My son and I  went to eat McDonalds' after his Taekwondo class.

After eating our lunch, we helped my mom pick her new smartphone.

We visited my mom and my son kept some hair of my mom's dog as souvenir.

 

7/8

We tested and took lots of videos of our face recognition system for our client this morning.

After that, we attended our weekly meeting until 5:00pm.

All day long, I couldn't program.

 

7/9

I have started plan our Japan itinerary recently.

I thought it was easy but it took me lots of time to search the route, map, food, price and so on.

I started to worry about not being able to finish our itinerary on time. 

 

7/10

I feel anxious and stressed as soon as my boss is in the office.

Therefore, I had stomach cramps all day long yesterday.

Everyday, I want to quit my job.

 

 

 

 

 

 

 

 


*freestyle:自由式

*for ages:很久,很長時間

*have some difficulty (in) Ving:在做..遇到困難

*be ahead of schedule:工作進度超前

*give sb. the silent treatment:和sb.冷戰

*sleep too lonag in the daytime:白天睡太多

*co-sleeping:親子同寢

*in the face of difficulty in Ving:在做...面臨困難

*can barely keep one's eyes open:幾乎睜不開眼睛

*wake sb up:把某人叫醒

*keep sth as souvenir:把sth留做紀念

*quit sb's job:離職

*have stomach cramps:胃痙攣 

文章標籤

muchone 發表在 痞客邦 留言(0) 人氣()

● 要將android studio的專案打包成sdk,分為兩種,.jar和.aar,差別在於

jar(Java Archive):是 Java 的一種檔案格式,包含class檔案與text檔案 ,不包含res中的檔案。

aar(Android Archive):除了上述的class檔案與text等檔案,還包含所有res中的檔案。

所以如果要打包的sdk只有使用class,那就直接打包jar檔案即可,但如果是有包含UI(res資源)的sdk,就要打包成aar檔案

 

●作法:

 1.開啟要打包的專案,將build.gradle(app)中的

apply plugin: 'com.android.application' 改為 apply plugin: 'com.android.library'

applicationId "your application id"註解掉

 

sdk1.png

 

2.上述步驟完成後sync,再rebuild project

sdk2.png

 

3.這時會產生兩個檔案,

jar檔:在app/build/intermediates/packaged-classes/debug下會有一個classes.jar

sdk3.png

aar檔:在app/build/outputs/aar下會有一個app-debug.aar檔

這樣就完成了SDK的打包囉!!

sdk4.png

 

*測試:另外開啟一個專案來測試剛剛打包的SDK是否可以正常的匯入新專案

匯入一樣分為兩種

(一)aar的匯入:

1.將打包好的aar檔放到新開專案中的app/libs下

sdk5.png

2.在build.gradle(project)中的repositories下增加

flatDir {

    dirs 'libs'

}

sdk6.png

3.到build.gradle(app) 新增 dependency:

implementation (name:'stroke', ext:'aar')

 

(二)jar檔,

1.將打包好的jar檔放到新開專案中的app/libs下

2.到build.gradle(app) 新增 dependency:

implementation fileTree(include: ['*.jar'], dir: 'libs')

sdk7.png

 

完成後測試StrokeTextView這個sdk是否可以正常使用,加入如下xml

(因為這邊打包的sdk有包含UI所以是採用AAR檔的方式)

sdk8.png

測試完成畫面!!成功!!

sdk9.png

文章標籤

muchone 發表在 痞客邦 留言(0) 人氣()

1.開啟新的專案,選擇Empty Activity

android 建立 library

 

2.設定專案名稱與位置

android 建立 library

3.專案開啟後,選擇File=>New Module 建立一個新的Module

android 建立 library

4.選擇Android Library=>Finish

android 建立 library

5.設定library name

android 建立 library

 

6.設定完,專案就會出現剛剛建立的library

android 建立 library

 

7.進到library的目錄下,新建一個空的Class

android 建立 library

 

8.在class中寫入要做為library的內容,本例中實際使用的程式內容比較多,這邊不列出

9.完成後將程式上傳至自己的github中

10.上傳後,在新建的repository中選Release=>Create a new release

android 建立 library

11.在上方欄位輸入版本號,下方為標題=>按下Publish

android 建立 library

 

12.發布完成後顯示如下畫面

android 建立 library

13.到JitPack.io 在"Look up"欄位輸入github repository 位址(名稱)再按下"Look up"=>下方會顯示剛才在github release 設定的版本號,按下"Get it"

android 建立 library

 

14.下方會出現如下畫面,把兩個gradle設定好就可以把這個library 用在別的專案

android 建立 library

 

**補充說明

1.另外開啟一個專案來測試剛剛建立的library可不可以gradle,在build.gradle加入剛剛的implementation,就可以了!

android 建立 library

 

2.在原本的library中也會有一個app資料夾,可以把要做sample或demo的程式放在這,使用本身的library,只要在app的build.gradle加入implementation project(':library名稱')

android 建立 library

 

*Github程式參考:StrokeTextView

*參考資料:How to create your own Android Library and publish it by Anuj Gupta

 

文章標籤

muchone 發表在 痞客邦 留言(1) 人氣()