2014年2月24日 星期一

如何不透過email 傳送資料給自己的手機或電腦


不要再使用email傳送檔案給自己了,使用pushbullet吧!!

甚麼是pushbullet?

當你在手機上有一些資料想要傳到電腦或者有些資料想從電腦傳到手機時,
通常我們都會用facebook ,email 或 藍芽 ....等方式傳資料給自己。
pushbullet想做的事情很簡單


用最好理解的方式說明,你可以把他想成是可以跟自己對話的 line or whatsapp


現在介紹pushbullet

登入之後會出現上方的圖,只要在自己的裝置上安裝他專屬的app 就可以傳送檔案,文件,文字了,在chrome上必須安裝extension。

pushbullet還支援api,有機會再來分享這部份給大家




2014年2月17日 星期一

Android 開發 (三十一) layer-list 使用

Layer-list 讓我們可以層疊客制多層的layer,由於android手機的解析度非常眾多,這對UE來說是非常大的負擔,很難用一張圖來滿足所有的解析度,通常比較簡單的button 或線都會用畫的,這樣的好處是,圖的解析度比較不會跑掉,只是這也就苦了RD了。


如何使用layer-list?

如下方的按鈕

<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >

    <item>
        <shape android:shape="rectangle" >
            <corners android:radius="10dp" />

            <solid android:color="#2caabb" />
        </shape>
    </item>
    <item
        android:bottom="3dp"
        android:left="3dp"
        android:right="3dp"
        android:top="3dp">
        <shape android:shape="rectangle" >
            <corners android:radius="10dp" />

            <solid android:color="#222244" />
        </shape>
    </item>
    <item
        android:bottom="7dp"
        android:left="7dp"
        android:right="7dp"
        android:top="7dp">
        <shape android:shape="rectangle" >
            <corners android:radius="10dp" />

            <solid android:color="#333333" />
        </shape>
    </item>
    <item
        android:bottom="9dp"
        android:left="9dp"
        android:right="9dp"
        android:top="9dp">
        <shape android:shape="rectangle" >
            <corners android:radius="10dp" />

            <solid android:color="#2cbb77" />
        </shape>
    </item>

</layer-list>

每個item都代表一層
上面那張圖是由四層圖層疊出來的
corners使方型的角變成圓角
shape 擁有四種形狀  ["rectangle" | "oval" | "line" | "ring"

更詳細的說明可以觀看 android developer的網址
http://developer.android.com/guide/topics/resources/drawable-resource.html#LayerList

Android 開發 (三十) Selector 使用

Selector讓我們可以客製化我們想要的UI色彩,
例如我希望按鈕在正常的情況下是灰色,在按下去時為藍色
又或者我希望checkbox在正常的情況下是灰色,被check的時候是藍色
要實作這個功能首先必須先在drawable-xxxx folder中創造一個

myselector.xml

<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:drawable="@color/mycolor1" android:state_pressed="true"></item>
    <item android:drawable="@color/mycolor2" android:state_pressed="false"></item>

</selector>

在上面的程式裡,我設定當view 的state為 pressed的時候  會顯示 mycolor1的drawable
當view的state為 not pressed的時候,會顯示mycolor2的drawable。

接著只需要設定 android:background="@drawable/myselector" 之後當客製化色彩的button就完成了

接著要客製化checkbox

checkboxselector.xml


<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:drawable="@color/mycolor1" android:state_checked="true"/>
    <item android:drawable="@color/mycolor2" android:state_checked="false"/>
    <item android:drawable="@color/mycolor2"/>

</selector>

上面的程式碼設定了當view 被 check的時候 顯示mycolor1 ,沒有check的時候顯示mycolor2
, 預設的色彩為 mycolor2
接著只需要設定 android:background="@drawable/checkboxselector"  就完成了

使用selector讓我們可以設定不同state時我們想要的色彩或者圖案。
在android developer網頁有更詳細的描述
http://developer.android.com/guide/topics/resources/drawable-resource.html#StateList

2014年2月16日 星期日

Android 開發 (二十九) imagelevel

為何要使用setimagelevel?

在設定圖片時,android 有一種方式讓我們可以在不同的level顯示不同的圖片,
這樣的好處是不需要重新去尋找圖檔,只需要在一開始做好設定,之後就利用定義好的level去切換就好了,例如  press 就是 level 1     release 就是level 2   check就是level3 ... etc


要如何使用setimagelevel的功能呢?

首先,我們必須先設定level-list  如下

imagelevel.xml
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:maxLevel="1" android:drawable="@drawable/c" />
    <item android:maxLevel="2" android:drawable="@drawable/b" />
    <item android:maxLevel="3" android:drawable="@drawable/d" />
    <item android:maxLevel="4" android:drawable="@drawable/e" />
    <item android:maxLevel="5" android:drawable="@drawable/a" />
</level-list>

接著設定imageview的src


    <ImageView
        android:id="@+id/img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/imagelevel" />

接著只需要在條件符合時呼叫 setImageLevel(1)   or  setImageLevel(2)
這樣就可以讓圖片隨著不同的state變更圖片了,這樣的好處是,這個頁面的圖片一目瞭然,
看程式的時候不再需要整個程式碼一張一張尋找了。

2014年2月13日 星期四

Android 開發 (二十八) onInterceptTouchEvent 和 onTouchEvent 的處理

為何要瞭解event的處理

有些時候會在viewgroup裡放其他View,然後會希望某些view的touch event不動作,這時候就必須設法去處理onInterceptTouchEvent  和  onTouchEvent。
例如在viewpager裡面放 imageview,當click在imageview時不希望viewpager有動作,又或者listview 裡放button,但是只希望接收到listview click event。


甚麼是onInterceptTouchEvent  和  onTouchEvent

onInterceptTouchEvent就是用來攔截事件,而onTouchEvent是用來處理事件。
當事件被InterceptEvent攔截之後就不會再往下傳遞給childLayout,
然而當事件被Touchevent處理過後就不會再向ParentLayout傳遞。


當所有的event回傳值都是false時
可以看到debug log 從
 listview OninterceptEvent -> TextView OninterceptEvent
->TextView OnTouchEvent -> listview OnTouchEvent
假設 textview 的 onTouchevent 回傳值為 true
Debug log會變成
 listview OninterceptEvent -> TextView OninterceptEvent ->TextView OnTouchEvent
由於事件被textview處理完成 ,所以listview就不需再處理。
假設 listview 的 onInterceptEvent 回傳值為 true
Debug log會變成
 listview OninterceptEven ->ListView OnTouchEvent
由於事件被Listview攔截 ,所以textview就不會接收到event。

結論

利用onInterceptTouchEvent  和  onTouchEvent這兩個event,
就可以輕鬆客制化自己想要的行為。

2014年2月11日 星期二

Android 開發 (二十七) tabhost 加上viewpager 使用

Tabhost以及ViewPager介紹

如下圖,在設計app時,常常會使用到這種分頁方式,
例如我寫了一個email app, tab a為  hotmail  , tab b 為 gmail  ....以此類推,
tabhost就是上面的tab,而viewPager讓我們能夠利用滑動切換頁面
兩個加在一起的話,有較好的使用者經驗。


要如何使用

首先必須先創建 main.xml
在xml可以看到,layout裡面同時擁有 tabhost 以及viewpager

<TabHost android:layout_height="fill_parent" android:layout_width="fill_parent" android:id="@+id/tabhost" xmlns:android="http://schemas.android.com/apk/res/android">


<LinearLayout android:layout_height="fill_parent" android:layout_width="fill_parent" android:orientation="vertical">

<TabWidget android:layout_height="wrap_content" android:layout_width="fill_parent" android:id="@android:id/tabs" android:layout_marginRight="3dp" android:layout_marginLeft="3dp"> </TabWidget>

<android.support.v4.view.ViewPager android:layout_height="fill_parent" android:layout_width="fill_parent" android:id="@+id/viewpager"/>

<FrameLayout android:layout_height="fill_parent" android:layout_width="fill_parent" android:id="@android:id/tabcontent" android:visibility="gone"> </FrameLayout>

</LinearLayout>

</TabHost>

接著在 tabFragment內


	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		View view = inflater.inflate(R.layout.login_tab_layout, container,false);
		mPager  = (ViewPager) view.findViewById(R.id.viewpager);
		mTabHost = (TabHost)  view.findViewById(R.id.tabhost);
		mTabHost.setup();	
		mTabHost.setOnTabChangedListener(new OnTabChangeListener() {
            @Override
            public void onTabChanged(String tabId) {
            	mTabHost.setOnTabChangedListener(new OnTabChangeListener() {
                        @Override
                        public void onTabChanged(String tabId) {                           	
                        		mPager.setCurrentItem(mPager.getAdapter().getItemPosition(fragmentMap.get(tabId)));
                        }
                    });
                
            }
        });
		
        mPager.setOnPageChangeListener(new OnPageChangeListener() {
            
        	@Override
            public void onPageSelected(int position) {            	
            		mTabHost.setCurrentTab(position);
            }
            
            @Override
            public void onPageScrolled(int arg0, float arg1, int arg2) {}
            
            @Override
            public void onPageScrollStateChanged(int arg0) {}
        });
		return view;
	}		

在點擊tabhost時同時切換viewpager
在切換viewpager時同時切換tabhost

接著要添加fragment在viewpager內,以及添加tab在Tabhost內

public void addFragmentTab(int titleResId, int tabViewResId, Fragment fragment , String tag){
		list.add(fragment);
		fragmentMap.put(tag, fragment);
		TextView tabView = (TextView) LayoutInflater.from(getActivity()).inflate(tabViewResId, null);
		tabView.setText(titleResId);

		TabSpec tabSpec = mTabHost.newTabSpec(tag).setIndicator(tabView);
	    tabSpec.setContent(new TabFactory(getActivity()));
	    mTabHost.addTab(tabSpec);
	   
	}


接著就完成了
搭配 Android 開發 (二十五) how to change bottom color of tabhost 
就可以完成客製化tab

附上 git 的project url

Android 開發 (二十六) handle hyperlink in textView(二)

handle hyperlink in textView(一) 中有提到如何使 textview 能夠處理url 使之能夠被點擊。
但是如果僅使用那邊的寫法,當將這個textview放到 listview內會造成 itemclick無法被點擊的問題。

為何會造成這個問題?

由於設定 setmovementmethod會同時 
  • setFocusable(true);
  • setClickable(true);
  • setLongClickable(true);
簡單的說就是 Event 都被 Textview處理掉了,所以listview拿不到itemclick event。
所以我們必須客製化touch event,只有在url被點擊的時候,才使用Textview去處理,其他時候就將event 往下傳遞。


首先要稍微提一下 onTouchEvent的 override
return true  代表這個事件在這邊被處理,所以不會再往下傳遞。
return false 代表事件並未被處理,會繼續向下傳遞。




首先先客製化 LinkMovementMethod 只有在url 被點擊時才處理 (return true)

 public static class LocalLinkMovementMethod extends LinkMovementMethod{
     static LocalLinkMovementMethod sInstance;


     public static LocalLinkMovementMethod getInstance() {
         if (sInstance == null)
             sInstance = new LocalLinkMovementMethod();

         return sInstance;
     }

     @Override
     public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
         int action = event.getAction();

         if (action == MotionEvent.ACTION_UP ||
                 action == MotionEvent.ACTION_DOWN) {
             int x = (int) event.getX();
             int y = (int) event.getY();

             x -= widget.getTotalPaddingLeft();
             y -= widget.getTotalPaddingTop();

             x += widget.getScrollX();
             y += widget.getScrollY();

             Layout layout = widget.getLayout();
             int line = layout.getLineForVertical(y);
             int off = layout.getOffsetForHorizontal(line, x);

             ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);

             if (link.length != 0) {
                 if (action == MotionEvent.ACTION_UP) {
                     link[0].onClick(widget);
                 } else if (action == MotionEvent.ACTION_DOWN) {
                     Selection.setSelection(buffer,
                             buffer.getSpanStart(link[0]),
                             buffer.getSpanEnd(link[0]));
                 }

                 if (widget instanceof LinkEnabledTextView){
                     ((LinkEnabledTextView) widget).linkHit = true;
                 }
                 return true;
             } else {
                 Selection.removeSelection(buffer);
                 Touch.onTouchEvent(widget, buffer, event);
                 return false;
             }
         }
         return Touch.onTouchEvent(widget, buffer, event);
     }
 }


接著在客製化的 textView 裡面 override touchevent 和 focus


 @Override
 public boolean hasFocusable() {
  // TODO Auto-generated method stub
  return false;
 }
 
 @Override
 public boolean onTouchEvent(MotionEvent event) {
     linkHit = false;
     boolean res = super.onTouchEvent(event);

     if (dontConsumeNonUrlClicks)
         return linkHit;
     return res;

 }


經過這樣的修改,listview的onitemclick event就不會再被textview吃掉了。

2014年2月10日 星期一

Android 開發 (二十五) how to change bottom color of tabhost

需求

如何更改tabhost 底下那條線的顏色,我花了不少時間尋找可用的方法,可惜一直找不到較好的方法,最後的方法是自己製作圖然後更換背景圖,效果比想像的還要好。
如下圖



實現方法

首先先實做兩個背景圖

line_label_1_pressed.xml
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
    <item android:top="-6dp" android:left="-6dp" android:right="-6dp">
        <shape>
            <size android:height="50dp"/>
            <solid android:color="@android:color/transparent"/>
            <stroke android:color="@color/myColor" android:width="6dp"/>
        </shape>
    </item>
</layer-list>

line_label_1.xml
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >

    <item>
        <shape>
            <solid android:color="@android:color/transparent" />
        </shape>
    </item>

</layer-list>



接著使用selector
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:drawable="@drawable/line_label_1_pressed" android:state_selected="true"/>
    <item android:drawable="@drawable/line_label_1"/>
</selector>


接著只需要在 view 裡面使用這個背景即可, 範例如下

android:background="@drawable/tab_line_selector_bottom"

當被selected的時候就會顯示, line_label_1_pressed 其他時候就會使用line_label_1的xml。

Android 開發 (二十四) handle hyperlink in textView(一)

為何需要客製化  textView 來處理hyperlink

最近在寫案子的時候遇到一個需求, 希望 textView 能夠偵測 http:// 並且判斷該url為何,並根據不同的url做不同的動作,假設只需要判斷 hyperlink  使用 textview 內建的android:autolink就可以完成,但是內建的textView並不能偵測click的動作,所有的hyperlink都會自動導向browser ,這並符合我們的需求,經過了一段時間的尋找,總算找到了解法,在這邊跟大家分享。


使用的原理

由於 textview 可以使用 SpannableString 然而 SpannableString 有個method 叫 setSpan ,而setSpan的
第一個參數可以傳各式各樣的Span,其中有一個叫做 ClickableSpan ,使用這個span讓我們可以接收到click event, 但是要使hyperlink可以點擊還必須多做一道手續,setMoveMentMethod 在設定完成之後,我們的功能就算大功告成了。

程式碼範例



 
 public void seTextLinkClickListener(TextLinkClickListener listener)
 {
  mListener = listener;
     MovementMethod m = getMovementMethod();
     if ((m == null) || !(m instanceof LinkMovementMethod)) {
         if (getLinksClickable()) {
          setMovementMethod(LinkMovementMethod.getInstance());
         }
     }
 }
        public void GenerateLink(String text)
 {
  SpannableString linkableText = new SpannableString(text);

     gatherLinks(listOfLinks, linkableText, hyperLinksPattern);

     for(int i = 0; i< listOfLinks.size(); i++)
     {
         Hyperlink linkSpec = listOfLinks.get(i);
         linkableText.setSpan(linkSpec.span, linkSpec.start, linkSpec.end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
     }

     setText(linkableText);
 }

 private final void gatherLinks(ArrayList<Hyperlink> links, Spannable s,
   Pattern pattern) {
  
  Matcher m = pattern.matcher(s);
  
  while (m.find()) {
   int start = m.start();
   int end = m.end();

   Hyperlink spec = new Hyperlink();

   spec.textSpan = s.subSequence(start, end);
   spec.span = new InternalURLSpan(spec.textSpan.toString());
   spec.start = start;
   spec.end = end;

   links.add(spec);
  }
 }
 public class InternalURLSpan extends ClickableSpan
 {
     private String clickedSpan;

     public InternalURLSpan (String clickedString)
     {
         clickedSpan = clickedString;
     }

     @Override
     public void onClick(View textView)
     {
         mListener.onTextLinkClick(textView, clickedSpan);
     }
 }
 
 class Hyperlink
 {
     CharSequence textSpan;
     InternalURLSpan span;
     int start;
     int end;
 }


接著只需要再mainActivity裡面使用這個檔案就可以了

附上 sample code
網路上也有相關的Git Project

2014年2月5日 星期三

Android 開發 (二十三) google console key 取得

在開發時常常需要用到google api,
在使用google api就必須前往 Google Console取得permission
首先將要使用的API turn on


接著Create Key  例如我是寫android的  就create android key

接著必須填入project 的 name 以及SHA1 Fingerprint
例如我創造一個project  mytubesample 我的 project name
就是com.example.mytubesample


該如何找到 SHA1 key  這是一個問題,
如果是使用eclipse就可使用下面的方式
Window -> Preferences -> Build -> SHA1 fingerprint

SHA1 Fingerprint就是我們需要的SHA1 Fingerprint
在創建完成之後


我們會看到API key  只需要將該key填入api中即可使用API

Android 開發 (二十二) 使用 Youtube SDK

如何使用youtube SDK
如同所有Google的sdk ,必須申請 developer key
在取得key之後,使用下方的sample code 就可簡單的使用 youtube sdk


public class MainActivity extends YouTubeFailureRecoveryActivity implements
  PlaybackEventListener {
 YouTubePlayer mYouTubePlayer;
 YouTubePlayerView youTubeView;
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

     youTubeView = (YouTubePlayerView) findViewById(R.id.youtube_view);
  youTubeView.initialize(DeveloperKey.DEVELOPER_KEY, this);
 }

 @Override
 public void onInitializationSuccess(YouTubePlayer.Provider provider,
   YouTubePlayer player, boolean wasRestored) {
  if (!wasRestored) {
   player.cueVideo("wKJ9KzGQq0w");
  }
  mYouTubePlayer = player;
  player.setPlayerStyle(PlayerStyle.MINIMAL);
  player.setPlaybackEventListener(this);
 }

 @Override
 public void onBackPressed() {
  // TODO Auto-generated method stub
  mYouTubePlayer.setFullscreen(false);
 }
 @Override
 protected YouTubePlayer.Provider getYouTubePlayerProvider() {
  return (YouTubePlayerView) findViewById(R.id.youtube_view);
 }

 @Override
 public void onBuffering(boolean arg0) {

  mYouTubePlayer.setFullscreen(true);
 }

 @Override
 public void onPaused() {
  // TODO Auto-generated method stub

 }

 @Override
 public void onPlaying() {
  // TODO Auto-generated method stub

 }

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

 }

 @Override
 public void onStopped() {
  // TODO Auto-generated method stub

 }

}

其中 YouTubeFailureRecoveryActivity 為 youtube sample code 所附加的檔案
youtube 的 sample code 可以在此下載

在這裡要特別提及的重點是,當使用fullscreen全螢幕撥放時,
很多人都會發現無法恢復成portrait mode
要恢復成portrait mode 其實很簡單,
注意我在 onbackpress做的事情,
當backpress時我將fullscreen mode設為false 這樣就能恢復成portrait mode了
附上sample code 供參考