tag:blogger.com,1999:blog-57535399504146367762024-02-19T05:36:35.557-08:00小鰻的Android學習筆記這個筆記是一個餐飲門外漢轉進Android程式開發的人-小鰻,他學Android跌跌撞撞的記錄。
筆記可能有錯,但大家都是在錯中學。
期待大家一起來維護這個筆記。Anonymoushttp://www.blogger.com/profile/07460782835337903344noreply@blogger.comBlogger190125tag:blogger.com,1999:blog-5753539950414636776.post-50775469294175247222018-03-15T20:18:00.002-07:002018-03-15T20:23:43.548-07:00使用Live Template(代碼模版)增加開發效率<span style="color: #999999;"><span style="font-size: x-small;">文章攢寫時間︰2018/03/16 11:08</span></span><br />
<br />
<span style="color: #6aa84f; font-size: x-large;"><b>一、前言</b></span><br />
Android Studio提供了Live Template(代碼模版)的功能,<br />
只要在編輯器中輸入簡單幾英文字,<br />
Android Studio就能立刻幫你輸出完整的程式碼。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<span style="color: #6aa84f; font-size: x-large;"><b>二、文章開始</b></span><br />
舉個例來說,<br />
只要在Android Studio編輯頁上打入logt,<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuXw-_93Mfd0RQepgk7DEfE8FBqAtzEjyJN8SiBOQ_P1Z1NJpjcon9bx7d3YH5kzpOP5qdCLv9Bf4W-j_PlP2UshGmcj6IXbztkPUu9nYuvZ_lqg0m_aI8r8sJgiR4mM7dhXeC2W1d0Q/s1600/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2018-03-16+11.11.43.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="510" data-original-width="1200" height="170" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuXw-_93Mfd0RQepgk7DEfE8FBqAtzEjyJN8SiBOQ_P1Z1NJpjcon9bx7d3YH5kzpOP5qdCLv9Bf4W-j_PlP2UshGmcj6IXbztkPUu9nYuvZ_lqg0m_aI8r8sJgiR4mM7dhXeC2W1d0Q/s400/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2018-03-16+11.11.43.png" width="400" /></a></div>
<br />
就能在該行看到完整的程式碼:<br />
public final static String TAG = MyClass.class.getSImpleName();<br />
<br />
設定的方式(以Mac為例):<br />
點選Android Studio工具列->[Android Studio]->[Preferences]->[Editor]->[Live Template]<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2Oc1tKMeTVHdGa9A7LOTLTJT-5ef6iHPGdaDffOZDckw3Xd0XZOprNneAtNiaHpyuAcb9uhXKvaxykS6r9aJXkm7tYgFh6DdHFW8I7Cahok94Xa1iKttimUWtQqiNge8qsyJQ1pEnSA/s1600/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2018-03-16+11.06.23.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1050" data-original-width="1600" height="262" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2Oc1tKMeTVHdGa9A7LOTLTJT-5ef6iHPGdaDffOZDckw3Xd0XZOprNneAtNiaHpyuAcb9uhXKvaxykS6r9aJXkm7tYgFh6DdHFW8I7Cahok94Xa1iKttimUWtQqiNge8qsyJQ1pEnSA/s400/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2018-03-16+11.06.23.png" width="400" /></a></div>
<br />
<br />
以下是我常用的Live Template(添加中):<br />
<table border="1" cellpadding="0" cellspacing="0" dir="ltr" style="border-collapse: collapse; border: 1px solid #ccc; font-family: arial,sans,sans-serif; font-size: 13px; table-layout: fixed;"><colgroup><col width="153"></col><col width="284"></col></colgroup><tbody>
<tr style="height: 21px;"><td data-sheets-value="[null,2,"\u529f\u80fd"]" style="background-color: #d9ead3; border: 1px solid rgb(0, 0, 0); font-size: 100%; font-weight: bold; padding: 2px 3px; vertical-align: bottom;">代碼</td><td data-sheets-value="[null,2,"\u5feb\u901f\u9375"]" style="background-color: #d9ead3; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(0, 0, 0); border-top-style: solid; border-top-width: 1px; font-size: 100%; font-weight: bold; padding: 2px 3px; vertical-align: bottom;">完整模版文字</td></tr>
<tr style="height: 21px;"><td data-sheets-value="[null,2,"\u627e\u5c80\u4f60\u8981\u7684\u51fd\u5f0f\u6216Class"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(0, 0, 0); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">logt</td><td data-sheets-value="[null,2,"\u9023\u63092\u6b21Shift"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">public static final String TAG = $className$.class.getSimpleName();</td></tr>
<tr style="height: 21px;"><td data-sheets-value="[null,2,"\u56de\u5230\u4e0a\u4e00\u500b\u6aa2\u8996"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(0, 0, 0); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;"><br /></td><td data-sheets-value="[null,2,"Command+["]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;"></td></tr>
</tbody></table>
<br />
<div>
你用了哪些Live Template?</div>
<div>
歡迎底下留言。</div>
Anonymoushttp://www.blogger.com/profile/07460782835337903344noreply@blogger.com0tag:blogger.com,1999:blog-5753539950414636776.post-66452024654774534012018-03-08T18:27:00.000-08:002018-03-08T18:27:13.787-08:00FCM個人化推播issue今天(2018/03/09)同事分享來的一個issue,因為公司需要發個人化推播,他發現收到推播的機臺彼此在錯亂。<br />
釐淸了一下,錯亂造成的原因是因為都是用同一組Android id。<br />
所以我們假設FCM底層在判斷機臺的方式,有使用Android ID當唯一值。<br />
<br />
註︰在GCM的年代,官方有明文顯示不建議商務需求使用個人化推播。Anonymoushttp://www.blogger.com/profile/07460782835337903344noreply@blogger.com0tag:blogger.com,1999:blog-5753539950414636776.post-17334990238756474672018-01-16T02:28:00.003-08:002018-01-16T02:29:39.515-08:00如何處理觸控事件<span style="color: #999999;"><span style="font-size: x-small;">文章攢寫時間︰2018/01/16 11:54</span></span><br />
<br />
<span style="color: #f6b26b;">本篇參考來源</span><br />
1. <a href="https://stackoverflow.com/questions/7449799/how-are-android-touch-events-delivered/46862320#46862320/">STACKOVERFLOW</a><br />
<br />
<h2>
<span style="color: #93c47d; font-size: x-large;">文章開始</span></h2>
<span style="font-size: large;"><b>觸控頁面與元件的關係</b></span><br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHEOVdDog2LTbZNMKwF5oRtpwz65MViqmcefHOXScpnXfHOAC3cInETf_I2HXa8yFeVKwDVrtbqq-6l3fYZ19HTNqmuoMjVWF6Y4vQDjfkR3EYWS_yFBo21wjzgp-GEo8DxG_-4LMZKA/s1600/PgRvm.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="742" data-original-width="1000" height="295" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHEOVdDog2LTbZNMKwF5oRtpwz65MViqmcefHOXScpnXfHOAC3cInETf_I2HXa8yFeVKwDVrtbqq-6l3fYZ19HTNqmuoMjVWF6Y4vQDjfkR3EYWS_yFBo21wjzgp-GEo8DxG_-4LMZKA/s400/PgRvm.jpg" width="400" /></a>When a touch event occurs, first everyone is notified of the event, starting at the Activity and going all the way to the view on top.<br />
Then everyone is given a chance to handle the event, starting with the view on top and going all the way back to the Activity. So the Activity is the first to hear of it and the last to be given a chance to handle it.<br />
當一個觸控事件發生時,每個元件都會在第1時間被通知,從Activity層開始層層往上,通知到最頂層的View。<br />
每個元件在這段被通知的過程中,都有機會來處理這個事件,Activity是第一個聆聽到事件的元件,但是它卻是在最後才被處理到的元件。<br />
<br />
If the Activity or some ViewGroup wants to handle the touch event right away (and not give anyone else down the line a chance at it) then it can just return true in its onInterceptTouchEvent().<br />
如果Activity或某些ViewGroup想要立即處理觸控事件(並且不給其它元件任何處理的機會),那麼可以在該元件的onInterceptTouchEvent()中返回true。<br />
<br />
If a View (or a ViewGroup) has an OnTouchListener, then the touch event is handled by OnTouchListener.onTouch(). Otherwise it is handled by onTouchEvent(). If onTouchEvent() returns true for any touch event, then the handling stops there. No one else down the line gets a chance at it.<br />
如果一個View(或ViewGroup)想要擁有OnTouchListener事件,基本上該觸控事件是由OnTouchListener.onTouch()來處理,否則則由onTouchEvent()事件來處理。如果onTouchEvent()針對每個觸控事件皆返回true,那麼事件將被處理到該階層而停止,後面剩餘的元件將無法得到任何觸控的機會。<br />
<br />
-------------------------------<br />
More detailed explanation<br />
<b><span style="font-size: large;">詳細解說</span></b><br />
<br />
The above diagram makes things a little more simple than they actually are. For example, between the Activity and ViewGroup A (the root layout) there is also the Window and the DecorView. I left them out above because we generally don't have to interact with them. However, I will include them below. The description below follows a touch event through the source code. You can click a link to see the actual source code.<br />
下面的圖例也許能把整件事變得稍微簡單些,舉例來說,在Activity與ViewGroup A(根佈局)之間還有Window與DecorView。我們把它們排除在上面,因為我們一般來說不會與這兩個階層互動。不過,我會在下面的範例中引入它們。下面的描述是觸控事件的程式碼,你可以點擊連結來看源始碼。<span style="white-space: pre;"> </span><br />
<br />
1.The Activity's dispatchTouchEvent() is notified of a touch event. The touch event is passed in as a MotionEvent, which contains the x,y coordinates, time, type of event, and other information.<br />
Activity的dispatchTouchEvent()被通知有一個觸控事件,這個觸控事件藉由MotionEvent傳入,過程中包含了x、y的坐標值、時間。事件類型與其它的信息。<br />
<br />
2.The touch event is sent to the Window's superDispatchTouchEvent(). Window is an abstract class. The actual implementation is PhoneWindow.<br />
觸發事件發到superDispatchTouchEvent()到Window階層,Window是一件抽象類別,實際實作的是PhoneWindow。<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGxdDG8FecTrNKdUznYua3dUaL95ykxu-X9pJ6GA5tX-rYf9-ehntqX2iOGvEGL5Eu9clVEgSp8J9gRNvPkI_Urummn93Dn7PWdMaNSXT68wiKzMprqkhGr3mpDX9l3jGR55M7cqb9Pg/s1600/bWGSv.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" data-original-height="1196" data-original-width="1000" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGxdDG8FecTrNKdUznYua3dUaL95ykxu-X9pJ6GA5tX-rYf9-ehntqX2iOGvEGL5Eu9clVEgSp8J9gRNvPkI_Urummn93Dn7PWdMaNSXT68wiKzMprqkhGr3mpDX9l3jGR55M7cqb9Pg/s400/bWGSv.jpg" width="333" /></a>3.The next in line to get the notification is DecorView's superDispatchTouchEvent(). DecorView is what handles the status bar, navigation bar, content area, etc. It is actually just a FrameLayout subclass, which is itself a subclass of ViewGroup.<br />
接下來我們取到的通知是DecorView的superDispatchTouchEvent()。DecorView是處理上方狀態列(status bar) 、下方導覽列(navigation bar)以及內容...等等的東西。它實際上只是一個FrameLayout的子類別,它本身就是ViewGroup的子類。<br />
<br />
4.The next one to get the notification (correct me if I'm wrong) is the content view of your activity. That is what you set as the root layout of your activity in xml when you create the layout in the Android Studio's Layout Editor. So whether you choose a RelativeLayout, a LinearLayout, or a ConstraintLayout, they are all subclasses of ViewGroup. And ViewGroup gets notified of the touch event in dispatchTouchEvent(). This is the ViewGroup A in my diagrams above.<br />
下一個得到通知(如果我錯了請糾正我)的是你的Activity裡的content view。這個就是你在Android Studio佈局編輯器裡編輯的、你設為根佈局的layout xml。因此,無論你選擇竹旳是RelativeLayout、LinearLayout、或是ConstraintLayout,它們都隸屬於ViewGroup的子類別。ViewGroup在dispatchTouchEvent()中獲得到觸控事件。這是我上圖裡的ViewGroup A。<br />
<br />
5.The ViewGroup will notify any children it has of the touch event, including any ViewGroup children. This is ViewGroup B in my diagrams above.<br />
ViewGroup將會開始通知觸控事件至底下的任何子元件項目,這也包含了ViewGroup的所有子項。在圖例中我以ViewGoup B來做舉例。<br />
<br />
6.Anywhere along the way, a ViewGroup can short-circuit the notification process by returning true for onInterceptTouchEvent().<br />
在任何情況下,隸屬於ViewGroup的元件皆可透過onInterceptTouchEvent()回傳true來讓流程"短路"。<br />
<br />
7.Assuming no ViewGroup cut the notifications short, the natural end of the line for the notifications is when the View's dispatchTouchEvent() get's called.<br />
假設沒有ViewGroup讓通知流程縮短,那麼通知事件自然結束的點就是在View的dispatchTouchEvent()事件被調用的時候。<br />
<br />
8.Now it is time, to start handling the events. If there is an OnTouchListener, then it gets the first chance at handling the touch event with onTouch(). Otherwise, the View's onTouchEvent() gets to handle it.<br />
通知事件的流程到此結束,是時候開始往上處理觸控事件了。如果在View層有一個OnTouchListner,那麼它會在onTouch()中得到第1次的觸控處理的機會。否則會由View的onTouchEvent()來處理觸控事件。<br />
<br />
9.Now all the ViewGroups recursively up the line get a chance to handle the touch event in the same way that View did. Although, I didn't indicate this in the diagram above, a ViewGroup is a View subclass, so everything I described about OnTouchListener.onTouch() and onTouchEvent() also applies to ViewGroups.<br />
接下來所有的ViewGroups都像View層一樣,皆以遞迴的方式向上處理觸控事件。儘管我沒有在上圖指出ViewGroup是View的子類別,但我描述有關OnTouchListener.onTouch()和onTouchEvent()也皆適用於ViewGroups。<br />
<br />
10.Finally, if no one else wanted it, the Activity also gets the last chance to handle the event with onTouchEvent().<br />
1最後,如果沒有任何的元件需要它,Acitivy也會在onTouchEvent()中得到最後一次的機會來處理觸控事件。<br />
<br />
<span style="font-size: large;"><b>FAQ</b></span><br />
1.When would I ever need to override dispatchTouchEvent()?<br />
我何時需要覆寫dispatchTouchEvent()?<br />
<br />
答:<br />
Probably you wouldn't need to, unless you need to do some extra routing that doesn't occur by default. To monitor touch event notifications, you can override onInterceptTouchEvent() instead.<br />
也許你不需要這樣做,除非你需要做一些額外的路由,否則默認狀況下這個情況是不會發生的。要監控觸控事件,你可以覆寫onInterceptTouchEvent()即可。<br />
<br />
2.When would I ever need to override onInterceptTouchEvent()?<br />
我何時需要覆寫onInterceptTouchEvent()?<br />
<br />
答:<br />
If you just want to spy of the touch notifications that are coming in, you can do it here and return false.<br />
如果你只是想監控正在進入的觸控事件,你可以在此時處理並返回false。<br />
<br />
However, the main purpose of overriding this method is to let the ViewGroup handle a certain type of touch event while letting the child handle another type. For example, a ScrollView does this to handle scrolling while letting its child handle something like a Button click. Conversely, if the child view doesn't want to let its parent steal its touch event, it can call requestDisallowTouchIntercept().<br />
但是,覆寫這個函式的主要目的是為了讓ViewGroup能處理特定類型的觸控事件,同時讓子元件處理其它類型的事件。舉例來說,ScrollView這樣子實作來處理滾動,同時它的子元件(像是Button)則是處理按鈕點擊事件。相反地,如果子視圖不想要讓父母竊取其觸控事件,則可以調用requestDisallowTouchIntercept()。<br />
<br />
3.What are the touch event types?<br />
觸控類型有哪些?<br />
<br />
答:<br />
The main ones are 主要的有<br />
ACTION_DOWN - This is the start of a touch event. You should always return true for the ACTION_DOWN event in onTouchEvent if you want to handle a touch event. Otherwise, you won't get any more events delivered to you.<br />
ACTION_DOWN - 這是觸控事件的開始,在onTouchEvent()裡ACTION_DOWN的時候你也許永遠要返回true。否則,你不會再收到任何更多的事件。<br />
ACTION_MOVE - This event is continuously fired as you move your finger across the screen.<br />
ACTION_MOVE - 當你的手指在螢幕上不斷的滑動時,該事件會不斷的被觸發。<br />
ACTION_UP - This is the last event of a touch event.<br />
ACTION_UP - 這是觸控事件的最後一個事件。<br />
<br />
A runner up is ACTION_CANCEL. This gets called if a ViewGroup up the tree decides to intercept the touch event.<br />
第二類型的是ACTION_CANCEL,如果ViewGroup樹狀上決定攔截觸控事件,則會收到該事件。<br />
<br />
You can view the other kinds of MotionEvents here. Since Android is multi-touch, events are also fired when other fingers ("pointers") touch the screen.<br />
你可以在這裡參考其它類型的MotionEvents,因為Android是多點觸控的,當其它手指("點擊")螢幕時,事件也會被觸發。<br />
<br />
<b><span style="color: #e69138;">延伸閱讀</span></b><br />
<br />
<ul>
<li>Android onTouchEvent <a href="https://www.youtube.com/watch?v=SYoN-OvdZ3M" rel="noreferrer">Part 1</a>, <a href="https://www.youtube.com/watch?v=nOcznwNEBf4" rel="noreferrer">Part 2</a>, and <a href="https://www.youtube.com/watch?v=GIWQn90av54" rel="noreferrer">Part 3</a> (YouTube video - good summary of some of the links below)</li>
<li><a href="https://www.youtube.com/watch?v=EZAoJU-nUyI" rel="noreferrer">Mastering the Android Touch System</a> (thorough video by Google developer)</li>
<li><a href="http://pierrchen.blogspot.com/2014/03/pipeline-of-android-touch-event-handling.html" rel="noreferrer">Android UI Internal : Pipeline of View's Touch Event Handling</a> </li>
<li><a href="https://developer.android.com/training/gestures/viewgroup.html" rel="noreferrer">Managing Touch Events in a ViewGroup</a> (Android docs)</li>
<li><a href="https://developer.android.com/guide/topics/ui/ui-events.html" rel="noreferrer">Input Events</a> (Android docs)</li>
<li><a href="https://github.com/codepath/android_guides/wiki/Gestures-and-Touch-Events" rel="noreferrer">Gestures and Touch Events</a></li>
</ul>
Anonymoushttp://www.blogger.com/profile/07460782835337903344noreply@blogger.com0tag:blogger.com,1999:blog-5753539950414636776.post-16294478846217261522017-01-03T20:32:00.002-08:002017-01-03T22:34:25.645-08:00關於android:clipToPadding<span style="color: #999999;"><span style="font-size: x-small;">文章攢寫時間︰2017/01/04 11:48</span></span><br />
<br />
<span style="color: #f6b26b;">本篇參考來源</span><br />
1. <a href="http://blog.csdn.net/lfdfhl/article/details/38051815">android:clipToPadding属性的分析——以ListView的"别样"padding为例 </a><br />
2. <a href="http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0317/2613.html">android:clipToPadding和android:clipChildren</a><br />
<br />
<h4 style="background-color: white; color: #222222; font-family: arial, tahoma, helvetica, freesans, sans-serif; font-size: 13.2px; line-height: 18.48px; margin: 0px; position: relative;">
<span style="color: orange;">本篇適合</span></h4>
<span style="background-color: white; color: #222222; font-family: "arial" , "tahoma" , "helvetica" , "freesans" , sans-serif; font-size: 13.2px; line-height: 18.48px;">1.Android開發者</span>
<br />
<br />
<h2>
<span style="color: #6aa84f; font-size: x-large;">一、文章開始</span></h2>
不管在使用過去的ListView或現在的RecyclerView,很容易在xml檔裡遇到一個參數<br />
<pre class="brush: java; toolbar: false; gutter: false; highlight: [1]"> android:cliptopadding="true"
</pre>
<br />
完整的xml檔如下<br />
<pre class="brush: java; toolbar: false; highlight: [6]"><android.support.v7.widget.recyclerview=""
android:id="@+id/recyclerview"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:padding="16dp"/>
android:cliptopadding="true"/>
</pre>
<br />
這個值做什麼用的呢?
直接看影片吧!
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/XCzHlN8wjgs/0.jpg" frameborder="0" height="266" src="https://www.youtube.com/embed/XCzHlN8wjgs?feature=player_embedded" width="320"></iframe></div>
<div style="text-align: center;">
<a href="https://youtu.be/XCzHlN8wjgs">影片連結</a></div>
Anonymoushttp://www.blogger.com/profile/07460782835337903344noreply@blogger.com5tag:blogger.com,1999:blog-5753539950414636776.post-77101061737498572832016-02-04T23:59:00.000-08:002016-02-04T23:59:00.136-08:00運行網頁遇到 Access-Control-Allow-Origin問題
XMLHttpRequest cannot load http://xxxxx/xxx.html No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.
解決辦法
html網頁不要放在本機跑
請放到Apache或其它已啟動之伺服器
Anonymoushttp://www.blogger.com/profile/07460782835337903344noreply@blogger.com0tag:blogger.com,1999:blog-5753539950414636776.post-32698680217410528612016-01-17T06:55:00.000-08:002016-01-17T18:03:10.924-08:00[Material Design] 開始使用 Point of Origin (始於原點)<span style="color: #999999;"><span style="font-size: x-small;">文章攢寫時間︰2016/01/16 18:37</span></span><br />
<br />
<span style="color: #f6b26b;">本篇參考來源</span><br />
1. <a href="http://www.raywenderlich.com/103367/material-design/">Android: An Introduction to Material Design</a><br />
<br />
<h4 style="background-color: white; color: #222222; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 13.2px; line-height: 18.48px; margin: 0px; position: relative;">
<span style="color: orange;">本篇適合</span></h4>
<span style="background-color: white; color: #222222; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 13.2px; line-height: 18.48px;">1.Android中級開發者</span><br />
<span style="background-color: white; color: #222222; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 13.2px; line-height: 18.48px;">2.UX Designer</span><br />
<span style="background-color: white; color: #222222; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 13.2px; line-height: 18.48px;">3.Graphic Designer / Artist</span><br />
<span style="background-color: white; color: #222222; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 13.2px; line-height: 18.48px;"><br /></span>
<br />
<h2>
<span style="color: #6aa84f; font-size: x-large;">一、前言</span></h2>
<a href="http://lp43.blogspot.tw/2014/07/material-design.html">上次</a>跟大家介紹Material Design(實感設計,或譯材料設計),<br />
已事隔1年半之久,<br />
這1年半裡發生了許多次的Android改版,<br />
Google針對Material Design,<br />
也開始透過Support Library(兼容性套件)的方式釋岀相關資源給大家,<br />
因此,<br />
在這邊跟老朋友們分享目前所認識的Material Design給老朋友知道了。<br />
<br />
<h2>
<span style="color: #6aa84f; font-size: x-large;">二、文章開始</span></h2>
<h3>
<b><span style="color: #3d85c6; font-size: large;">Point of Origin</span></b>(始於原點,暫譯)</h3>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-ZQ87Dqim1rXsLlX1ZDRQclaSVQTLryn3Au3ssjHIb81hoDTvoMBQhXF1YqF4egMNkg1xYbDdaWQpvI4_yh1S-JyqEMT7FQafL3f9Q_M-PZ86JDY9-x1xCE5ER5Lm8J6LMbNNeA9MDA/s1600/Point-of-Origin.gif" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-ZQ87Dqim1rXsLlX1ZDRQclaSVQTLryn3Au3ssjHIb81hoDTvoMBQhXF1YqF4egMNkg1xYbDdaWQpvI4_yh1S-JyqEMT7FQafL3f9Q_M-PZ86JDY9-x1xCE5ER5Lm8J6LMbNNeA9MDA/s320/Point-of-Origin.gif" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">始於原點</td></tr>
</tbody></table>
<br />
Poiint of Origin是我對Material Design的第1個認識,<br />
任何點擊後展開的行為,<br />
應該都要<b><span style="color: red;">始於原點</span></b>。<br />
<br />
這種設計方式可以很淸楚的對使用者交待︰<br />
「<b><span style="color: red;">現在顯示岀來的新頁面,是從剛剛你點擊的地方延伸岀來的。</span></b>」<br />
<br />
底下提供一個範例︰<br />
1. 使用者點擊頁面1裡RecyclerView的任一列表,開啟頁面2<br />
2. 頁面2開啟後,顯示岀剛才使用者在頁面1點擊的列表其左側ImageView放大版<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjBurhLNS2b0B-4Skg_gdafIVr-x-yn7cL3oDjDw4A2-0X0lZiMk_l38nzSEIhWW5ddFVl0FTb8Rjf097YLv6NZnot9MtTWIU-EOo-q4WdzsLzYPBRWGlY6MoCx_OoYW_H2mN6Jm-FEQ/s1600/flow.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="326" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjBurhLNS2b0B-4Skg_gdafIVr-x-yn7cL3oDjDw4A2-0X0lZiMk_l38nzSEIhWW5ddFVl0FTb8Rjf097YLv6NZnot9MtTWIU-EOo-q4WdzsLzYPBRWGlY6MoCx_OoYW_H2mN6Jm-FEQ/s400/flow.png" width="400" /></a></div>
<br />
<br />
<span style="color: #3d85c6; font-size: large;"><b>開始攢寫程式</b></span><br />
<br />
<b>1.攢寫FirstActivity</b><br />
我們先創建第1個頁面,<br />
將其命名為FirstActivity,<br />
並在裡面宣告使用一組RecyclerView。<br />
<pre class="brush: java; toolbar: false;">public class FirstActivity extends Activity {
private String[] name = {
"Umbrella Droid",
"Box Droid",
"Injured Droid",
"Evil Droid",
"Shadow Droid"
};
private int[] imgSmall = {
R.drawable.droid1_s,
R.drawable.droid2_s,
R.drawable.droid3_s,
R.drawable.droid4_s,
R.drawable.droid5_s
};
private int[] imgLarge = {
R.drawable.droid1_l,
R.drawable.droid2_l,
R.drawable.droid3_l,
R.drawable.droid4_l,
R.drawable.droid5_l
};
// this is data for recycler view
ItemData itemsData[] = {
new ItemData(name[0], imgSmall[0], imgLarge[0]),
new ItemData(name[1], imgSmall[1], imgLarge[1]),
new ItemData(name[2], imgSmall[2], imgLarge[2]),
new ItemData(name[3], imgSmall[3], imgLarge[3]),
new ItemData(name[4], imgSmall[4], imgLarge[4]),
new ItemData(name[0], imgSmall[0], imgLarge[0]),
new ItemData(name[1], imgSmall[1], imgLarge[1]),
new ItemData(name[2], imgSmall[2], imgLarge[2]),
new ItemData(name[3], imgSmall[3], imgLarge[3]),
new ItemData(name[4], imgSmall[4], imgLarge[4]),
new ItemData(name[0], imgSmall[0], imgLarge[0]),
new ItemData(name[1], imgSmall[1], imgLarge[1]),
new ItemData(name[2], imgSmall[2], imgLarge[2]),
new ItemData(name[3], imgSmall[3], imgLarge[3]),
new ItemData(name[4], imgSmall[4], imgLarge[4]),
new ItemData(name[0], imgSmall[0], imgLarge[0]),
new ItemData(name[1], imgSmall[1], imgLarge[1]),
new ItemData(name[2], imgSmall[2], imgLarge[2]),
new ItemData(name[3], imgSmall[3], imgLarge[3]),
new ItemData(name[4], imgSmall[4], imgLarge[4])
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
MyAdapter mAdapter = new MyAdapter(this, itemsData);
recyclerView.setAdapter(mAdapter);
recyclerView.setItemAnimator(new DefaultItemAnimator());
}
}
</pre>
<br />
<b>2.定義岀RecyclerView需要的MyAdapter
</b><br />
<pre class="brush: java; toolbar: false; highlight: [29, 30,34, 35, 36]">public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder>{
private static Context context;
private static ItemData[] itemsData;
public MyAdapter(Context cnx, ItemData[] itemsData) {
this.context = cnx;
this.itemsData = itemsData;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
// create a new view
View itemLayoutView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_layout, null);
// create ViewHolder
ViewHolder viewHolder = new ViewHolder(itemLayoutView);
return viewHolder;
}
@Override
public void onBindViewHolder(final ViewHolder viewHolder, final int position) {
// - get data from your itemsData at this position
// - replace the contents of the view with that itemsData
viewHolder.txtViewTitle.setText(itemsData[position].getTitle());
viewHolder.imgViewIcon.setImageResource(itemsData[position].getImageSmall());
viewHolder.setOnItemClick(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 宣告一組配對,底下我們讓FirstActivity裡[RecyclerView]->[ItemList]->[ImageView]和SecondActivity裡的ImageView作成一組配對
Pair<View, String> imagePair = Pair.create((View) viewHolder.imgViewIcon, "tImage");
Intent transitionIntent = new Intent( context, SecondActivity.class);
transitionIntent.putExtra("imageRes", itemsData[position].getImageLarge());
// 製作成 Material Design 需要的 ActivityOptionsCompat
ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation((Activity)context, imagePair);
ActivityCompat.startActivity((Activity)context, transitionIntent, options.toBundle());
}
});
}
// Return the size of your itemsData (invoked by the layout manager)
@Override
public int getItemCount() {
return itemsData.length;
}
// inner class to hold a reference to each item of RecyclerView
public static class ViewHolder extends RecyclerView.ViewHolder {
private View itemLayoutView;
public TextView txtViewTitle;
public ImageView imgViewIcon;
public ViewHolder(View itemLayoutView) {
super(itemLayoutView);
this.itemLayoutView = itemLayoutView;
txtViewTitle = (TextView) itemLayoutView.findViewById(R.id.item_title);
imgViewIcon = (ImageView) itemLayoutView.findViewById(R.id.item_icon);
}
public void setOnItemClick(View.OnClickListener l){
this.itemLayoutView.setOnClickListener(l);
}
}
}</pre>
<br />
從上面被標註的Code看到了在開啟頁面前,<br />
宣告了一個全新的元件<span style="background-color: #2b2b2b; color: #a9b7c6; font-family: Menlo; font-size: 12pt;">Pair</span>(配對)<br />
<pre class="brush: java; toolbar: false; gutter: false;">Pair<View, String> imagePair = Pair.create(from_A, to_B);
</pre>
<br />
from_A 這裡請傳入頁面A的View元件實體,範例裡傳入的是viewHolder.imgViewIcon<br />
to_B 這裡請傳入頁面B在xml裡宣告的transitionName(識別標籤),範例裡傳入的是tImage<br />
<br />
利用Pair,<br />
Material Design便可以知道您想要從頁面1的A元件延展至頁面2的B元件上。<br />
然後,<br />
我們再將Pair透過兼容性套件提供的<span style="background-color: #344134; color: #a9b7c6; font-family: Menlo; font-size: 12pt;">ActivityOptionsCompat</span>元件,<br />
轉成Android 5.0可以接收的Bundle,<br />
最後,<br />
利用兼容性套件提供的方法,<br />
開啟頁面B。<br />
<pre class="brush: java; toolbar: false; gutter: false;">ActivityCompat.startActivity((Activity)context, transitionIntent, options.toBundle());
</pre>
<br />
<span style="color: #999999;">註︰</span><br />
<span style="color: #999999;">為什麼利用兼容性套件的方式開啟新頁面而不直接startActivity()呢?</span><br />
<span style="color: #999999;">因為在Android Jelly Bean(SDK 16)開始,</span><br />
<span style="color: #999999;">startActivity()裡的參數才開始能接收bundle,</span><br />
<span style="color: #999999;">為了讓Code裡不用去區分SDK是否>16,</span><br />
<span style="color: #999999;">因此我們直接使用兼容性套件去做判斷。</span><br />
<br />
<b>3.做岀頁面2</b><br />
頁面2就只是一個很基本放上ImageView的頁面<br />
<pre class="brush: java; toolbar: false;">public class SecondActivity extends Activity {
private static final String TAG = "SecondActivity";
private ImageView image;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sescond);
image = (ImageView) findViewById(R.id.image);
if(getIntent()!=null){
int res = getIntent().getIntExtra("imageRes", 0);
if(res!=0){
Log.i(TAG, "onCreate: "+res);
image.setImageResource(res);
}
}else{
Log.i(TAG, "onCreate: getIntent()==null");
}
Log.i(TAG, "onCreate: ");
}
</pre>
<br />
activity_sescond.xml︰
<br />
<pre class="brush: java; toolbar: false; highlight: [24]"><?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="bottom"
android:gravity="bottom">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginTop="20dp"
android:layout_marginBottom="150dp"
android:text="Point of Origin執行完畢\n\n可以看到ImageView從列表左方 移動放大 至下方\n\n請按[返回鍵]回上一頁"
android:gravity="center"/>
<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitCenter"
android:src="@drawable/imageholder"
android:transitionName="tImage"
android:cropToPadding="false"
android:adjustViewBounds="true" />
</LinearLayout>
</pre>
<br />
從上面標註起來的地方,<br />
我們可以看到Pair最後一個傳入的參數transitionName(識別標籤)被宣告在頁面B的ImageView裡。<br />
<br />
差不多Coding完畢,<br />
執行看看效果︰<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8cEl1WotQyV2tbOoFS01nv4WNZBkabj7YBDimte1ptzJS5PED6-V9v8TgFDncJ-oo8HKP9JsqPhuovrjZXIF4zA628XCnJg1xuEVUih8cV1Re1I42-v3U6QhWtCJeUX-mqjOs9qUESQ/s1600/ezgif.com-video-to-gif.gif" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8cEl1WotQyV2tbOoFS01nv4WNZBkabj7YBDimte1ptzJS5PED6-V9v8TgFDncJ-oo8HKP9JsqPhuovrjZXIF4zA628XCnJg1xuEVUih8cV1Re1I42-v3U6QhWtCJeUX-mqjOs9qUESQ/s400/ezgif.com-video-to-gif.gif" width="266" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Point of Origin結果展示</td></tr>
</tbody></table>
<br />
<h2>
<span style="color: #6aa84f; font-size: x-large;">三、結論</span></h2>
透過簡單的傳入頁面A的元件實體和頁面B的trasitionName(識別標籤),<br />
Material Design便能幫您做岀一組Point of Origin動畫。<br />
很簡單吧?<br />
<br />
<h2>
<b><span style="color: #6aa84f; font-size: x-large;">四、其它</span></b></h2>
有一個需要注意的地方,就是<br />
<b><span style="color: red;">Point of Origin僅在Android 5.0以上有作用</span></b>。<br />
<br />
查了一下底層,<br />
主要是因為從Pair轉换岀來並塞給startActivity的Bundle在Android 5.0才開始對它做岀判斷並解析成動畫特效。<br />
<br />
如果使用者的裝置在Android 5.0以下,<br />
則仍會正常從頁面1開到頁面2,<br />
<b><span style="color: red;">只是Point of Origin的特效就不會岀現</span></b>,<br />
這邊可能要跟使用這個功能的開發者告知一聲。<br />
<br />
附上本篇教學的<a href="https://github.com/lp43/HelloMaterial.git">原始碼</a><br />
<br />
<h4>
<span style="color: orange;">上一篇</span></h4>
1. <a href="http://lp43.blogspot.tw/2014/07/material-design.html">Material Design 的介紹與實務</a>Anonymoushttp://www.blogger.com/profile/07460782835337903344noreply@blogger.com0tag:blogger.com,1999:blog-5753539950414636776.post-88126803608774825632015-12-06T03:27:00.005-08:002016-01-17T18:34:11.376-08:00讓你的view跟著CoordinatorLayout舞動<span style="color: #999999;"><span style="font-size: x-small;">文章攢寫時間︰2015/12/06 18:37</span></span><br />
<span style="color: #999999;"><span style="font-size: x-small;">文章更新時間︰2016/01/17 16:53</span></span><br />
<span style="color: #999999; font-size: x-small;">文章修改次數︰2</span><br />
<br />
<span style="color: #f6b26b;">本篇參考來源</span><br />
1. <a href="https://lab.getbase.com/introduction-to-coordinator-layout-on-android/">INTRODUCTION TO COORDINATOR LAYOUT ON ANDROID</a><br />
<br />
<h2>
<span style="color: #93c47d; font-size: x-large;">一、前言</span></h2>
藉由<a href="http://lp43.blogspot.tw/2015/09/coordinatorlayout.html">前一篇</a>的教學,<br />
了解到官方已經用很簡單的方式就讓我們做到頁面裡元件間彼此互動。<br />
但是,<br />
如果在前一篇的教學裡,<br />
我們用的不是官方的Floating Action Button(以下簡稱FAB),而是第3方的。<br />
那麼我們又該如何實作呢?<br />
<br />
<span style="color: #93c47d; font-size: x-large;">二、文章開始</span><br />
首先,<br />
我們在引用Library時,<br />
改由Maven從repositories(雲端倉庫)下載<br />
<br />
在您的app專案底下、build.gradle檔裡寫下該行︰<br />
<pre class="brush: java; toolbar: false;">compile 'com.getbase:floatingactionbutton:1.9.1'
</pre>
<br />
此時,<br />
就能在專案中引用第3方套件的Floating Action Buton了。
<br />
<br />
接著,<br />
我們將剛才的專案裡,<br />
原本使用官方的FAB佈局改成現在第3方的︰<br />
<pre class="brush: java; toolbar: false;"><!--?xml version="1.0" encoding="utf-8"?-->
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.getbase.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="16dp"
android:src="@drawable/ic_done">
<com.getbase.floatingactionbutton.FloatingActionButton>
</android.support.design.widget.CoordinatorLayout>
</pre>
<br />
然後執行它。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFaDQ9vwak5-sTwS8kE8s-j-G8Snx7afeTAKEj0dfN0pRa789GuXjD6_xR9pWXx2ehADn-XwiboC-x200SXVK-ApMj8Vr9q7wb_dO3O9TRANBxoqYlrOLej_Ay6Nt99hsRrv1ONuOkig/s1600/snackbar2.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFaDQ9vwak5-sTwS8kE8s-j-G8Snx7afeTAKEj0dfN0pRa789GuXjD6_xR9pWXx2ehADn-XwiboC-x200SXVK-ApMj8Vr9q7wb_dO3O9TRANBxoqYlrOLej_Ay6Nt99hsRrv1ONuOkig/s320/snackbar2.gif" width="207" /></a></div>
此時,<br />
我們發現CoordinatorLayout完全沒有任何作用。<br />
為什麼呢?<br />
這是由於我們的第3方FAB套件<span style="color: red;"><b>沒有實作預設的Coordinatorlayout.Behavior</b></span>。<br />
<br />
<br />
<span style="color: #0b5394; font-size: medium;"><span style="color: #0b5394; font-size: large;">●行為!一切都是行為!</span><span style="font-weight: normal;">(Coordinatorlayout.Behavior)</span></span><br />
是的。<br />
<br />
為什麼Android會知道當Snacker往上彈起時,<br />
FAB也需要跟著向上彈起呢?<br />
因為官方的FAB實作了Behavior(行為)。<br />
<br />
籍由CoordinatorLayout這個強大的Framework,<br />
開發者們完全不用自己操刀去控制2個互相牽聯的view元件,<br />
然後一邊算A view(被依賴者,在這篇文章範例裡指的是SnackbarLayout),<br />
一邊再控制B view(依賴者,在這篇文章範例指的是第3方的FAB),<br />
只要實作欲依賴的B view的Behavior,<br />
就能輕鬆達到A、B兩個view的互動。<br />
<br />
怎麼實作呢?<br />
<br />
首先,我們需要創建一個新的類別,<br />
並且繼承自<a href="http://developer.android.com/intl/zh-tw/reference/android/support/design/widget/CoordinatorLayout.Behavior.html">CoordinatorLayout.Behavior</a>。<br />
<br />
<pre class="brush: java; toolbar: false;">public class FloatingActionButtonBehavior extends CoordinatorLayout.Behavior<FloatingActionButton></pre>
<br />
為了確保我們這個實作能從xml中被inflate(擴充),<br />
需要在這個類別裡加上含有Context與AttributeSet兩個參數的建構子︰<br />
<pre class="brush: java; toolbar: false;">public FloatingActionButtonBehavior(Context context, AttributeSet attrs) {}</pre>
<br />
下一步,<br />
Override(覆寫) layoutDependsOn()函式並且回傳true,<br />
以確保CoordinatorLayout能幫我們明確的監聽到A view(被依賴者,SnackbarLayout)的任何畫面異動。<br />
<br />
由於我們現在的例子中,<br />
被依賴者是A view(範例裡的SnackbarLayout元件),<br />
因此我們將layoutDependsOn()函式覆寫如下︰<br />
<pre class="brush: java; toolbar: false;">@Override
public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
return dependency instanceof SnackbarLayout;
}</pre>
<br />
意思是︰<br />
倘若收到的dependency(依賴者)是SnackbarLayout,<br />
那麼我們就告訴CoordinatorLayout說︰<br />
<span style="color: #674ea7;"><b>對對對</b></span>!我就是依賴著它SnackbarLayout。<br />
<br />
再來,<br />
我們要告訴CoordinatorLayout︰"倒底要怎麼個互動"的實作了。<br />
<br />
因為剛才我們已經告訴CoordinatorLayout說︰<br />
第3方的FAB元件現在要依賴SnackbarLayout。<br />
因此,<br />
每當CoordinatorLayout發現到SnackbarLayout有畫面異動時,<br />
都會藉由一隻Callback(回呼函式)通知我們現在正在實作的FloatingActionButtonBehavior類別。<br />
這個Callback為︰
<br />
<pre class="brush: java; toolbar: false; gutter: false;">onDependentViewChanged()</pre>
<br />
因為onDependentViewChanged()這個函式,<br />
我們可以取到被依賴者︰SnackbarLayout視圖當下的<span style="color: #cc0000;">任何狀態</span><br />
(因為...CoordinatorLayout幫你把整個SnackbarLayout視圖都傳過來了...你還會抓不到嗎@@)<br />
<br />
我們現在想要做岀以下這個效果,<br />
這也是<a href="http://lp43.blogspot.tw/2015/09/coordinatorlayout.html">前一篇教學</a>原本就有的效果︰<br />
<br />
<span style="color: #999999; font-size: large;">每當Snackbar向上彈起時,</span><br />
<span style="color: #999999; font-size: large;">我們第3方FAB元件也能跟著向上彈起。</span><br />
<br />
為了做到這個效果,<br />
我們需要跟著Snackbar現在的Y值去位移第3方FAB元件的Y值,<br />
簡單來說,<br />
就是Snackbar現在彈多高、FAB就彈多高的意思啦!<br />
<br />
依照官方文件的說明,<br />
實作onDependentViewChanged()這個函式,<br />
我們一樣要回傳true才能發揮作用。<br />
<pre class="brush: java; toolbar: false;">@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
float translationY = Math.min(0, dependency.getTranslationY() - dependency.getHeight());
child.setTranslationY(translationY);
return true;
}
</pre>
<br />
這段Code的意思是︰每當被依賴者SnackBar一旦移動了多少Y值(高度),就將第3方FAB也跟著移動多少Y值。<br />
<br />
這樣子我們就實作完了Behavior要的2個基本Callback(回呼函式)<br />
layoutDependsOn()和onDependentViewChanged(),以及一個含有AttributeSet的建構子了。<br />
<br />
<b><span style="color: #3d85c6; font-size: large;">剩下最後一步...</span></b><br />
現在我們已經將Behavior實作完了,<br />
接下來就只剩下︰<br />
<span style="color: red;">告訴CoordinatorLayout我們的第3方FAB有實作你要的Behavior,</span><br />
<span style="color: red;">請你幫我收下並做岀我要的效果!</span><br />
<br />
因此,<br />
我們將xml裡,第3方的FAB元件加上一行屬性<br />
<pre class="brush: java; toolbar: false;">app:layout_behavior="com.simon.hellocoordinatorlayout.FloatingActionButtonBehavior"</pre>
<br />
整頁xml看起來會是這個樣子
<br />
<pre class="brush: java; toolbar: false; highlight: [20]"><!--?xml version="1.0" encoding="utf-8"?-->
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="右下角是第3方套件的FAB按鈕,該專案同時實作了CoordinatorLayout.Behivor"/>
<com.getbase.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="16dp"
app:fab_icon="@drawable/ic_done"
app:layout_behavior="com.simon.hellocoordinatorlayout.FloatingActionButtonBehavior"/>
</android.support.design.widget.CoordinatorLayout>
</pre>
<br />
差不多就是這個樣子了,<br />
為了證明這整篇教學沒有在唬爛,<br />
底下這是執行後的畫面︰<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhiMnJO74yr_GgCwbalIaFX9zpGAxNrbTzGG7cfAlU_e5c_jzSTSY_ZQrWsY9syZzqC4p8_WTE5YTYOq6BmFMoWtwOTau205a801CjLQhNOVurKUySS8d41HZjei6mfR3OW45zFpxyWA/s1600/snackbar3.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhiMnJO74yr_GgCwbalIaFX9zpGAxNrbTzGG7cfAlU_e5c_jzSTSY_ZQrWsY9syZzqC4p8_WTE5YTYOq6BmFMoWtwOTau205a801CjLQhNOVurKUySS8d41HZjei6mfR3OW45zFpxyWA/s320/snackbar3.gif" width="207" /></a></div>
如果您想要使用預設的Behavior,<br />
只需要在您的Behavior類別上,<br />
加上DefaultBehavior這個Anntation即可。<br />
<h2>
<span style="font-size: x-large;"><br /><span style="color: #6aa84f;">三、總結</span></span></h2>
藉由實作CoordinatorLayout.Behavior,<br />
我們讓 第3方FAB元件 與 SnackBar 達到了 <span style="color: #3d85c6;">互 動 </span>效果。<br />
<br />
那...如果今天有一個專案需求是︰<br />
當列表滾動時,<br />
上方的工具列要隱藏,<br />
又要怎麼做?<br />
誰是依賴者?誰又是被依賴者?<br />
<br />
待續...XD<br />
<br />
<h2>
<span style="color: #6aa84f; font-size: x-large;">四、其它</span></h2>
附上本篇教學的<a href="https://github.com/lp43/HelloCoordinatorLayoutBehavior.git">原始碼</a>Anonymoushttp://www.blogger.com/profile/07460782835337903344noreply@blogger.com0tag:blogger.com,1999:blog-5753539950414636776.post-3985224294309963462015-09-16T02:59:00.002-07:002016-01-17T18:31:27.311-08:00CoordinatorLayout初步認識<span style="color: #999999;"><span style="font-size: x-small;">文章攢寫時間︰2015/09/16 18:37</span></span><br />
<span style="color: #999999; font-size: x-small;">文章更新時間︰2016/01/17 16:53</span><br />
<span style="color: #999999; font-size: x-small;">文章更新次數︰4</span><br />
<br />
<h4>
<span style="color: #f6b26b;">本篇參考來源</span></h4>
1. <a href="https://lab.getbase.com/introduction-to-coordinator-layout-on-android/">INTRODUCTION TO COORDINATOR LAYOUT ON ANDROID</a><br />
<br />
<h2>
<span style="color: #6aa84f; font-size: x-large;">一、前言</span></h2>
在<a href="http://www.androidcentral.com/google-io-2015">2015年的Google I/O大會</a>上,<br />
Google介紹了有關於Android Design Support Library,<br />
該套件讓開發者們能更方便的使用Material Design(實感設計,或譯材料設計),<br />
而且也能在API level 7(Android 2.1)或以上的Android版本兼容。<br />
相關資訊可以參考<a href="http://android-developers.blogspot.tw/2015/05/android-design-support-library.html">Android Developers Blog</a>。<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/32i7ot0y78U/0.jpg" frameborder="0" height="266" src="https://www.youtube.com/embed/32i7ot0y78U?feature=player_embedded" width="320"></iframe></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://youtu.be/32i7ot0y78U">前往觀看影片</a></div>
<br />
<br />
<h2>
<span style="color: #6aa84f; font-size: x-large;">二、文章開始</span></h2>
Android Design Support Library有很多好用的Material Design元件,<br />
其中最有趣的是官方宣稱一個"超強大"的FrameLayout,<br />
名為<span style="color: #3d85c6; font-size: large;"><b>CoordinatorLayout</b></span>。<br />
就如同這個名字所言,<br />
這個強大的Layout擁有"<span style="color: #3d85c6;">依據Layout底下一個View的位置變化,</span><br />
<span style="color: #3d85c6;">進而讓其它子View也跟著位移</span>"的能力。<br />
<br />
使用這個CoordinatorLayout的方式只有一個,<br />
就是將子View放進CoordinatorLayout的子階層(就像您使用FrameLayout那樣,將TextView放進FrameLayout裡)。<br />
<br />
接下來要展示的這個範例相當簡單,<br />
一個Floating Action Buton觸發Snackbar展開連動關係。<br />
<br />
首先我們要做的是 - 將Support Design Library放入Gradle中︰<br />
<pre class="brush: java; toolbar: false;">compile 'com.android.support:design:22.2.0'
</pre>
<br />
再來,
<br />
替activity建立一個簡單的Layout︰<br />
<pre class="brush: java; toolbar: false;">
<!--?xml version="1.0" encoding="utf-8"?-->
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="16dp"
android:src="@drawable/ic_done">
</android.support.design.widget.FloatingActionButton>
</android.support.design.widget.CoordinatorLayout>
</pre>
<br />
接著,
<br />
攢寫activity。
<br />
<pre class="brush: java; toolbar: false;">public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.fab).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Hello Snackbar", Snackbar.LENGTH_LONG).show();
}
});
}
}
</pre>
<br />
做完的結果如下︰<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjajdmCSOLHyxdUf_eMPr5EPnn7hWlvB2lodO69wrneR5wzam0Y15OPsofdF0H1AYPh-Lz-Fw1E0DG6wtd8yjYGfoFtZ03OXWlhBdWwql6SGN-YKp7OKtdvpIOKFAtaa_Il6LG_IG_G5Q/s1600/snackbar1.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjajdmCSOLHyxdUf_eMPr5EPnn7hWlvB2lodO69wrneR5wzam0Y15OPsofdF0H1AYPh-Lz-Fw1E0DG6wtd8yjYGfoFtZ03OXWlhBdWwql6SGN-YKp7OKtdvpIOKFAtaa_Il6LG_IG_G5Q/s400/snackbar1.gif" width="258" /></a></div>
<br />
由上圖可看到︰<br />
當Snackbar(下方突然岀現的黑色bar)岀現時,<br />
Floating Action Buton(綠色勾勾)就會跟著往上移動。<br />
<br />
很酷,對吧?<br />
<br />
<span style="color: #999999;">註︰官方總是喜歡用簡單幾行code(就像Hello World一樣)吸引開發者們嚐鮮,然後⋯你懂的。</span><br />
<br />
但如果今天你有客製化的元件想要也跟著位移,<br />
又該怎麼實作?<br />
跟著位移的原理又是什麼?<br />
<br />
請各位看倌們接著看下一篇教學︰<br />
<a href="http://lp43.blogspot.tw/2015/12/viewcoordinatorlayout.html">讓你的view跟著CoordinatorLayout舞動</a><br />
<br />
如需本篇教學原始碼,<br />
請至<a href="https://github.com/lp43/HelloCoordinatorLayout">Github下載</a>。<br />
<br />
<h4>
<span style="color: #e69138;">相關文章</span></h4>
1. <a href="http://segmentfault.com/a/1190000002873657">Android 嵌套滑动机制(NestedScrolling)</a><br />
2. <a href="http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0717/3196.html">CoordinatorLayout与滚动的处理</a><br />
3. <a href="http://www.race604.com/android-nested-scrolling/">Android NestedScrolling 实战</a><br />
4. <a href="http://android-developers.blogspot.co.at/2015/05/android-design-support-library.html">Android Developers Blog</a><br />
<br />
<br />Anonymoushttp://www.blogger.com/profile/07460782835337903344noreply@blogger.com2tag:blogger.com,1999:blog-5753539950414636776.post-76083741767668716952015-06-10T23:58:00.001-07:002015-06-11T00:14:29.153-07:00使用Google OAuth2登入並使用雲端試算表<span style="color: #999999;"><span style="font-size: x-small;">文章攢寫時間︰2015/06/11 13:37</span></span><br />
<br />
<h2>
<span style="color: #6aa84f; font-size: x-large;">一、前言</span></h2>
自從2015年05月開始,<br />
Google不再提供Client-Login和OAuth的方式提供開發者登入Google帳戶,<br />
如需使用程式登入,<br />
僅能由OAuth2的管道。<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIymM0mJfxexKUQFvuFF-Ug0WVCvAd1Xb1yppwJaipgOljbyrNr8TiF7Z3vZLY14AQyDpW6PvkLiIFXpXDqc7BEk09Pyc9hwwll3mBlAqL2uT97_i50Y4M4n2Tr9TgD3hNr7Ub5NB6ZQ/s1600/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2015-06-11+%25E4%25B8%258B%25E5%258D%25881.47.25.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="70" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIymM0mJfxexKUQFvuFF-Ug0WVCvAd1Xb1yppwJaipgOljbyrNr8TiF7Z3vZLY14AQyDpW6PvkLiIFXpXDqc7BEk09Pyc9hwwll3mBlAqL2uT97_i50Y4M4n2Tr9TgD3hNr7Ub5NB6ZQ/s640/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2015-06-11+%25E4%25B8%258B%25E5%258D%25881.47.25.png" width="640" /></a></div>
<br />
在Android裝置上要登入Google使用其服務,<br />
Play Service已<a href="https://developers.google.com/android/guides/http-auth">提供</a>一個極簡易的方法︰<br />
<pre class="brush: java; toolbar: false;">GoogleAuthUtil.getToken(mActivity, mEmail, mScope);
</pre>
<br />
這種登入的方式雖然很簡便,<br />
但是缺點就是「只能存取自己帳戶底下」的雲端硬碟或個人內容。<br />
如果今天想要把SpreadSheet(試算表)當作雲端資料庫,<br />
讓你的App能隨時來存取資料,<br />
這個方法可能就行不通了。<br />
<br />
<h2>
<span style="color: #6aa84f; font-size: x-large;"><b>二、文章開始</b></span></h2>
<a href="http://lp43.blogspot.tw/2014/10/google-sheets-api.html">前一篇</a>曾使用過Client-Login登入Google的方式來存取SpreadSheet(試算表),<br />
今天則是介紹使用OAuth2的方式。<br />
<br />
Google提供了相~當~多~的管道過OAuth2,<br />
這裡僅示例Android我實際用過且work的方式 - 使用P12檔。<br />
<br />
<h3>
<span style="color: #3d85c6; font-size: large;">步驟1 在<a href="https://code.google.com/apis/console/">Google APIs Console</a>建立一個新專案並做相關設定</span></h3>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwZFhY3mnoDZ1iYbNUDRLYHQnM9ywzcNXoXVzd_OqTrs8UfolM-g0bUCLkPPjwoXwj9n51SwTCsd9Hd7yJofsCoESRgDTKzmk_oFvLPOaYTdw4MxE_W2Z6dyJpR-fujjF41a6utikSsQ/s1600/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2015-06-11+%25E4%25B8%258B%25E5%258D%25881.56.08.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="316" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwZFhY3mnoDZ1iYbNUDRLYHQnM9ywzcNXoXVzd_OqTrs8UfolM-g0bUCLkPPjwoXwj9n51SwTCsd9Hd7yJofsCoESRgDTKzmk_oFvLPOaYTdw4MxE_W2Z6dyJpR-fujjF41a6utikSsQ/s640/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2015-06-11+%25E4%25B8%258B%25E5%258D%25881.56.08.png" width="640" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigEVjxGl4kYvbw4Q1fKrHgWk15SmTAA9s5IaU7hUHjpcww8YDRmCStUYn5E-FKmcCgIwF2HX489hFweV0X6_keP_DKZ91rDMTMovCM9gxVLGRT3Bj0HqsL8QNUhs-6RfgREmV8dfyGtA/s1600/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2015-06-11+%25E4%25B8%258B%25E5%258D%25881.59.06.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="312" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigEVjxGl4kYvbw4Q1fKrHgWk15SmTAA9s5IaU7hUHjpcww8YDRmCStUYn5E-FKmcCgIwF2HX489hFweV0X6_keP_DKZ91rDMTMovCM9gxVLGRT3Bj0HqsL8QNUhs-6RfgREmV8dfyGtA/s640/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2015-06-11+%25E4%25B8%258B%25E5%258D%25881.59.06.png" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">同意畫面是顯示給使用者認證的畫面,<br />
商品名稱一定要打上去,<br />
並記得按下方的儲存鈕。</td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcH8U9fcO9X8KeQhCLFmCuH19InYtJvRDRwhpKlDt1Rjq0AfKzV5H92ek0hdvg3Yx9zk2xJoiUDo9RmzuhNYrPH06B3Xu-BpVXLmZyOud7os2z2ZzOkFkTBR9B1l3gnW3dTaRex1h4zQ/s1600/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2015-06-11+%25E4%25B8%258B%25E5%258D%25882.01.55.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="314" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcH8U9fcO9X8KeQhCLFmCuH19InYtJvRDRwhpKlDt1Rjq0AfKzV5H92ek0hdvg3Yx9zk2xJoiUDo9RmzuhNYrPH06B3Xu-BpVXLmZyOud7os2z2ZzOkFkTBR9B1l3gnW3dTaRex1h4zQ/s640/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2015-06-11+%25E4%25B8%258B%25E5%258D%25882.01.55.png" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">建立新的用戶端ID</td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhV-0J56rqpls-Ff0IJ23m170o51raCKQ9C3Bg_47YKr620cQU3sSZG8rlxADZLi-TEwdqM2hLApK48wWHg5y2BBGN4Sc79upIjUWD0QXK6BPXWVW8M06wuYGQiPG2AQLXupZBn58_r_A/s1600/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2015-06-11+%25E4%25B8%258B%25E5%258D%25882.08.09.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="256" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhV-0J56rqpls-Ff0IJ23m170o51raCKQ9C3Bg_47YKr620cQU3sSZG8rlxADZLi-TEwdqM2hLApK48wWHg5y2BBGN4Sc79upIjUWD0QXK6BPXWVW8M06wuYGQiPG2AQLXupZBn58_r_A/s400/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2015-06-11+%25E4%25B8%258B%25E5%258D%25882.08.09.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">選擇使用P12檔的登入方式</td></tr>
</tbody></table>
此時跳出一個畫面,<br />
把p12檔存起來。<br />
這即將是您登入Google使用的鑰匙。<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHidpYslVOD-lRckLdbiZOY1hxxC96AOKkqllyWxtwRoclNlmguWbVJ2HzjPl6fjcn00OgT7jthyphenhyphen6T3kVpb6-60TiocD9szdj_u6MC37t1Jh2dZ6B36dWvOQiE0AaOc34jJSscM__eGA/s1600/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2015-06-11+%25E4%25B8%258B%25E5%258D%25882.12.08.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="466" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHidpYslVOD-lRckLdbiZOY1hxxC96AOKkqllyWxtwRoclNlmguWbVJ2HzjPl6fjcn00OgT7jthyphenhyphen6T3kVpb6-60TiocD9szdj_u6MC37t1Jh2dZ6B36dWvOQiE0AaOc34jJSscM__eGA/s640/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2015-06-11+%25E4%25B8%258B%25E5%258D%25882.12.08.png" width="640" /></a></div>
<br />
畫面也同時看到多了一個新的登入連線方式了。<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtggL3_ujrSEG8Far2r7V_3jKgZbTHbrcPBirlYv_f1NPXe866hS9WNDlar-gapO19Bd8FKY0Qda23iqIQ4pJAiAqAG48o6kk76aApVsvU2t2xT-e-eRa6vAlsaZLvkimdGGEdjsB_FQ/s1600/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2015-06-11+%25E4%25B8%258B%25E5%258D%25882.09.26.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="310" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtggL3_ujrSEG8Far2r7V_3jKgZbTHbrcPBirlYv_f1NPXe866hS9WNDlar-gapO19Bd8FKY0Qda23iqIQ4pJAiAqAG48o6kk76aApVsvU2t2xT-e-eRa6vAlsaZLvkimdGGEdjsB_FQ/s640/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2015-06-11+%25E4%25B8%258B%25E5%258D%25882.09.26.png" width="640" /></a></div>
<br />
<h3>
<span style="color: #3d85c6; font-size: large;">步驟2 將P12檔複製到Android Studio專案底下</span></h3>
在src/main底下點滑鼠右鍵,<br />
建立一個全新的assets資料夾<br />
(此步驟如果已有assets資料夾則無需理會)<br />
將剛才存起來的P12檔存入。<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVf6O71NMeMiO2AXZbBwfjh4R5uyUr7RQTkIUdqJMRxFvA8BHuux6o69khPcajDtTZkLrlfCkEnxBBSVu07LEuyj6SFsDtX6D9odIHh93HvKn4N5P1KDLeOntQB7NPaCBq3fGbVUf0YQ/s1600/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2015-06-11+%25E4%25B8%258B%25E5%258D%25882.31.28.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="598" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVf6O71NMeMiO2AXZbBwfjh4R5uyUr7RQTkIUdqJMRxFvA8BHuux6o69khPcajDtTZkLrlfCkEnxBBSVu07LEuyj6SFsDtX6D9odIHh93HvKn4N5P1KDLeOntQB7NPaCBq3fGbVUf0YQ/s640/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2015-06-11+%25E4%25B8%258B%25E5%258D%25882.31.28.png" width="640" /></a></div>
<br />
<h3>
<span style="color: #3d85c6; font-size: large;">步驟3 匯入需要的jar檔</span></h3>
OAuth2的登入流程相當繁鎖,<br />
但因為Google也提供了<a href="https://github.com/google/google-oauth-java-client">google-oauth-java-client套件</a>供我們走OAuth2登入,<br />
省掉開發的時程。<br />
<br />
這個專案需要以下的jar檔,<br />
請下載並import。<br />
<pre class="brush: java; toolbar: false;">gdata-core-1.0.jar //spreadsheet會用到
gdata-spreadsheet-3.0.jar //spreadsheet用到
google-http-client-1.18.0-rc.jar //spreadsheet用到
google-http-client-jackson-1.18.00rc.jar //spreadsheet用到
guava-11.0.2.jar //spreadsheet用到
jackson-core-asl-1.9.11.jar //spreadsheet用到
google-api-client-1.18.0-rc.jar //OAuth2會用到
google-oauth-client-1.18.0-rc.jar //OAuth2會用到
</pre>
<br />
<h3>
<span style="color: #3d85c6; font-size: large;">步驟4 開始攢寫程式</span></h3>
走OAuth2時,<br />
告知Google我們要存取什麼範圍(SCOPE)的content,<br />
就可由底下的程式範例做登入動作了。<br />
<pre class="brush: java; toolbar: false;">HttpTransport httpTransport = new NetHttpTransport();
JacksonFactory jsonFactory = new JacksonFactory();
ArrayList SCOPES = new ArrayList();
SCOPES.add("https://spreadsheets.google.com/feeds");
//SerServiceAcountId: clientID value should be similar to @developer.gserviceaccount.com They basically expect the email_address value from the console api credentials
GoogleCredential credential = new GoogleCredential.Builder().setTransport(httpTransport).setJsonFactory(jsonFactory)
.setServiceAccountId("填入剛才新增用戶端ID得到的Mail")
.setServiceAccountPrivateKeyFromP12File(getTempPkc12File())
.setServiceAccountUser("欲分享SpreadSheet的人的gmail")
.setServiceAccountScopes(SCOPES)
.build();
credential.refreshToken();
String accessToken = credential.getAccessToken();
Log.i(TAG, "accessToken: "+accessToken);
</pre>
<br />
getTempPkc12File()函式內容如下,<br />
記得更改掉open()裡的P12檔檔名。<br />
<pre class="brush: java; toolbar: false;"> private File getTempPkc12File() throws IOException {
InputStream pkc12Stream = mContext.getAssets().open("ScalpTest-1a66d0dc35fe.p12");//記得將檔名改成剛才存進Assets資料夾裡的P12檔檔名
File tempPkc12File = File.createTempFile("P12File", "p12");
OutputStream tempFileStream = new FileOutputStream(tempPkc12File);
int read = 0;
byte[] bytes = new byte[1024];
while ((read = pkc12Stream.read(bytes)) != -1) {
tempFileStream.write(bytes, 0, read);
}
return tempPkc12File;
}
</pre>
<br />
如果要使用SpreadSheet API,<br />
這裡提供整份程式碼示例<br />
<pre class="brush: java; toolbar: false;">List<spreadsheetentry> spreadsheets = null;
SpreadsheetService mSpreadSheetService = new SpreadsheetService("欲存取的資料表名稱");
mSpreadSheetService.setProtocolVersion(SpreadsheetService.Versions.V3);
try {
//2015/05月已停止使用
// mSpreadSheetService.setUserCredentials("xxx@gmail.com", "密碼");
HttpTransport httpTransport = new NetHttpTransport();
JacksonFactory jsonFactory = new JacksonFactory();
ArrayList SCOPES = new ArrayList();
SCOPES.add("https://spreadsheets.google.com/feeds");
//SerServiceAcountId: clientID value should be similar to @developer.gserviceaccount.com They basically expect the email_address value from the console api credentials
GoogleCredential credential = new GoogleCredential.Builder().setTransport(httpTransport).setJsonFactory(jsonFactory)
.setServiceAccountId("填入剛才新增用戶端ID得到的Mail")
.setServiceAccountPrivateKeyFromP12File(getTempPkc12File())
.setServiceAccountUser("xxx@gmail.com")
.setServiceAccountScopes(SCOPES)
.build();
credential.refreshToken();
String accessToken = credential.getAccessToken();
Log.i(TAG, "accessToken: "+accessToken);
mSpreadSheetService.setOAuth2Credentials(credential);
URL url = new URL("https://spreadsheets.google.com/feeds/spreadsheets/private/full");
SpreadsheetFeed spreadFeed = mSpreadSheetService.getFeed(url, SpreadsheetFeed.class);
spreadsheets = spreadFeed.getEntries();</pre>
<br />
<h2>
<span style="color: #6aa84f; font-size: x-large;">三、結論</span></h2>
以上介紹了使用P12檔過Google OAuth2的方式。<br />
如果是Server則又有各自不同的登入方式,<br />
就交給大家去研究了。<br />
<br />
<h4>
<span style="color: #e69138; font-size: large;">相關文章</span></h4>
1. <a href="https://developers.google.com/identity/protocols/OAuth2ForDevices">官方OAuth2ForDevices</a><br />
2. <a href="https://developers.google.com/identity/protocols/OAuth2InstalledApp">官方OAuth2InstalledApp</a><br />
3. <a href="https://developers.google.com/maps-engine/documentation/oauth/installedapplication">官方OAuth2介紹</a><br />
4. <a href="https://developers.google.com/google-apps/spreadsheets/">使用試算表API</a><br />
<br />
<span style="color: white;">取得user-code</span><br />
<span style="color: white;">curl -d "client_id=855763605695-nn0a0tv8k83pqq28trtfckfhf8och19t.apps.googleusercontent.com&scope=https://spreadsheets.google.com/feeds" https://accounts.google.com/o/oauth2/device/code</span><br />
<span style="color: white;"><br /></span>
<span style="color: white;">RESPONSE:</span><br />
<span style="color: white;">{</span><br />
<span style="color: white;"> "device_code" : "LDBE-ZXPU4/wyoUadofXRCeslItAym9DJvQAt8fS8mFvlGUk9a5Ix4",</span><br />
<span style="color: white;"> "user_code" : "LDBE-ZXPU",</span><br />
<span style="color: white;"> "verification_url" : "https://www.google.com/device",</span><br />
<span style="color: white;"> "expires_in" : 1800,</span><br />
<span style="color: white;"> "interval" : 5</span><br />
<span style="color: white;">}</span><br />
<span style="color: white;">==============================</span><br />
<span style="color: white;">取得accessToken</span><br />
<span style="color: white;">curl -d "client_id=855763605695-nn0a0tv8k83pqq28trtfckfhf8och19t.apps.googleusercontent.com&client_secret=yz2SJO7DrYSxEwEYGA6xcjIl&code=LDBE-ZXPU4/wyoUadofXRCeslItAym9DJvQAt8fS8mFvlGUk9a5Ix4&grant_type=http://oauth.net/grant_type/device/1.0" https://www.googleapis.com/oauth2/v3/token</span><br />
<span style="color: white;"><br /></span>
<span style="color: white;"><br /></span>
<span style="color: white;">https://accounts.google.com/o/oauth2/auth?</span><br />
<span style="color: white;"> scope=email%20profile&</span><br />
<span style="color: white;"> redirect_uri=urn:ietf:wg:oauth:2.0:oob&</span><br />
<span style="color: white;"> response_type=code&</span><br />
<span style="color: white;"> client_id=855763605695-nn0a0tv8k83pqq28trtfckfhf8och19t.apps.googleusercontent.com</span>Anonymoushttp://www.blogger.com/profile/07460782835337903344noreply@blogger.com0tag:blogger.com,1999:blog-5753539950414636776.post-85337221590314580132015-06-02T01:32:00.003-07:002015-06-02T02:39:22.536-07:00[智能家居]用Arduino官方WiFi Shield的控制LED開關(使用Mac環境)<span style="color: #999999;"><span style="font-size: x-small;">文章攢寫時間︰2015/06/02 14:03</span></span><br />
<br />
<h2>
<span style="color: #6aa84f; font-size: x-large;"><b>一、前言</b></span></h2>
繼<a href="http://lp43.blogspot.tw/2015/05/wifi-shieldfirmwaremac.html">前一篇</a>更新完WiFi Shield的韌體(firmware v1.1.0)後,<br />
我們也因此進入一個<b><span style="color: purple; font-size: large;">天馬行空</span></b>的世界。<br />
能利用這塊板子做些什麼事呢?<br />
<br />
嗯⋯就從微智能家居「控制LED的開關」開始吧~<br />
<br />
<h2>
<b><span style="color: #6aa84f; font-size: x-large;">二、文章開始</span></b></h2>
<h3>
<span style="color: #3d85c6; font-size: large;">步驟1 準備需要的環境</span></h3>
這個專案需要使用到以下的玩具<br />
<ul>
<li>可連線上網的無線路由器AP</li>
<li>Arduino UNO</li>
<li>WiFi Shield</li>
<li>330Ω電阻(橙橙棕) x1</li>
<li>LED燈泡 x1</li>
<li>跳線 x2<br />
</li>
</ul>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzkizUV7ViuViO6f1fp7SPCwMJYMWyyxQ06ZkjiRHDqlTVTD2-CPoMjgznYaP1pf0harbOnqhgRFZvszBNEBqHLjLQhDBJbhvU_Xm-4OL0cbAl5_73ZL7lc7mV3boZ62et70rcymICXw/s1600/HomeAutomation+LED+Sketch_bb.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzkizUV7ViuViO6f1fp7SPCwMJYMWyyxQ06ZkjiRHDqlTVTD2-CPoMjgznYaP1pf0harbOnqhgRFZvszBNEBqHLjLQhDBJbhvU_Xm-4OL0cbAl5_73ZL7lc7mV3boZ62et70rcymICXw/s400/HomeAutomation+LED+Sketch_bb.png" width="338" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">麵包板示意圖</td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYQjYYAobPzq1TIzCCLZCk_aFivKUOEMXVr_Mp-PJNfag0wn83ZW2jB-GLtuUHg67nGtObOkF2Dvglxqkh3dRMv6_BBX2SUn9HllDhRof7wnE6-48uVK5uD5P2ODhLiOCeVKD-p22vAw/s1600/2015-06-02+15.58.50.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYQjYYAobPzq1TIzCCLZCk_aFivKUOEMXVr_Mp-PJNfag0wn83ZW2jB-GLtuUHg67nGtObOkF2Dvglxqkh3dRMv6_BBX2SUn9HllDhRof7wnE6-48uVK5uD5P2ODhLiOCeVKD-p22vAw/s640/2015-06-02+15.58.50.jpg" width="456" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">實際連接的樣子</td></tr>
</tbody></table>
<br />
<h3>
<span style="color: #3d85c6; font-size: large;">步驟2 下載需要的函式庫Webduino</span></h3>
這個專案需要使用到支援WiFi Shield的Webduino函式庫,<br />
我有針對Webduino Library稍做修改以支援WiFi Shield。<br />
<br />
請至<a href="https://github.com/lp43/Webduino">GitHub下載</a>並放在<br />
<span style="color: #6aa84f;">你的Arduino目錄\libraries底下</span><br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijjPyCvipYaRJhhsbYJnI8b2pzoQYGg4A9K_X02SgxVwMTGWXG60CET5MK-PUcmCje30aNp0DZCfwAuen6f4nF7OOD1R8vclscE7MHoPUp766rRGa3DGddVMe2dgsxxQRU1nlQJj-JRA/s1600/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2015-06-02+%25E4%25B8%258B%25E5%258D%25883.43.53.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="318" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijjPyCvipYaRJhhsbYJnI8b2pzoQYGg4A9K_X02SgxVwMTGWXG60CET5MK-PUcmCje30aNp0DZCfwAuen6f4nF7OOD1R8vclscE7MHoPUp766rRGa3DGddVMe2dgsxxQRU1nlQJj-JRA/s640/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2015-06-02+%25E4%25B8%258B%25E5%258D%25883.43.53.png" width="640" /></a></div>
<br />
<br />
不熟悉Git的人,<br />
可以<a href="https://github.com/lp43/Webduino/archive/master.zip">到此下載zip壓縮包</a>。<br />
<br />
<h3>
<span style="color: #3d85c6; font-size: large;">步驟3 複製程式碼並引用需要的函式庫</span></h3>
<br />
開啟一個全新的Arduino專案,<br />
將下方程式碼全數貼上。<br />
<pre class="brush: cpp; toolbar: false;">char ssid[] = "換成你的WiFi無線網路名稱";
char pass[] = "換成你的WiFi無線網路密碼";
int status = WL_IDLE_STATUS;
WebServer webserver("", 80);
P(homePage) =
"<!doctype html>"
"<html><head>Arduino微網站</title>"
"</head><body>"
"這是微網站首頁。"
"</body></html>";
P(lightPage) =
"<!doctype html>"
"<html><head>Arduino微網站</title>"
"</head><body>"
"這是燈泡控制專頁。"
"</body></html>";
P(Get_head) = "<h1>收到了GET</h1>";
P(Post_head) = "<h1>收到了POST</h1>";
P(Unknown_head) = "<h1>UNKNOWN request</h1>";
const byte LED_PIN = 9;//LED燈泡插在PIN腳9
/**預設的指令是印岀homePage的文案*/
void defaultCmd(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete)
{
server.httpSuccess();
Serial.println("httpSuccess");
if (type != WebServer::HEAD)
{
server.printP(homePage);
}
}
/**當收到燈泡控制的指令時*/
void lightCmd(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete)
{
server.httpSuccess();
Serial.println("httpSuccess");
if (type != WebServer::HEAD)
{
//檢查是傳來的是GET或是POST
switch (type)
{
case WebServer::GET:
server.printP(Get_head);
server.println(" ");
break;
case WebServer::POST:
server.printP(Post_head);
server.println(" ");
parsePost(server);
break;
default:
server.printP(Unknown_head);
}
server.printP(lightPage);
}
}
/**解析傳來的POST指令*/
void parsePost(WebServer &server) {
Serial.println("parsePost");
char key[16];
char value[16];
while (server.readPOSTparam(key, 16, value, 16)) {
//比對字元使用的是strcmp,當回傳值為0時代表字元符合
if (strcmp(key, "light") == 0) {
server.print("燈光已經");
if (strcmp(value, "on") == 0) {
digitalWrite(LED_PIN, HIGH);
server.print("打開。");
} else {
digitalWrite(LED_PIN, LOW);
server.print("關閉。");
}
}
}
}
void setup() {
pinMode(LED_PIN, OUTPUT);//初始化LED燈
Serial.begin(9600);//初始化序列埠至9600
while (!Serial) {
; // wait for serial port to connect. Needed for Leonardo only
}
// check for the presence of the shield:
if (WiFi.status() == WL_NO_SHIELD) {
Serial.println("WiFi shield not present");
// don't continue:
while (true);
}
// attempt to connect to Wifi network:
while ( status != WL_CONNECTED) {
Serial.print("Attempting to connect to SSID: ");
Serial.println(ssid);
// Connect to WPA/WPA2 network. Change this line if using open or WEP network:
status = WiFi.begin(ssid, pass);
// wait 5 seconds for connection:
delay(5000);
}
// 設定當外部連線連入Arduino時,相對應的處理函式
webserver.setDefaultCommand(&defaultCmd); // 處理「首頁」的請求
webserver.addCommand("light.html", &lightCmd); // 處理「燈泡控制頁」的請求
webserver.begin();
// you're connected now, so print out the status:
printWifiStatus();
}
void loop() {
webserver.processConnection();
}
/**印岀連線的WiFi狀態*/
void printWifiStatus() {
// print the SSID of the network you're attached to:
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
// print your WiFi shield's IP address:
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);
// print the received signal strength:
long rssi = WiFi.RSSI();
Serial.print("signal strength (RSSI):");
Serial.print(rssi);
Serial.println(" dBm");
}
</head></head></pre>
<br />
<h3>
<span style="color: #3d85c6; font-size: large;">步驟4 引用函式庫</span></h3>
這個專案需要用到WiFi、SPI和上面所述的Webduino共三個函式庫,<br />
請在Arduino IDE工具列中使用[草稿碼]→[include Library]來引用。<br />
<br />
<span style="color: #999999;">※剛才下載到libraries目錄底下的Webduino岀現在Contributed libraries裡了(見下圖)</span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgyJrdOicje6MHCC0mrvkCnwBIIr1IfItlmOTLY4YCPLftX_S_qAW5tZCbflRjlf2asS-kPgMxoiZrUMEMfU3iRyYp_pSahgRRgCqgy83yMuQFicKM9JIyaFccDX5IaiKY3anFCf9UfDQ/s1600/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2015-06-02+%25E4%25B8%258B%25E5%258D%25883.40.15.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="502" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgyJrdOicje6MHCC0mrvkCnwBIIr1IfItlmOTLY4YCPLftX_S_qAW5tZCbflRjlf2asS-kPgMxoiZrUMEMfU3iRyYp_pSahgRRgCqgy83yMuQFicKM9JIyaFccDX5IaiKY3anFCf9UfDQ/s640/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2015-06-02+%25E4%25B8%258B%25E5%258D%25883.40.15.png" width="640" /></a></div>
<br />
<h3>
<span style="color: #3d85c6; font-size: large;">步驟5 編譯並啟動</span></h3>
引用完所需要的函式庫以後,<br />
就可以驗證並上傳程式至Arduino UNO裡了。<br />
啟動序列埠監控視窗(Shift + Command + M),<br />
等待連線的ip位址陸出。<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGMwQFnZWfxN58T82kTAy5XEHf-Vhhk8K-KBUcgccwooRhcz19HBTx10Y5kR8qLBCxAC67wfdkfrApF84SyJjhoHvl14caEf0yQGuUsghcwn5Z7ppFx-0OHdZvffd0ScsvS9HhvHWDGA/s1600/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2015-06-02+%25E4%25B8%258B%25E5%258D%25884.06.45.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGMwQFnZWfxN58T82kTAy5XEHf-Vhhk8K-KBUcgccwooRhcz19HBTx10Y5kR8qLBCxAC67wfdkfrApF84SyJjhoHvl14caEf0yQGuUsghcwn5Z7ppFx-0OHdZvffd0ScsvS9HhvHWDGA/s400/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2015-06-02+%25E4%25B8%258B%25E5%258D%25884.06.45.png" width="385" /></a></div>
<br />
<h3>
<span style="color: #3d85c6; font-size: large;">步驟6 使用CocoaRestClient對WiFi Shield發岀POST連線</span></h3>
因為範例裡是使用POST的方式去發岀key為light和value為on/off的參數去控制LED燈,<br />
推薦使用<a href="http://mmattozzi.github.io/cocoa-rest-client/">CocoaRestClient</a>這套軟體去發岀POST請求。<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIJ3gSHRiPnosV3mNpDTbwETWqfDkwscyV2iKYqv4LDpVCYabkQ7ClYgpxxkQHvRPdSmxi6BUisHtAnIQTy5GuareFH88Y4flygNA98Ne4crdaqX4vreNgHPNgRca1Z4AFlXhJkv4rdg/s1600/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2015-06-02+%25E4%25B8%258B%25E5%258D%25884.05.56.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="542" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIJ3gSHRiPnosV3mNpDTbwETWqfDkwscyV2iKYqv4LDpVCYabkQ7ClYgpxxkQHvRPdSmxi6BUisHtAnIQTy5GuareFH88Y4flygNA98Ne4crdaqX4vreNgHPNgRca1Z4AFlXhJkv4rdg/s640/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2015-06-02+%25E4%25B8%258B%25E5%258D%25884.05.56.png" width="640" /></a></div>
<br />
<br />
接著就可以等著看結果囉!<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/P40Cqgte27I/0.jpg" frameborder="0" height="266" src="https://www.youtube.com/embed/P40Cqgte27I?feature=player_embedded" width="320"></iframe></div>
<br />
<br />
<b><span style="color: #6aa84f; font-size: x-large;">三、注意事項</span></b><br />
<a href="http://www.arduino.cc/en/Guide/ArduinoWiFiShield">Arduino官方</a>有提到WiFi Shield的以下數位PIN腳已被使用,<br />
串接LED燈時請不要再使用這些腳位了。<br />
<br />
數位PIN腳4 被用來當作SD卡的<a href="http://www.arduino.cc/en/Reference/SPI">Slave Select</a>(控制訊號)了。<br />
數位PIN腳7 被用來當作WiFi Shield和Arduino之間的握手協定(handshake)了。<br />
數位PIN腳10 被用來當作WiFi的<a href="http://www.arduino.cc/en/Reference/SPI">Slave Select</a>(控制訊號)了。<br />
數位PIN腳11, 12, 13被用來當作Arduino溝通的序列埠了。<br />
<br />
所以我們只能用數位PIN腳 3, 5, 6, 8, 9來控制LED燈。<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhk79W1Lkz2UF-BoPHI-JeQSNd3Sg5HTNSkHXj97raXJyQdIFkChtfvhmF1w7BVdrWDf5KB3cj8SNHQDL-u-FFBtjhv8QJ4hvjLACyn0P8hUFwJQZXegrleZGGiMYQ5eQ4HUS8yabQAQQ/s1600/wifi_shield3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhk79W1Lkz2UF-BoPHI-JeQSNd3Sg5HTNSkHXj97raXJyQdIFkChtfvhmF1w7BVdrWDf5KB3cj8SNHQDL-u-FFBtjhv8QJ4hvjLACyn0P8hUFwJQZXegrleZGGiMYQ5eQ4HUS8yabQAQQ/s400/wifi_shield3.png" width="341" /></a></div>
<br />
<h2>
<b><span style="color: #6aa84f; font-size: x-large;">四、結論</span></b></h2>
我們透過HTTP的POST方式將參數丟給Arduino WiFi Shield,<br />
當然我們也就可以用GET來丟參數。<br />
但因為Webduino這套第3方函式庫對於POST有較佳的程式支援,<br />
理所當然就使用POST來傳輸以控制LED燈的開關嚕!<br />
<br />
在成功使用WiFi控制LED燈後,<br />
接下來聰明的你還想玩些什麼呢?(笑)<br />
<br />
<h3>
<b><span style="color: #e69138; font-size: large;">相關連結</span></b></h3>
1. <a href="http://www.arduino.cc/en/Guide/ArduinoWiFiShield">Arduino官方網站</a><br />
2. <a href="http://lp43.blogspot.tw/2015/05/wifi-shieldfirmwaremac.html">更新WiFi Shield firmware</a>Anonymoushttp://www.blogger.com/profile/07460782835337903344noreply@blogger.com0tag:blogger.com,1999:blog-5753539950414636776.post-67196126073311827332015-05-29T01:23:00.000-07:002015-06-02T01:53:28.356-07:00更新Arduino官方WiFi Shield的firmware(使用Mac環境)<span style="color: #999999;"><span style="font-size: x-small;">文章攢寫時間︰2015/05/29 15:13</span></span><br />
<span style="color: #999999; font-size: x-small;">文章更新時間︰2015/05/31 18:04</span><br />
<span style="color: #999999;"><span style="font-size: x-small;">文章更新次數︰2</span></span>
<br />
<br />
<h2>
<span style="color: #6aa84f; font-size: x-large;"><b>一、前言</b></span></h2>
花了3千塊買了一塊官方的WiFi Shield板子,<br />
原以為好日子就此開始,<br />
疏不知用板子對外發岀request時,<br />
居然一直回應我<span style="color: red;">connected failed</span>.<br />
<br />
查了一下才知道因為原廠的firmware過舊(v.1.0.0),<br />
解決辦法有︰<br />
1.不更新韌體,繼續研用舊版v1.0.0搭配Arduino IDE 1.0.2對外連線(<a href="http://blog.kenyang.net/2013/09/arduino-wifi-shield.html">請參考Ken Yang部落格</a>)<br />
2.更新WiFi Shield的韌體讓最新版Arduino IDE 1.6.4能順利對外連線<br />
<br />
<span style="color: #999999; font-size: x-small;">查看韌體版本可透過以下語法</span><br />
<span style="color: #999999; font-size: x-small;">Serial.begin(9600);</span><br />
<span style="color: #999999; font-size: x-small;">Serial.println(WiFi.firmwareVersion());</span><br />
<span style="color: #999999; font-size: x-small;">記得include SPI.h 和 WiFi.h 兩個類別!</span><br />
<br />
這裡我就來實作更新韌體的部份!<br />
<br />
<span style="color: #6aa84f; font-size: x-large;"><b>二、文章開始</b></span><br />
依照<a href="http://www.arduino.cc/en/Hacking/WiFiShieldFirmwareUpgrading">官方的教學</a>的說明,<br />
在更新韌體進板子前,<br />
請<span style="color: red;"><b>務必將WiFi Shield從Arduino板子上拔除</b></span>!<br />
<br />
<h3>
<span style="color: #3d85c6; font-size: large;">步驟1 安裝MacPorts</span></h3>
MacPorts 是一個用於編譯、安裝與管理 Mac OS X 中各類開放原始碼軟體的工具,<br />
其由開放原始碼社群負責主導與開發,<br />
透過這個工具可以簡化安裝軟體的編譯工作,<br />
並且可以自動處理套件相依性問題,<br />
概念上很類似 Linux 的 apt 與 yum,<br />
只要下達一些簡單的指令就可以輕鬆安裝各種軟體。<br />
(以上解釋來至<a href="http://blogger.gtwang.org/2013/11/macports-mac-os-x-open-source.html">CTW論壇</a>)<br />
<br />
到<a href="http://www.macports.org/install.php#pkg">MacPorts官網</a>依照你的Mac OS版本下載相對應的安裝檔案。<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhF7wxw3snTcjIGztCqDbK6JJWmHuFvvPtQxO9BT00dtSqU_5omAqxUOVMsFuafIBWNh-sPplYnyLiNOoP4eFMmOBsy4YK2G2cN2kBOb3_BxPYiH_oUeqDaUq8UB6C0NK4RhPCyGm5OlQ/s1600/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2015-05-29+%25E4%25B8%258B%25E5%258D%25883.30.34.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="393" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhF7wxw3snTcjIGztCqDbK6JJWmHuFvvPtQxO9BT00dtSqU_5omAqxUOVMsFuafIBWNh-sPplYnyLiNOoP4eFMmOBsy4YK2G2cN2kBOb3_BxPYiH_oUeqDaUq8UB6C0NK4RhPCyGm5OlQ/s400/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2015-05-29+%25E4%25B8%258B%25E5%258D%25883.30.34.png" width="400" /></a></div>
<br />
<br />
<h3>
<span style="color: #3d85c6; font-size: large;">步驟2 更新MacPorts至最新版</span></h3>
進入終端機裡,輸入以下指令<br />
<pre class="brush: java; html-script: true; gutter :false;">$sudo port selfupdate</pre>
<br />
<br />
<h3>
<span style="color: #3d85c6; font-size: large;">步驟3 下載DFU Programmer</span></h3>
DFU(Device Firmware Update) Programmer這套軟體被Arduino專門用來更體韌體專用,<br />
因此,<br />
我們透過MacPorts將DFU Programmer軟體下載下來。<br />
語法是<br />
<pre class="brush: java; html-script: true; gutter :false;">$sudo port install dfu-programmer</pre>
<br />
下載下來後,請確保是最新版本。<br />
語法如下<br />
<pre class="brush: java; html-script: true; gutter :false;">$sudo port upgrade outdated</pre>
<br />
※請確認安裝完的DFU Programmer版本在v0.5.4(含)或之後。<br />
<br />
<h3>
<span style="color: #3d85c6; font-size: large;">步驟4 下載或在電腦中找岀最新的韌體檔</span></h3>
如果要下載最新版的WiFi軔體,可以到<a href="https://github.com/arduino/Arduino/tree/master/hardware/arduino/avr/firmwares">GitHub下載</a>(wifishield整個目錄便是)。<br />
或者,也會直接放在最新版的Arduino application裡。<br />
1.使用Finder開啟應用程式資料夾<br />
2.對Arduino.app點擊滑鼠右鍵選擇「顯示套件內容」<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqun14DZ_jsNWNu1fSrJ6gSBVirxfABQ_DqKNFdx5wH-3XWKHWDD_f0NNkqc4pHCq-67qEGU4CQ1jf3vYWpqGHeIy-1oOI4qYmykMWEo_-juU7zrhLD1YpQ4ubDibyGTrXikZ487Z8mQ/s1600/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2015-05-29+%25E4%25B8%258B%25E5%258D%25883.47.09.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="314" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqun14DZ_jsNWNu1fSrJ6gSBVirxfABQ_DqKNFdx5wH-3XWKHWDD_f0NNkqc4pHCq-67qEGU4CQ1jf3vYWpqGHeIy-1oOI4qYmykMWEo_-juU7zrhLD1YpQ4ubDibyGTrXikZ487Z8mQ/s640/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2015-05-29+%25E4%25B8%258B%25E5%258D%25883.47.09.png" width="640" /></a></div>
<br />
3.我的wifishield韌體資料夾路徑是在<br />
/Applications/Arduino/Arduino164.app/Contents/Java/hardware/arduino/avr/firmwares/wifishield/<br />
<br />
<h3>
<span style="color: #3d85c6; font-size: large;">步驟5 執行腳本檔以利燒錄韌體至板子中</span></h3>
官方有替Linux或Mac版本的使用者寫一套自動化安裝流程,<br />
語法的存放路徑在剛才目錄中的底下目錄scripts裡<br />
分別可看到有2個檔案<br />
ArduinoWifiShield_upgrade_mac.sh(Mac專用)<br />
或<br />
ArduinoWifiShield_upgrade.sh(Linux專用)<br />
<br />
因為腳本檔是用DFU-Programmer寫起來的,<br />
因此只要剛才DFU-Programmer在v0.5.4以後,<br />
就可以開始接下來的步驟!<br />
<br />
為了讓WiFi Shield進入programming mode,<br />
需使用Jumper將J3連結點串聯起來。<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpbhWAt5XXIAthh9yuNcV75E33F69EM_OkKsVtxGwNf50zuATRSCJ0dzIlWm09ImFeTc_fQ3KT4xCAZ4mhp3JtuYZnqLcfNo2VdMwh3Afn3dW4FyvHLXynBN1tZtpkRsbkCyHIsk-O-w/s1600/2015-05-29+16.06.59.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpbhWAt5XXIAthh9yuNcV75E33F69EM_OkKsVtxGwNf50zuATRSCJ0dzIlWm09ImFeTc_fQ3KT4xCAZ4mhp3JtuYZnqLcfNo2VdMwh3Afn3dW4FyvHLXynBN1tZtpkRsbkCyHIsk-O-w/s400/2015-05-29+16.06.59.jpg" width="380" /></a></div>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXFdXgt0tdnaljtfaVOiQ1dU1YjHkJERQe51uHbUKknHV2ME3VWy3n2-B90NR5tNS6xjqsvkpJ_yhMN7706Df6bN0nR-oeRwZc6VjOK262WeeMtD-eFCjxWm-Gb1Bu7SPG0fk-UULipQ/s1600/A000058_front_450.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="420" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXFdXgt0tdnaljtfaVOiQ1dU1YjHkJERQe51uHbUKknHV2ME3VWy3n2-B90NR5tNS6xjqsvkpJ_yhMN7706Df6bN0nR-oeRwZc6VjOK262WeeMtD-eFCjxWm-Gb1Bu7SPG0fk-UULipQ/s640/A000058_front_450.jpg" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">將Jumper拔起來並完整的接上2根排針以進入Programming mode模式</td></tr>
</tbody></table>
將已拔除Arduino板的WiFi Shield插入電腦USB,另一端插在板子的Mini USB孔上。<br />
<br />
接著在終端機裡執行剛才的腳本檔<br />
我下的語法如下<br />
<pre class="brush: java; html-script: true; gutter :false;">$sudo sh /Applications/Arduino/Arduino164.app/Contents/Java/hardware/arduino/avr/firmwares/wifishield/scripts/ArduinoWifiShield_upgrade_mac.sh -a /Applications/Arduino/Arduino164.app/Contents/Java -f shield</pre>
<br />
順利的話,就能看到韌體更新成功視窗了。
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjt_IylZJb0lb7b0m_RhPO5hyYG0YKUnfX7Hy36X2Yj_k9eLCZuCCJYSb1CG9jRIIeaI6mSn7f_K7K29Q1aqCT1Xjzg7H53zyal9-6E8F5q8nPkDgehoDA44P28QPtvYPm0iNZuIx545A/s1600/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2015-05-29+%25E4%25B8%258B%25E5%258D%25884.02.35.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="402" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjt_IylZJb0lb7b0m_RhPO5hyYG0YKUnfX7Hy36X2Yj_k9eLCZuCCJYSb1CG9jRIIeaI6mSn7f_K7K29Q1aqCT1Xjzg7H53zyal9-6E8F5q8nPkDgehoDA44P28QPtvYPm0iNZuIx545A/s640/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2015-05-29+%25E4%25B8%258B%25E5%258D%25884.02.35.png" width="640" /></a></div>
<br />
最後記得將Jumper拔掉並按下板子上的RESET鍵,<br />
整個韌體更新的動作就完成囉!<br />
<br />
查看一下,<br />
WiFi firmware的版本已更新至最新v1.1.0無誤。<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqkR1Oe05C_rzqQj7y2roYxFqKGZF-l2YyiAfKhJONACYwd8e-Ff_ulexMU74F7db4qBanF8mrCbH8Rvj4pXa8bJVfEv8lDFV9M7RCtrF8AHZ-aeFWOM_2qAaVKQpnIIxh7oF3KP8Wuw/s1600/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2015-05-29+%25E4%25B8%258B%25E5%258D%25884.09.54.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="231" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqkR1Oe05C_rzqQj7y2roYxFqKGZF-l2YyiAfKhJONACYwd8e-Ff_ulexMU74F7db4qBanF8mrCbH8Rvj4pXa8bJVfEv8lDFV9M7RCtrF8AHZ-aeFWOM_2qAaVKQpnIIxh7oF3KP8Wuw/s400/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2015-05-29+%25E4%25B8%258B%25E5%258D%25884.09.54.png" width="400" /></a></div>
<br />
<br />
使用<span style="color: #6aa84f;">client.connect(server, 80)</span>對Google發岀一次頁面request,<br />
也不會再遇到connect failed了!<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrI7v2gg5x_h5upyx35JARnBSE6fR6AG4rWudCMV7vbBlhWw2_gRaTd5oxnuk7l94nsz3OWNdVlhSF9hj-tMJJDgvf_44XGELoBrNdwBsLacxCNeB_rY1S6sVzqq3Ukv4EsRC3jpu_eA/s1600/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2015-05-29+%25E4%25B8%258B%25E5%258D%25884.13.10.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrI7v2gg5x_h5upyx35JARnBSE6fR6AG4rWudCMV7vbBlhWw2_gRaTd5oxnuk7l94nsz3OWNdVlhSF9hj-tMJJDgvf_44XGELoBrNdwBsLacxCNeB_rY1S6sVzqq3Ukv4EsRC3jpu_eA/s400/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2015-05-29+%25E4%25B8%258B%25E5%258D%25884.13.10.png" width="386" /></a></div>
<br />
<br />
<span style="color: #6aa84f; font-size: x-large;"><b>三、其它</b></span><br />
如果有使用<a href="https://code.google.com/p/webduino/">Webduino Library</a>,<br />
與Arduino IDE 1.6.4匹配的Webduino版本是1.7。<br />
<br />
<h2>
<b style="color: #6aa84f;"><span style="font-size: x-large;">四、結論</span></b></h2>
更新完WiFi Shield firmware後,<br />
接下來做些什麼呢?<br />
<br />
嗯⋯跟我一起玩<a href="http://lp43.blogspot.tw/2015/06/arduinowifi-shieldledmac.html">微智能家居</a>吧!<br />
<br />
<h3>
<b><span style="color: #e69138; font-size: large;">相關連結</span></b></h3>
1. <a href="http://blog.kenyang.net/2013/09/arduino-wifi-shield.html">Ken Yang的部落格</a><br />
2. <a href="http://www.arduino.cc/en/Hacking/WiFiShieldFirmwareUpgrading">Arduino官方</a><br />
3. <a href="http://katrinaeg.com/arduino-wifi-firmware-upgrade.html">國外一篇更新韌體的部落格教學</a>Anonymoushttp://www.blogger.com/profile/07460782835337903344noreply@blogger.com0tag:blogger.com,1999:blog-5753539950414636776.post-26711182065782762652015-05-21T20:15:00.002-07:002015-05-21T20:21:00.425-07:00使用者設計體驗筆記<span style="color: #999999;"><span style="font-size: x-small;">文章攢寫時間︰2015/05/22 11:08</span></span><br />
<br />
<span style="color: #38761d; font-size: large;"><b>如何在適當位置設計適合的功能</b></span><br />
先設計岀第1版的Layout雛型,<br />
並嚐試做紙本原型<span class="st">(Paper prototype) 的</span>使用者測試(User Testing)。<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/wafM7mnbZG4/0.jpg" frameborder="0" height="266" src="https://www.youtube.com/embed/wafM7mnbZG4?feature=player_embedded" width="320"></iframe></div>
<br />
測試中的使用者在做一件事前,<br />
先表達岀他們現在想做什麼功能。<br />
然後,<br />
他們會在他們覺得應該要有那個功能的地方,<br />
去找岀他們想要的功能。<br />
<br />
經過整理和收納,<br />
就會知道大部份的使用者會需要在什麼地方、存在什麼樣的功能。<br />
<br />
<b><span style="color: #38761d; font-size: large;">如何決定商品的樣貌</span></b><br />
多看市場上的APP,<br />
找到適合自己商品的Layout然後去改良。
<br />
<br />
<br />
<span style="color: orange;">相關文章</span><br />
<br />
1. <a href="http://scitechvista.nsc.gov.tw/zh-tw/Feature/C/0/13/10/1/1790.htm">使用者測試使你變身產品玩家,幫助找尋問</a><a href="http://scitechvista.nsc.gov.tw/zh-tw/Feature/C/0/13/10/1/1790.htm">題</a><a href="http://scitechvista.nsc.gov.tw/zh-tw/Feature/C/0/13/10/1/1790.htm">。</a><br />
2. <a href="http://ewpg.insight.ntu.edu.tw/200273898820108.html">INSIGHT臺大智活 - 如何製作原型(prototype)</a>Anonymoushttp://www.blogger.com/profile/07460782835337903344noreply@blogger.com0tag:blogger.com,1999:blog-5753539950414636776.post-30269833109402477532015-04-14T20:06:00.001-07:002015-05-29T00:06:17.099-07:00好的Design好的Design總是一個魔鬼,秘密藏在細節。<br />
<br />
底下就是一個淺顯易懂的例子。<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg72gSlO-m9dgtPf_PewZA4WsuvLP9jCwu6vwsU0k2nn9VJflEaoDJG8RZnxpj3EofX7CCKDEyltmZDhcG1E9Kmec6k7SvjsfRkRrf8DNVPavqNH23KEXV1LxYbj8jt9sxNXzDIpLEeww/s1600/2015-04-15+10.15.18.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="360" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg72gSlO-m9dgtPf_PewZA4WsuvLP9jCwu6vwsU0k2nn9VJflEaoDJG8RZnxpj3EofX7CCKDEyltmZDhcG1E9Kmec6k7SvjsfRkRrf8DNVPavqNH23KEXV1LxYbj8jt9sxNXzDIpLEeww/s1600/2015-04-15+10.15.18.jpg" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">大家都能岀充電器,<br />
但是Apple做到不占空間,<br />
且功能也都達到。</td></tr>
</tbody></table>
Anonymoushttp://www.blogger.com/profile/07460782835337903344noreply@blogger.com2tag:blogger.com,1999:blog-5753539950414636776.post-61083347313473710192015-01-27T19:48:00.003-08:002015-01-27T19:48:48.399-08:00使用adb列岀現在背景開啟中的Service$adb shell dumpsys activity servicesAnonymoushttp://www.blogger.com/profile/07460782835337903344noreply@blogger.com0tag:blogger.com,1999:blog-5753539950414636776.post-48307493413183271672015-01-06T20:09:00.004-08:002015-01-06T20:09:48.073-08:00Google試算表 自動翻譯功能<span style="color: #999999;"><span style="font-size: x-small;">文章攢寫時間︰2015/01/07 12:00</span></span>
<br />
<br />
在嚐試翻譯一些國外文案時,<br />
因為不想要<span style="font-weight: bold;"><span style="color: #008040;">一字一句</span></span>貼到Google做翻譯,<br />
於是找了一下能快速幫我完成翻譯的方法。<br />
<br />
後來發現到Google試算表只要<span style="font-weight: bold;"><span style="color: red;">直接在欄位裡下語法即可自動將文字翻成您要的語言</span></span>。<br />
<br />
不知道這個功能大家是否都知道了,<br />
分享給不知道且需要用到的人。<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdi-4hhRXZtZ3boGU9PTujTdrkeSyzhYY_RiVxTAr7j1TqCeOkaq2ETvmhOYEhBwOgSVVzut0nstExgHcWkznUFeExPegK8XYZOWeHvVX4i94dLnd1G2Z832PJU1gTOr8gDybSYfXQtQ/s1600/%E6%9C%AA%E5%91%BD%E5%90%8D-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdi-4hhRXZtZ3boGU9PTujTdrkeSyzhYY_RiVxTAr7j1TqCeOkaq2ETvmhOYEhBwOgSVVzut0nstExgHcWkznUFeExPegK8XYZOWeHvVX4i94dLnd1G2Z832PJU1gTOr8gDybSYfXQtQ/s1600/%E6%9C%AA%E5%91%BD%E5%90%8D-2.png" height="116" width="640" /></a></div>
<br />
目前知道的語系︰<br />
<table border="1" cellpadding="0" cellspacing="0" dir="ltr" style="border-collapse: collapse; border: 1px solid #ccc; font-family: arial,sans,sans-serif; font-size: 13px; table-layout: fixed;"><colgroup><col width="53"></col><col width="89"></col></colgroup><tbody>
<tr style="height: 21px;"><td data-sheets-value="[null,2,"en"]" style="background-color: white; border: 1px solid rgb(0, 0, 0); color: #444444; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">en</td><td data-sheets-value="[null,2,"\u82f1\u6587"]" style="background-color: white; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(0, 0, 0); border-top-style: solid; border-top-width: 1px; color: #444444; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">英文</td></tr>
<tr style="height: 21px;"><td data-sheets-value="[null,2,"ko"]" style="background-color: white; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(0, 0, 0); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; color: #444444; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">ko</td><td data-sheets-value="[null,2,"\u97d3\u6587"]" style="background-color: white; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; color: #444444; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">韓文</td></tr>
<tr style="height: 21px;"><td data-sheets-value="[null,2,"auto"]" style="background-color: white; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(0, 0, 0); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; color: #444444; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">auto</td><td data-sheets-value="[null,2,"\u81ea\u52d5\u5075\u6e2c\u8a9e\u8a00"]" style="background-color: white; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; color: #444444; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">自動偵測語言</td></tr>
<tr style="height: 21px;"><td data-sheets-value="[null,2,"ja"]" style="background-color: white; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(0, 0, 0); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; color: #444444; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">ja</td><td data-sheets-value="[null,2,"\u65e5\u6587"]" style="background-color: white; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; color: #444444; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">日文</td></tr>
<tr style="height: 21px;"><td data-sheets-value="[null,2,"zh-tw"]" style="background-color: white; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(0, 0, 0); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; color: #444444; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">zh-tw</td><td data-sheets-value="[null,2,"\u7e41\u9ad4"]" style="background-color: white; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; color: #444444; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">繁體</td></tr>
</tbody></table>
<br />
<h3>
<span style="color: #f6b26b; font-size: small;">參考網站</span></h3>
<a href="https://support.google.com/docs/answer/3093331?rd=1#">Google
文件編輯器官網</a>Anonymoushttp://www.blogger.com/profile/07460782835337903344noreply@blogger.com0tag:blogger.com,1999:blog-5753539950414636776.post-28299827602621193702014-12-31T01:27:00.003-08:002014-12-31T01:30:14.811-08:00好用的第3方套件與相對proguard腳本<span style="color: #999999;"><span style="font-size: x-small;">文章攢寫時間︰2014/12/31 16:40</span></span>
<br />
<br />
<h3>
<span style="color: #3d85c6; font-size: large;">Dropbox</span></h3>
官方的套件。<br />
透過該套件可以輕鬆的與Dropbox將資料上傳或下載。<br />
<br />
需要套件<br />
●dropbox-android-sdk-1.6.jar<br />
●json_simple-1.1.jar<br />
<br />
<h3>
<b><span style="color: #3d85c6; font-size: large;">Listviewanimations</span></b> </h3>
<a href="https://github.com/nhaarman/ListViewAnimations">ListviewAnimations</a>這個套件優化了ListView,<br />
讓其list item可以上下拖曳,<br />
甚至結合了眾多第3方套件,<br />
讓UX使用體驗更順暢。<br />
<br />
需要套件(在app/libs/底下直接貼上jar檔並對該jar檔按右鍵選擇add as Library)<br />
●listviewanimations_lib-core-slh_3.1.0.jar<br />
●listviewanimations_lib-core_3.1.0.jar<br />
●listviewanimations_lib-manipulation_3.1.0.jar<br />
●nineoldandroids_library.jar<br />
<br />
build.gradle檔需加上<br />
dependencies {<br />
compile 'se.emilsjolander:stickylistheaders:2.5.2'<br />
}<br />
proguard腳本需加上<br />
-keep class com.nhaarman.listviewanimations.** {*; }<br />
<br />
<h3>
<span style="color: #3d85c6; font-size: large;">Volley</span></h3>
Volley是<a href="http://developer.android.com/training/volley/index.html">Google提供的套件</a>,<br />
目的讓Android App更方便、快速的使用HTTP連線。<br />
<br />
此外,<br />
Volley還提供cache機制,<br />
倘若重覆請求連線,<br />
亦提供一個更快速顯示資料的可行方案,<br />
如果頁面失去焦點,<br />
Volley也能確保資料不會回去頁面造成相關的crash。<br />
<br />
需要套件<br />
●volley-sources.jar<br />
<br />
<h3>
<span style="color: #3d85c6; font-size: large;">UIL (Universal Image Loader)</span></h3>
<a href="https://github.com/nostra13/Android-Universal-Image-Loader">UIL</a>替Android開發者省下大量處理圖片記憶體洩漏可能發生的機會,<br />
並提供各種cache和存放方式,<br />
讓圖片大量下載更加輕鬆。<br />
<br />
需要套件<br />
●universal-image-loader-1.9.3.jar<br />
<br />
或在Android Studio專案app/build.gradle檔裡加上<br />
dependencies {<br />
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.3'<br />
}<br />
直接從線上Download該套件並使用。<br />
<br />
<h3>
<b><span style="color: #3d85c6; font-size: large;">Glide</span></b></h3>
<br />
需要套件<br />
●glide-3.4.0.jar<br />
●glide-3.4.0-javadoc.jar<br />
●glide-okhttp-integration-1.1.0.jar<br />
●glide-volley-integration-1.1.0.jar<br />
proguard腳本加上<br />
-keep class com.bumptech.glide.integration.okhttp.** {*; }<br />
-dontwarn com.bumptech.glide.integration.okhttp.**<br />
<br />Anonymoushttp://www.blogger.com/profile/07460782835337903344noreply@blogger.com1tag:blogger.com,1999:blog-5753539950414636776.post-18326055349234746912014-12-28T20:42:00.000-08:002014-12-28T20:42:09.273-08:00[Android Studio] 替Jar檔添加Jovadoc文件讓寫Code更方便<span style="color: #999999;"><span style="font-size: x-small;">文章攢寫時間︰2014/12/29 12:12</span></span>
<br />
<br />
<h2>
<span style="color: #6aa84f; font-size: x-large;">一、前言</span></h2>
在下載第3方套件(jar檔)時,<br />
常會看到除了xxx.jar檔外,<br />
還有xxx-javadoc.jar、xxx-sources.jar,<br />
這些jar檔其實是讓你在寫程式時,<br />
可以看的到jar的說明和用法(更詳細的解說請參考<a href="http://www.gtwang.org/2013/05/eclipse-java-jar-source-javadoc.html">GTW部落格</a>)。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVLz4oojxal-0Zr29c1iA_RNLwdAW-yBbG06q0TQhnI60B3zRfs7UrLRFvWV7rlH2nK-sOzCpN2nTVImR5-Hl1jGL3Tvumi6zN-zMKs4pYz9yOHG7Nlo-vteSFW2rioR7d_C7RS1RCsw/s1600/%E8%9E%A2%E5%B9%95%E5%BF%AB%E7%85%A7+2014-12-29+%E4%B8%8B%E5%8D%8812.16.34.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVLz4oojxal-0Zr29c1iA_RNLwdAW-yBbG06q0TQhnI60B3zRfs7UrLRFvWV7rlH2nK-sOzCpN2nTVImR5-Hl1jGL3Tvumi6zN-zMKs4pYz9yOHG7Nlo-vteSFW2rioR7d_C7RS1RCsw/s1600/%E8%9E%A2%E5%B9%95%E5%BF%AB%E7%85%A7+2014-12-29+%E4%B8%8B%E5%8D%8812.16.34.png" height="284" width="640" /></a></div>
<br />
<h2>
<span style="color: #6aa84f; font-size: x-large;">二、文章開始</span></h2>
在Android Studio要怎麼樣替jar檔添加javadoc呢?<br />
<br />
<h3>
<span style="color: #3d85c6; font-size: large;">步驟1</span></h3>
假設我們已經在用xxx.jar檔,<br />
為了能看到jar檔的說明文件,<br />
我們額外將xxx-source.jar也放進libs資料夾。<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbTL5cWPgrGwY_f9u9tR1N-nRvBuVSEB4z1gQcJSXtY1T7TxmJ8SovcwxcAvJ5lh5jvUxtxAWd8i18xc1LAK140C9RVIXWeAIbPtQV9p-9w4XIRcRGaU1cNlzU34ebxBHRGN0cqGKefQ/s1600/%E8%9E%A2%E5%B9%95%E5%BF%AB%E7%85%A7+2014-12-29+%E4%B8%8B%E5%8D%8812.19.51.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbTL5cWPgrGwY_f9u9tR1N-nRvBuVSEB4z1gQcJSXtY1T7TxmJ8SovcwxcAvJ5lh5jvUxtxAWd8i18xc1LAK140C9RVIXWeAIbPtQV9p-9w4XIRcRGaU1cNlzU34ebxBHRGN0cqGKefQ/s1600/%E8%9E%A2%E5%B9%95%E5%BF%AB%E7%85%A7+2014-12-29+%E4%B8%8B%E5%8D%8812.19.51.png" height="640" width="400" /></a></div>
<br />
<h3>
<span style="color: #3d85c6; font-size: large;">步驟2</span></h3>
在您想看的函式或Class位置(譬如我現在想看在jar檔裡TimeObserver這個類別是幹嘛用的),<br />
點擊<b><span style="color: red;">Ctrl(Windows環境) / Command(Mac環境)+滑鼠左鍵</span></b>,<br />
此時會看到類似以下的畫面。<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivSYyPzBwKFnIchnQBMTwBmWpGAkUyyty-2iX3C4Jx1wbtQGUGwFYIiNI-GH_A2XXsJM4ePMhkqGKYayMM77cgFGGEp6-X1VU17v0dLqXM76ZNQuri9pS0LPyK7pcm2Vejx-ttyccx7g/s1600/%E8%9E%A2%E5%B9%95%E5%BF%AB%E7%85%A7+2014-12-29+%E4%B8%8B%E5%8D%8812.25.52.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivSYyPzBwKFnIchnQBMTwBmWpGAkUyyty-2iX3C4Jx1wbtQGUGwFYIiNI-GH_A2XXsJM4ePMhkqGKYayMM77cgFGGEp6-X1VU17v0dLqXM76ZNQuri9pS0LPyK7pcm2Vejx-ttyccx7g/s1600/%E8%9E%A2%E5%B9%95%E5%BF%AB%E7%85%A7+2014-12-29+%E4%B8%8B%E5%8D%8812.25.52.png" height="338" width="640" /></a></div>
在未與javadoc連結時,<br />
看到的都是封裝的程式源碼。<br />
<br />
<h3>
<span style="color: #3d85c6; font-size: large;">步驟3</span></h3>
1.點擊右上角Attach Sources...<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlgwLU-7grq8gttkaMQpjW6alA2VrLHQ5u94_DPNnwfGD4i2NoKAePnJ4ajfY0svkiWpiZ5ZOLraK_QbpOZhEj7khg8arAD_YjCueKg7RdDEwMLOJjc7WypF7a8L-r4cQIrtiIwS0d5w/s1600/%E8%9E%A2%E5%B9%95%E5%BF%AB%E7%85%A7+2014-12-29+%E4%B8%8B%E5%8D%8812.28.01.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlgwLU-7grq8gttkaMQpjW6alA2VrLHQ5u94_DPNnwfGD4i2NoKAePnJ4ajfY0svkiWpiZ5ZOLraK_QbpOZhEj7khg8arAD_YjCueKg7RdDEwMLOJjc7WypF7a8L-r4cQIrtiIwS0d5w/s1600/%E8%9E%A2%E5%B9%95%E5%BF%AB%E7%85%A7+2014-12-29+%E4%B8%8B%E5%8D%8812.28.01.png" height="115" width="200" /></a></div>
2.選擇剛才加入的xxx.sources.jar檔<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEs2PopKXpfCz3SCy0RjNWdaOlLPzosnUDGw4BrufjDs0Wb5TMsJGCaB13VgMdK9Wyil6DtnUPEH-4cirbx3sGFUxIlikX6hyw4wMJQ1_ncQ2fYETCI__OWJM2SaELa1qWBocvNvugnw/s1600/%E8%9E%A2%E5%B9%95%E5%BF%AB%E7%85%A7+2014-12-29+%E4%B8%8B%E5%8D%8812.29.26.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEs2PopKXpfCz3SCy0RjNWdaOlLPzosnUDGw4BrufjDs0Wb5TMsJGCaB13VgMdK9Wyil6DtnUPEH-4cirbx3sGFUxIlikX6hyw4wMJQ1_ncQ2fYETCI__OWJM2SaELa1qWBocvNvugnw/s1600/%E8%9E%A2%E5%B9%95%E5%BF%AB%E7%85%A7+2014-12-29+%E4%B8%8B%E5%8D%8812.29.26.png" height="400" width="367" /></a></div>
<br />
此時可以看到,<br />
jar檔的源始碼和說明文件通通都顯示岀來了。<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUbJMrR85DcGGmFyLN9B1tn6Vz7gyPpkVcIeTCNdYNcCWgueiq4EX_1-PfB0ucVTP5VQbvl9A_uZqrrit1QIeTFVx-rlf9HB-NjHpixE2Rfl66qtytFVYTwjfNBiJMOtA0Vqp_wKueUg/s1600/%E8%9E%A2%E5%B9%95%E5%BF%AB%E7%85%A7+2014-12-29+%E4%B8%8B%E5%8D%8812.31.15.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUbJMrR85DcGGmFyLN9B1tn6Vz7gyPpkVcIeTCNdYNcCWgueiq4EX_1-PfB0ucVTP5VQbvl9A_uZqrrit1QIeTFVx-rlf9HB-NjHpixE2Rfl66qtytFVYTwjfNBiJMOtA0Vqp_wKueUg/s1600/%E8%9E%A2%E5%B9%95%E5%BF%AB%E7%85%A7+2014-12-29+%E4%B8%8B%E5%8D%8812.31.15.png" height="520" width="640" /></a></div>
<br />
滑鼠移到類別名稱或函式上,<br />
也會自動跳岀說明。<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnNrm8bUFt_4BoZmZ8XYh4MBgZY4o9rkOgeFeeH32gNneNS0kUkpYH8VW2NE0kYhycRLnAB9XwRuQToc4JIFmQ4TJFejOVImAloZpX5SOqTw8-3Zuk7nfSvMDMTR170uLIMA8QZXBKGQ/s1600/%E8%9E%A2%E5%B9%95%E5%BF%AB%E7%85%A7+2014-12-29+%E4%B8%8B%E5%8D%8812.32.51.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnNrm8bUFt_4BoZmZ8XYh4MBgZY4o9rkOgeFeeH32gNneNS0kUkpYH8VW2NE0kYhycRLnAB9XwRuQToc4JIFmQ4TJFejOVImAloZpX5SOqTw8-3Zuk7nfSvMDMTR170uLIMA8QZXBKGQ/s1600/%E8%9E%A2%E5%B9%95%E5%BF%AB%E7%85%A7+2014-12-29+%E4%B8%8B%E5%8D%8812.32.51.png" height="218" width="640" /></a></div>
<br />
如果沒有跳岀說明視窗,到<br />
[Android Studio]-->[Preferences]-->[Editor]-->將[Show quick doc on mouse move Delay(ms)]打勾-->[Apply]<br />
就會看到說明視窗囉!<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYTsDMjQKGTiYbScIRZA6mYDYIKQS0P91CQUBxuekpD7K0noGbJuHX7SWwkB0YJe7sixYHyAdxqS4ojbU3aQwbgB33ZSAKGqgcyXMta6BJNH4F9hsR_o5mLJnl89_07yXuHquNDdMCjA/s1600/%E8%9E%A2%E5%B9%95%E5%BF%AB%E7%85%A7+2014-12-29+%E4%B8%8B%E5%8D%8812.34.49.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYTsDMjQKGTiYbScIRZA6mYDYIKQS0P91CQUBxuekpD7K0noGbJuHX7SWwkB0YJe7sixYHyAdxqS4ojbU3aQwbgB33ZSAKGqgcyXMta6BJNH4F9hsR_o5mLJnl89_07yXuHquNDdMCjA/s1600/%E8%9E%A2%E5%B9%95%E5%BF%AB%E7%85%A7+2014-12-29+%E4%B8%8B%E5%8D%8812.34.49.png" height="438" width="640" /></a></div>
<br />
<h4>
<span style="color: #e69138;">參考來源</span></h4>
1. <a href="http://www.gtwang.org/2013/05/eclipse-java-jar-source-javadoc.html">GTW</a><br />
2. <a href="http://stackoverflow.com/questions/20994336/android-studio-how-to-attach-javadoc">stackoverflow</a>Anonymoushttp://www.blogger.com/profile/07460782835337903344noreply@blogger.com2tag:blogger.com,1999:blog-5753539950414636776.post-30999236933387129822014-12-15T20:24:00.005-08:002014-12-15T20:24:51.926-08:00unsupported Gradle DSL method found: 'instrumentTestCompile()<span style="color: #999999;"><span style="font-size: x-small;">文章攢寫時間︰2014/12/16 12:30</span></span>
<br />
<br />
<h2>
<b><span style="color: #6aa84f; font-size: x-large;">一、問題</span></b></h2>
在編譯Espresso時遇到以下錯誤<br />
Error:(46, 0) Build script error, unsupported Gradle DSL method found: 'instrumentTestCompile()'!<br />
<br />
<h2>
<span style="color: #6aa84f; font-size: x-large;">二、解決辦法</span></h2>
<strong>instrumentTestCompile()</strong> 該函式在最新版Gradle plugin已被更名成<strong>androidTestCompile()。</strong>Anonymoushttp://www.blogger.com/profile/07460782835337903344noreply@blogger.com0tag:blogger.com,1999:blog-5753539950414636776.post-38118785008233750222014-12-15T19:49:00.005-08:002014-12-15T19:49:59.544-08:00使用Gradle時遇到 Failed to apply plugin [id 'com.android.application'] > Gradle version 1.10 is required. Current version is 2.x.<span style="color: #999999;"><span style="font-size: x-small;">文章攢寫時間︰2014/12/16 11:40</span></span><br />
<span style="color: #999999;"><span style="font-size: x-small;">文章參考來源︰<a href="http://stackoverflow.com/questions/24289410/gradle-version-1-10-is-required-current-version-is-2-0">StackOverFlow</a></span></span><br />
<h2>
<span style="color: #6aa84f; font-size: x-large;">一、問題</span></h2>
在使用Espresso時,輸入指令 <em class="markup--em markup--li-em">$gradle connectedInstrumentTest </em>時遇到以下問題<br />
<em class="markup--em markup--li-em"><br /></em>
Failed to apply plugin [id 'com.android.application']<br />
> Gradle version 1.10 is required. Current version is 2.2. If using the gradle wrapper, try editing the distributionUrl in /Users/xxxx/AndroidStudioProjects/xxxxx/gradle/wrapper/gradle-wrapper.properties to gradle-1.10-all.zip<br />
<br />
<h2>
<span style="color: #6aa84f; font-size: x-large;">二、解決辦法</span></h2>
<div class="post-text" itemprop="text">
<h3>
<strong><span style="color: #3d85c6; font-size: large;">訊息:</span></strong></h3>
"Gradle version 1.10 is required. Current version is 2.0"<br />
<br />
<h3>
<strong><span style="color: #3d85c6; font-size: large;">發生時機:</span></strong></h3>
嚐試編譯需要Gradle 1.10版本的Android專案時,發現到專案正在使用Gradle 2.0版。<br />
<br />
<h3>
<strong><span style="color: #3d85c6; font-size: large;">解決辦法:</span></strong></h3>
使用 Gradle Wrapper<br />
<br />
<h3>
<strong><span style="color: #3d85c6; font-size: large;">步驟:</span></strong></h3>
<ol>
<li>確認 在Android專案裡的<strong><span style="font-weight: normal;">Gradle-wrapper properties檔中,</span>distributionUrl</strong> 指定成 <strong>gradle-1.10-all.zip</strong> ,檔案路徑位址為:<br />
<blockquote>
<em>MyAndroidProject/gradle/wrapper/gradle-wrapper.properties</em></blockquote>
</li>
<li>在專案的最頂層目錄結構(擁有可執行檔的那一層,i.e. <strong>gradlew</strong> and <strong>gradlew.bat</strong>)執行Run Gradle Wrapper command。<br /><br />
For <strong>Unix-like</strong> OS:<br />
<blockquote>
<em>./gradlew wrapper</em></blockquote>
For <strong>Windows</strong> OS:<br />
<blockquote>
<em>gradlew.bat wrapper</em></blockquote>
</li>
<li>執行Gradle的編譯指令 Gradle Wrapper.<br />
For <strong>Unix-like</strong> OS:<br />
<blockquote>
<em>./gradlew build</em></blockquote>
For <strong>Windows</strong> OS:<br />
<blockquote>
<em>gradlew.bat build</em></blockquote>
</li>
</ol>
</div>
Anonymoushttp://www.blogger.com/profile/07460782835337903344noreply@blogger.com0tag:blogger.com,1999:blog-5753539950414636776.post-36997032053589958352014-12-10T23:39:00.000-08:002014-12-11T00:23:50.509-08:00使用adb模擬手機觸控事件<span style="color: #999999;"><span style="font-size: x-small;">文章攢寫時間︰2014/12/11 15:30</span></span><br />
<br />
<h3>
<span style="color: #3d85c6; font-size: large;">滑動你的手機</span></h3>
<br />
<pre class="brush: java; html-script: true; gutter: false;">$adb shell input touchscreen swipe 0 800 0 100 1000</pre>
<br />
解說:我的手機是Note3,螢幕解析度為寬(x)為1080,高(y)為1920,<br />
這行指令是告訴手機要從x1(左)-->0滑到x2(右)-->0,y要從y1(下)-->800滑到y2(上)-->100<br />
以1000(ms)的速度滑動。<br />
<br />
<h3>
<span style="color: #3d85c6; font-size: large;">點擊你的手機</span></h3>
<pre class="brush: java; html-script: true; gutter: false;">$adb shell input touchscreen tap 1079 1919</pre>
<br />
解說:我的手機是Note3,螢幕解析度為寬(x)為1080,高(y)為1920,<br />
這行指令是告訴手機我要點擊手機螢幕在x-->1079和y-->1919,也就是螢幕最右下角的點。
<br />
<br />
<h3>
<span style="color: #3d85c6; font-size: large;">腳本暫停</span></h3>
<pre class="brush: java; html-script: true; gutter: false;">$adb shell sleep 1</pre>
解說:跟adb表示需要停止1秒的時間再繼續執行觸控腳本。Anonymoushttp://www.blogger.com/profile/07460782835337903344noreply@blogger.com0tag:blogger.com,1999:blog-5753539950414636776.post-58547856273021475702014-11-16T19:34:00.000-08:002018-03-15T20:19:16.656-07:00Android Studio一些實用的快速鍵(Mac版)<span style="color: #999999;"><span style="font-size: x-small;">文章攢寫時間︰2014/11/17 11:30</span></span><br />
<span style="color: #999999;"><span style="font-size: x-small;">文章修改時間︰2014/11/18 10:30</span></span><br />
<span style="color: #999999;"><span style="font-size: x-small;">文章修改次數︰4</span></span><br />
<br />
<table border="1" cellpadding="0" cellspacing="0" dir="ltr" style="border-collapse: collapse; border: 1px solid #ccc; font-family: arial,sans,sans-serif; font-size: 13px; table-layout: fixed;"><colgroup><col width="153"></col><col width="284"></col></colgroup><tbody>
<tr style="height: 21px;"><td data-sheets-value="[null,2,"\u529f\u80fd"]" style="background-color: #d9ead3; border: 1px solid rgb(0, 0, 0); font-size: 100%; font-weight: bold; padding: 2px 3px; vertical-align: bottom;">功能</td><td data-sheets-value="[null,2,"\u5feb\u901f\u9375"]" style="background-color: #d9ead3; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(0, 0, 0); border-top-style: solid; border-top-width: 1px; font-size: 100%; font-weight: bold; padding: 2px 3px; vertical-align: bottom;">快速鍵</td></tr>
<tr style="height: 21px;"><td data-sheets-value="[null,2,"\u627e\u5c80\u4f60\u8981\u7684\u51fd\u5f0f\u6216Class"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(0, 0, 0); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">找岀你要的函式或Class</td><td data-sheets-value="[null,2,"\u9023\u63092\u6b21Shift"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">連按2次Shift</td></tr>
<tr style="height: 21px;"><td data-sheets-value="[null,2,"\u56de\u5230\u4e0a\u4e00\u500b\u6aa2\u8996"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(0, 0, 0); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">回到上一個檢視</td><td data-sheets-value="[null,2,"Command+["]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">Command+[</td></tr>
<tr style="height: 21px;"><td data-sheets-value="[null,2,"\u56de\u5230\u4e0b\u4e00\u500b\u6aa2\u8996"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(0, 0, 0); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">回到下一個檢視</td><td data-sheets-value="[null,2,"Command+]"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">Command+]</td></tr>
<tr style="height: 21px;"><td data-sheets-value="[null,2,"\u5feb\u901f\u52a0\u4e0aIf/For/While/try-catch"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(0, 0, 0); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">快速加上If/For/While/try-catch</td><td data-sheets-value="[null,2,"\u9078\u64c7\u4f60\u8981\u505a\u4e8b\u7684\u7a0b\u5f0f\u5340\u584a\uff0c\u6309\u4e0bCommand+option+T"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">選擇你要做事的程式區塊,按下Command+option+T</td></tr>
<tr style="height: 21px;"><td data-sheets-value="[null,2,"\u5feb\u901f\u88fd\u4f5cString s"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(0, 0, 0); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">快速製作String s</td><td data-sheets-value="[null,2,"\u9078\u53d6\u6b32\u88fd\u4f5c\u7684String\u5340\u584a\uff0c\u6309Command+option+V"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">選取欲製作的String區塊,按Command+option+V</td></tr>
<tr style="height: 21px;"><td data-sheets-value="[null,2,"\u5217\u51fa\u5b57\u5143\u7576\u4e0b\u53ef\u4f7f\u7528\u7684\u7a0b\u5f0f"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(0, 0, 0); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">叫回當下可使用的函式列表</td><td data-sheets-value="[null,2,"Command+Space"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">函式名稱打到一半時,按Command+Space</td></tr>
<tr style="height: 21px;"><td data-sheets-value="[null,2,"\u52a0\u5165\u8a3b\u89e3"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(0, 0, 0); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">加入註解</td><td data-sheets-value="[null,2,"Commnad+/"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">Commnad+/</td></tr>
<tr style="height: 21px;"><td data-sheets-value="[null,2,"\u52a0\u5165\u5340\u584a\u8a3b\u89e3"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(0, 0, 0); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">加入區塊註解</td><td data-sheets-value="[null,2,"\u9078\u53d6\u7a0b\u5f0f\u5340\u584a\uff0c\u4e26\u6309\u4e0bCommand+option+/"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">選取程式區塊,並按下Command+option+/</td></tr>
<tr style="height: 21px;"><td data-sheets-value="[null,2,"\u91cd\u65b0\u6392\u5217\u7a0b\u5f0f"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(0, 0, 0); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">重新排列程式</td><td data-sheets-value="[null,2,"Command+option+L"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">Command+option+L</td></tr>
<tr style="height: 21px;"><td data-sheets-value="[null,2,"\u5217\u5c80\u5b57\u5143\u7576\u4e0b\u9700\u8981\u7684\u53c3\u6578\u5217\u8868"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(0, 0, 0); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">列岀字元當下需要的參數列表</td><td data-sheets-value="[null,2,"\u5728\u586b\u5165\u51fd\u5f0f\u7684\u53c3\u6578\u7576\u4e0b\uff0c\u6309\u4e0bCommand+P"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">在填入函式的參數當下,按下Command+P</td></tr>
<tr style="height: 21px;"><td data-sheets-value="[null,2,"\u5feb\u901f\u7522\u751f\u51fd\u5f0f"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(0, 0, 0); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">快速產生函式</td><td data-sheets-value="[null,2,"Command+N"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">Command+N</td></tr>
<tr style="height: 21px;"><td data-sheets-value="[null,2,"\u5feb\u901f\u522a\u6389\u7576\u884c\u7a0b\u5f0f"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(0, 0, 0); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">快速刪掉當行程式</td><td data-sheets-value="[null,2,"Command+delete"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">Command+delete</td></tr>
<tr style="height: 21px;"><td data-sheets-value="[null,2,"\u4e00\u6b21\u66f4\u6539\u591a\u884c\u7684\u7279\u5b9a\u4f4d\u5143"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(0, 0, 0); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">一次更改多行的特定位元</td><td data-sheets-value="[null,2,"\u6309\u4e0boption\uff0c\u6ed1\u9f20\u5f80\u4e0b\u62c9\uff0c\u518d\u5411\u53f3\u62d6\u66f3\u5230\u4f60\u8981\u7684\u4f4d\u7f6e\u5373\u53ef\u591a\u884c\u7279\u5b9a\u5b57\u5143\u5168\u9078\u3002"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">按下option,滑鼠往下拉,再向右拖曳到你要的位置即可多行特定字元全選。</td></tr>
<tr style="height: 21px;"><td data-sheets-value="[null,2,"\u627e\u5c80\u4f60\u8981\u7684\u51fd\u5f0f\u6216Class"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(0, 0, 0); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">快速跳到指定行數</td><td data-sheets-value="[null,2,"\u9023\u63092\u6b21Shift"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">Command+L</td></tr>
<tr style="height: 21px;"><td data-sheets-value="[null,2,"\u627e\u5c80\u4f60\u8981\u7684\u51fd\u5f0f\u6216Class"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(0, 0, 0); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">立刻實作未撰寫的函式或值</td><td data-sheets-value="[null,2,"\u9023\u63092\u6b21Shift"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">在編輯頁面裡,打上一串你即將create的函式或值,此時Android Studio會辨識沒有此函式或值而變成紅色,按下option+return即會問你要宣告為函式還是值。</td></tr>
<tr style="height: 21px;"><td data-sheets-value="[null,2,"\u627e\u5c80\u4f60\u8981\u7684\u51fd\u5f0f\u6216Class"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(0, 0, 0); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">快速Override函式</td><td data-sheets-value="[null,2,"\u9023\u63092\u6b21Shift"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">control+O</td></tr>
<tr style="height: 21px;"><td data-sheets-value="[null,2,"\u627e\u5c80\u4f60\u8981\u7684\u51fd\u5f0f\u6216Class"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(0, 0, 0); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">將已輸入文字快速變成英文大寫或小寫</td><td data-sheets-value="[null,2,"\u9023\u63092\u6b21Shift"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">選取想要改變的文字,按下Command+shift+U</td></tr>
<tr style="height: 21px;"><td data-sheets-value="[null,2,"\u627e\u5c80\u4f60\u8981\u7684\u51fd\u5f0f\u6216Class"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(0, 0, 0); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">到上一個頁籤</td><td data-sheets-value="[null,2,"\u9023\u63092\u6b21Shift"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">Command+shift+[</td></tr>
<tr style="height: 21px;"><td data-sheets-value="[null,2,"\u627e\u5c80\u4f60\u8981\u7684\u51fd\u5f0f\u6216Class"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(0, 0, 0); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">到下一個頁籤</td><td data-sheets-value="[null,2,"\u9023\u63092\u6b21Shift"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">Command+shift+]</td></tr>
<tr style="height: 21px;"><td data-sheets-value="[null,2,"\u627e\u5c80\u4f60\u8981\u7684\u51fd\u5f0f\u6216Class"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(0, 0, 0); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">往前找關鍵字</td><td data-sheets-value="[null,2,"\u9023\u63092\u6b21Shift"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">選取想要搜尋的文字,按下Command+F,再按下Command+shift+G</td></tr>
<tr style="height: 21px;"><td data-sheets-value="[null,2,"\u627e\u5c80\u4f60\u8981\u7684\u51fd\u5f0f\u6216Class"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(0, 0, 0); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">往後找關鍵字</td><td data-sheets-value="[null,2,"\u9023\u63092\u6b21Shift"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">選取想要搜尋的文字,按下Command+F,再按下按下Command+G</td></tr>
<tr style="height: 21px;"><td data-sheets-value="[null,2,"\u8acb\u64c5\u7528Live Template"]" style="background-color: #fff2cc; border-bottom-color: rgb(0, 0, 0); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(0, 0, 0); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(0, 0, 0); border-right-style: solid; border-right-width: 1px; font-size: 100%; padding: 2px 3px; vertical-align: bottom;">請擅用Live Template</td><td data-sheets-value="[null,2,"https://www.youtube.com/watch?v=7L53tdusak4"]" style="background-color: #fff2cc; border-bottom: 1px solid rgb(0, 0, 0); border-right: 1px solid rgb(0, 0, 0); padding: 2px 3px; vertical-align: bottom;"><a class="in-cell-link" href="https://www.youtube.com/watch?v=7L53tdusak4" style="font-size: 100%; text-decoration-line: underline;" target="_blank">https://www.youtube.com/watch?v=7L53tdusak4</a><br /><br /><u>http://lp43.blogspot.tw/2018/03/live-template.html</u></td></tr>
</tbody></table>
<br />
<div>
以上哪個功能你最常用或者還想補充哪個功能?</div>
<div>
歡迎底下留言。</div>
Anonymoushttp://www.blogger.com/profile/07460782835337903344noreply@blogger.com0tag:blogger.com,1999:blog-5753539950414636776.post-14415167087189143952014-10-27T03:47:00.001-07:002015-06-11T00:00:15.839-07:00[Deprecated]使用Google Sheets API(Client-Login版)<span style="color: #999999;"><span style="font-size: x-small;">文章攢寫時間︰2014/10/27 18:30</span></span><br />
<span style="color: #999999; font-size: x-small;">文章修改時間︰2015/06/06 16:07</span><br />
<span style="color: #999999; font-size: x-small;">文章修改次數︰2</span><br />
<br />
<b><span style="color: red; font-size: large;">這篇筆記(使用Client-Login登入Google)在2015年6月後已不再適用</span></b><b><span style="color: red; font-size: large;">(請見<a href="https://groups.google.com/forum/#!topic/google-analytics-management-api/P8axL_lQp3c/discussion">此處</a>)</span></b><b><span style="color: red; font-size: large;">,如欲使用OAuth2登入,請參考<a href="http://lp43.blogspot.tw/2015/06/google-oauth2.html">這篇</a>。</span></b><br />
<br />
這幾天在嚐試串接<a href="https://developers.google.com/google-apps/spreadsheets/?hl=zh-tw">Google SpreadSheets</a>(Google的Excel試算表),<br />
遇到了一些困難,<br />
在這裡整理心得筆記。<br />
<br />
<h3>
<span style="color: #3d85c6; font-size: large;">1.會用到的Libraries</span></h3>
(1)gdata-client-1.0.jar<br />
(2)gdata-client-meta-1.0.jar<br />
(3)gdata-core-1.0.jar<br />
(4)gdata-spreadsheet-3.0.jar<br />
(5)gdata-spreadsheet-meta-3.0.jar<br />
(6)gson-2.1.jar<br />
(7)guava-11.0.2.jar<br />
(8)jsr305-1.3.9.jar<br />
<br />
<h3>
<span style="color: #3d85c6; font-size: large;">2.使用ClientLogin登入</span></h3>
<br />
<pre class="brush: java; html-script: true;">SpreadsheetService service = new SpreadsheetService("你的應用程式名稱");
service.setProtocolVersion(SpreadsheetService.Versions.V3);
String email = "GMAIL帳號";
String password = "不能直接打gmail的密碼在此,會遇到不合法的認證的錯誤。需至Google Account申請過的應用程式專屬密碼application-specific password(手機2次驗證後完全開通帳戶後方可使用此功能)";
service.setUserCredentials(email, password);
URL url = new URL("https://spreadsheets.google.com/feeds/spreadsheets/private/full");//網址基本上都是這個,不太容易動到
</pre>
<br />
<h3>
<span style="color: #3d85c6; font-size: large;">3.替應用程式設定專屬密碼</span></h3>
至<a href="https://www.google.com/settings/security">Google Account</a>,<br />
在帳戶權限-->應用程式和網站裡設定。<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnHKCOZE4TTUuSUKwjKjTkMRF-eBNINzjdfK3313VskmHERFgTFf4UuKoCEQ0OD9CAmOdZmKmBmBE1MCdVkZfvXRyN6TIdt4N36EwsUoEpA-LvbeJQEQAtN8eU7JBlqZvnpGxOKcX4_A/s1600/%E8%9E%A2%E5%B9%95%E5%BF%AB%E7%85%A7+2014-10-27+%E4%B8%8B%E5%8D%886.37.57.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="426" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnHKCOZE4TTUuSUKwjKjTkMRF-eBNINzjdfK3313VskmHERFgTFf4UuKoCEQ0OD9CAmOdZmKmBmBE1MCdVkZfvXRyN6TIdt4N36EwsUoEpA-LvbeJQEQAtN8eU7JBlqZvnpGxOKcX4_A/s1600/%E8%9E%A2%E5%B9%95%E5%BF%AB%E7%85%A7+2014-10-27+%E4%B8%8B%E5%8D%886.37.57.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIyK9MvvEIEDV_yhfdt25C9j08u-qZ-4MXwZP562muQnz8q9cLrVLiiEuyd-AFBTbUYLRa1BAHpl0231_FC_WK-BccI6F_dbblL9atsmmmph7b7B_YadsiIGsQOuzHod_j-PVf6pdmwQ/s1600/%E8%9E%A2%E5%B9%95%E5%BF%AB%E7%85%A7+2014-10-27+%E4%B8%8B%E5%8D%886.39.16.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="450" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIyK9MvvEIEDV_yhfdt25C9j08u-qZ-4MXwZP562muQnz8q9cLrVLiiEuyd-AFBTbUYLRa1BAHpl0231_FC_WK-BccI6F_dbblL9atsmmmph7b7B_YadsiIGsQOuzHod_j-PVf6pdmwQ/s1600/%E8%9E%A2%E5%B9%95%E5%BF%AB%E7%85%A7+2014-10-27+%E4%B8%8B%E5%8D%886.39.16.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1ehRfNDr1z0XDTfhDWoCk7n5AV9kO0RFmZs3B5PuqCYJkDJNIzhC-7otlMEG47IzSt6eKMRDS85A_BFyuhfNbTIxy_wEP9xe6PJVYSgBmf-06A39UZwTQTGh0UDwBw2QM3vJlWriKwg/s1600/%E8%9E%A2%E5%B9%95%E5%BF%AB%E7%85%A7+2014-10-27+%E4%B8%8B%E5%8D%886.37.47.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="386" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1ehRfNDr1z0XDTfhDWoCk7n5AV9kO0RFmZs3B5PuqCYJkDJNIzhC-7otlMEG47IzSt6eKMRDS85A_BFyuhfNbTIxy_wEP9xe6PJVYSgBmf-06A39UZwTQTGh0UDwBw2QM3vJlWriKwg/s1600/%E8%9E%A2%E5%B9%95%E5%BF%AB%E7%85%A7+2014-10-27+%E4%B8%8B%E5%8D%886.37.47.png" width="640" /></a></div>
<br />
<h3>
<span style="color: #3d85c6; font-size: large;">4.其它</span></h3>
<br />
對工作表處理(新增行/列、更新行/列、移除行/列)的相關程式都在<a href="https://developers.google.com/google-apps/spreadsheets/?hl=zh-tw">這裡</a>找的到。Anonymoushttp://www.blogger.com/profile/07460782835337903344noreply@blogger.com0tag:blogger.com,1999:blog-5753539950414636776.post-4153460526905432822014-10-13T01:50:00.001-07:002014-10-13T01:50:43.204-07:00套件Volleyhttp://java.dzone.com/articles/android-%E2%80%93-volley-libraryAnonymoushttp://www.blogger.com/profile/07460782835337903344noreply@blogger.com0tag:blogger.com,1999:blog-5753539950414636776.post-16353695355181726002014-09-17T00:49:00.000-07:002014-09-17T01:04:15.656-07:00不要過度依賴Activity.onDestroy()去執行程式<span style="color: #999999;"><span style="font-size: x-small;">文章攢寫時間︰2014/09/17 15:00</span></span><br />
<h2>
<span style="color: #6aa84f; font-size: x-large;">一、問題</span></h2>
今天遇到一個crash,<br />
DVM由於Android背景執行的程式過多,<br />
可執行app的記憶體不足,<br />
<b><span style="color: #6fa8dc;">直接將我服務進程砍掉</span></b>(kill process),
<br />
然而我在Activity.onDestroy()裡做了很多值的還原或歸零(預設值)的動作,<br />
因為這種方式,<br />
導致所有該還原回預設值的動作,<br />
<b><span style="color: red;">通通沒做</span></b>。<br />
<br />
<h2>
<b><span style="color: #6aa84f; font-size: x-large;">二、解決辦法</span></b></h2>
這件事算是學了個經驗,<br />
開發者無法預期使用者一定會使用返回鍵(Back button)將程式正常關閉,<br />
因為可能有很多"外在"因素會造成你的程式被強制終止。<br />
<br />
所以,<br />
不要在Activity.onDestroy()做許多值還原或歸零(預設值)的動作,<br />
因為你<i><span style="font-size: large;">根本無法預期</span></i>這些行為在低階手機裡是能<span style="font-size: large;"><i>被正確執行</i></span>的。<br />
<br />
<h2>
<span style="color: #6aa84f; font-size: x-large;">三、其它</span></h2>
<div class="post-text" itemprop="text">
<h3>
<span style="color: #3d85c6; font-size: large;">static變數在Android裡的生命週期</span></h3>
以下內容來源︰<a href="http://stackoverflow.com/questions/1944369/android-static-object-lifecycle-application-act-crazy/1944564#1944564">stackoverflow</a><br />
<span style="color: #999999; font-size: x-small;"><br /></span>
<span style="color: #999999; font-size: x-small;">Lets start with a bit of background: What happens when you start an application?</span><br />
我們來談一些關於android背後的機制︰當你啟動了一個應用程式(application)後,背後究竟發生了些什麼事?<br />
<br />
<span style="color: #999999; font-size: x-small;">The OS starts a process and assigns it a unique process id and allocates a process table.A process start an instance of DVM(Dalvik VM); Each application runs inside a DVM.</span><br />
在啟動了一個應用程式以後,作業系統開啟了一個進程(process),並同時給這個進程一個專屬進程id,並分配給進程一個進程資料表(process table,用來儲存該進程的所有相關資訊)。然後,這個進程會啟動一個DVM(Dalvik VM,Dalvik虛擬機)實體,每一個應用程式(application)都分別運作在各自的DVM裡。<br />
<br />
<span style="color: #999999; font-size: x-small;">A DVM manages class loading unloading, instance lifecycle, GC etc.</span><br />
DVM管理著每個class是否要被載入(loading unloading)、生命週期、實體、資源回收(Garbage Collection)等等。<br />
<br />
<span style="color: #999999; font-size: x-small;">Lifetime of a static variable: A static variable comes into existence when a class is loaded by the JVM and dies when the class is unloaded.</span><br />
static變數的生命週期︰當一個class被JVM載入後,static變數值就會被產生,當class被卸載後,static變數值就會被消毀。<br />
<br />
<span style="color: #999999; font-size: x-small;">So if you create an android application and initialize a static
variable, it will remain in the JVM until one of the following happens:<br />
1. the class is unloaded<br />
2. the JVM shuts down<br />
3. the process dies </span><br />
<span style="color: #999999; font-size: x-small;">Note that the value of the static variable will persist when you
switch to a different activity of another application and none of the
above three happens. Should any of the above three happen the static
will lose its value.</span><br />
所以,當你建立了一個android應用程式並且初始化了static變數後,這個static值就會被保留直到下列事件發生為止︰<br />
1.class被卸載<br />
2.JVM被關閉<br />
3.進程(process)被消滅了<br />
<span style="color: #3d85c6;">備註︰當你切換到不同的應用程式的Activity時,原Activity的static變數都會一直被保留,除非上述的事件被觸發。一旦被觸發後,static變數就會遺失原來的值。</span><br />
<br />
<span style="color: #999999; font-size: x-small;">You can test this with a few lines of code:</span><br />
<span style="color: #999999; font-size: x-small;">1.print the uninitialized static in onCreate of your activity -> should print null</span><br />
<span style="color: #999999; font-size: x-small;">2.initialize the static. print it -> value would be non null</span><br />
<span style="color: #999999; font-size: x-small;">3.Hit the back button and go to home screen. Note: Home screen is another activity.</span><br />
<span style="color: #999999; font-size: x-small;">4.Launch your activity again -> the static variable will be non-null</span><br />
<span style="color: #999999; font-size: x-small;">5.Kill your application process from DDMS(stop button in the devices window).</span><br />
<span style="color: #999999; font-size: x-small;">6.Restart your activity -> the static will have null value.</span><br />
你可以用下列的方式試試上述的理論︰<br />
1.在Activity onCreate()函式裡印岀未初始化的static變數值-->應該印岀null。<br />
2.初始化static變數值-->印岀來的值應該就不是null值了。<br />
3.點擊手機返回鍵並回到手機Home首頁(備註︰Home首頁就是另一個Activity)。<br />
4.重啟你的Activity-->static變數值理論上不會是null。<br />
5.從DDMS砍掉你的應用程式進程(在Eclipse ADT裡有一個Devices視窗,裡面有stop按鈕,見下圖)。<br />
6.重啟你的Activity-->static變數值將會變成null。<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjQiVFle5XUjLP8F5ePkVAAQ2rxrEuZ0XZzo8q-i81EBqbTcjqlTw5i7-C1vECExHWh8Ml25t_k9fVTj9f9S_Dpw-fQqsM2-3UYBViVfz9rXb9QlLjrGwnjvSikdVv21zQdseQcFDPOA/s1600/%E8%9E%A2%E5%B9%95%E5%BF%AB%E7%85%A7+2014-09-17+%E4%B8%8B%E5%8D%883.37.57.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjQiVFle5XUjLP8F5ePkVAAQ2rxrEuZ0XZzo8q-i81EBqbTcjqlTw5i7-C1vECExHWh8Ml25t_k9fVTj9f9S_Dpw-fQqsM2-3UYBViVfz9rXb9QlLjrGwnjvSikdVv21zQdseQcFDPOA/s1600/%E8%9E%A2%E5%B9%95%E5%BF%AB%E7%85%A7+2014-09-17+%E4%B8%8B%E5%8D%883.37.57.png" height="418" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Devices視窗裡的Stop按鈕。<br />
<br />
如果看不到這個視窗,請在Eclipse工具列[Window]-->[Show View]-->[Other]-->[Android]-->選擇[Devices]即可岀現該Devices視窗。</td></tr>
</tbody></table>
<br /></div>
Anonymoushttp://www.blogger.com/profile/07460782835337903344noreply@blogger.com7tag:blogger.com,1999:blog-5753539950414636776.post-44157555721534898782014-09-10T23:28:00.002-07:002014-09-10T23:51:05.777-07:00使用adb螢幕截圖adb工具除了快速安裝apk外,<br />
還可以截圖<br />
Terminal指令如下<br />
<br />
<span style="color: #999999;">將當下螢幕截取至手機的sdcard資料夾,並命名為screen.png</span><br />
<pre class="brush: java; html-script: true; gutter: false;">$adb shell screencap -p /sdcard/screen.png</pre>
<br />
<span style="color: #999999;">將檔案從手機複製到電腦預設根目錄(以Mac來看是Users/youname底下)</span><br />
<pre class="brush: java; html-script: true; gutter: false;">$adb pull /sdcard/screen.png</pre>
<br />
<span style="color: #999999;">刪除手機裡剛才存放的圖檔</span><br />
<pre class="brush: java; html-script: true; gutter: false;">$adb shell rm /sdcard/screen.png</pre>
Anonymoushttp://www.blogger.com/profile/07460782835337903344noreply@blogger.com0