2014年1月30日 星期四

Android 開發 (二十一) 使用 URL開啟app

假設我們寫了一個很龐大的app裡面有很多功能,
然後今天來了一個使用者,他希望找到我們app裡的某個功能,
我們當然不可能直接前去指導那個使用者如何使用
這時我們可以使用 intent-filter 使我們的app導向特定頁面

又或者我在FB粉絲團留言中只提供簡略的描述,
當使用者點擊網址我希望能利用網址導向app的詳細頁面。

要實做這個功能就必須使用 intent-filter



 <activity
            android:name="com.example.intentfiltersample.MainActivity"
            android:label="@string/app_name" >
            <intent-filter android:priority="0">
                <action android:name="android.intent.action.MAIN" />
                <action android:name="android.intent.action.VIEW" />

                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />

                <data
                   android:path="com.*"
                    android:scheme="http" >
                </data>
            </intent-filter>
        </activity>

這樣只需要在facebook上 貼文   http://com.functionA   http://com.functionB  並點擊
這樣就可以導向不同的功能頁面

接著只需要在接到時 getIntent().getDataString();  就可以取得網址了
附上 sample Code

2014年1月29日 星期三

Android 開發 (二十) GridView 與scrollView共同使用

在開發專案時有些時候會需要類似下面左圖的樣式
上方是一個view ,下方是gridview,但是在滑動的時候卻是整片UI滑動,
也就是當滑動時會變成像右圖的樣式。


要實做這種view 有兩種方式
一種是在ScrollView裡面動態的將view 加入
另一種方法是使用 ScrollView裡面包含GridView

關於第一種作法,之後有空會在詳細說明
現在要介紹的是第二種方法,當我在實作這個方法時,
我天真的以為只需要在scrollview 裡面包 gridview就好了
程式碼如下


<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#333333"
    tools:context=".MainActivity" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >

        <TextView
            android:layout_width="match_parent"
            android:layout_height="300dp"
            android:background="#a5c9ff"
            android:gravity="center_horizontal|center_vertical"
            android:text="@string/hello_world" />

        <GridView
            android:id="@+id/gridview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:horizontalSpacing="3dp"
            android:numColumns="2"
            android:stretchMode="columnWidth"
            android:verticalSpacing="3dp" />
    </LinearLayout>

</ScrollView>

當我開開心心的將code 寫好,deploy到手機之後,
這才發現現實是殘酷的,事情永遠不是像我想得那麼簡單,
gridview只顯示出兩個item,其他的item不翼而飛。

為了要解決這個問題,就必須客製化gridview


public class MyGridView extends GridView { 
    public MyGridView(Context context) { 
        super(context); 
    } 
    public MyGridView(Context context, AttributeSet attrs) { 
        super(context, attrs); 
    } 
    public MyGridView(Context context, AttributeSet attrs, int defStyle) { 
        super(context, attrs, defStyle); 
    } 
    @Override 
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
 
        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, 
                MeasureSpec.AT_MOST); 
        super.onMeasure(widthMeasureSpec, expandSpec); 
    } 
} 

在onMeasure的時候調整高度,然後更改原本的gridview改成客製化的gridview
這樣所有的問題就都解決了。

附上 sample Code

2014年1月28日 星期二

Android 開發 (十九) BroadcastReceiver

使用 BroadcastReceiver  讓我們可以去接收某些Action,例如手機沒電,或者某些自己特定的action,在 OnReceive的時候我們可以做一些接收到這個action時所需作的處理,
在這邊寫一些範例


   @Override
   public void onClick(View arg0) {
           Intent intent = new Intent().setAction(  
                         "myAction").putExtra("Tag",  
                         "Data");  
                  
              sendBroadcast(intent);  
   }
   

在button click的時候 ,sendBroadcast 送廣播給手機, 並且設定 action為我自己客製化的action,
這時所有監聽這個action的都會被呼叫。


    public void onReceive(Context context, Intent intent) {  
     Log.d("Ted","OnReceive");
        this.context = context;  
        if(intent.getAction().equals("myAction")){  
            Log.v("Ted", intent.getStringExtra("Tag"));  
            showNotification();  
        }  
        
    } 

我在接收到action的時候發送Notification,使手機出現Notification。

附註:
使用BroadcastReceiver 必須在manifest裡增加receiver,範例如下


        <receiver android:name="Receiver1" >
            <intent-filter>
                <action android:name="myAction" >
                </action>
            </intent-filter>
        </receiver>

myAction為客製化的action,在button click時我有設定action,所以在button click之後會去呼叫Receiver1,並且在 OnReceive可以做相關的動作。

附上 sample Code

Android 開發 (十八) Notification

甚麼是Notification



可以看到上圖的NavigateTo SecondActivity這個 Notification
有很多地方可以看到Notification的應用
例如: 收到信件,需要軟體更新時,收到GCM時,鬧鐘

在這邊會稍微介紹如何使用Notification



    NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    Intent notifyIntent = new Intent(MainActivity.this,
      SecondActivity.class);
    notifyIntent.putExtra("SecondActivity", "Btn");
    
    PendingIntent appIntent = PendingIntent.getActivity(
      MainActivity.this, 0, notifyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    Notification notification = new Notification();
    notification.icon = R.drawable.ic_launcher;
    notification.tickerText = "notification on status bar.";
          notification.flags |= Notification.FLAG_AUTO_CANCEL; 
    notification.setLatestEventInfo(MainActivity.this,
      "NavigateTo", "SecondActivity", appIntent);
    notificationManager.notify(0, notification);

首先先取得 NotificationManager  接著創造 intent ,  SecondActivity為點擊時倒向的Activity,
PendingIntent用來存取 intent ,其中有個重點 PendingIntent.FLAG_UPDATE_CURRENT為必要的,假設缺少這行會造成SecondActivity在getIntent的時候只會取得第一次的Intent,
如果需要瞭解這行的用意,只需要實作多個Intent帶入不同參數,並倒向同一個activity就可以複製出這個問題。

接著創造 Notification  並且做相關設定,其中Notification.FLAG_AUTO_CANCEL為點擊時Notification會自動消失。
在notificationManager.notify之後就會產生出notification。

接著要介紹如何客製化UI


    RemoteViews contentView = new RemoteViews(getPackageName(), R.layout.customview);

    contentView.setImageViewResource(R.id.image, R.drawable.ic_launcher);

    contentView.setTextViewText(R.id.text, "Hello, this message is in a custom expanded view");
    Intent notifyIntent = new Intent(MainActivity.this,
      MainActivity.class);
    
    PendingIntent appIntent = PendingIntent.getActivity(
      MainActivity.this, 0, notifyIntent, 0);
    int icon = R.drawable.ic_launcher;
       long when = System.currentTimeMillis();
       Notification notification = new Notification(icon, "Custom Notification", when);
         // Notification notification = new Notification();
    notification.contentIntent = appIntent;
    notification.contentView = contentView;
          notification.flags |= Notification.FLAG_AUTO_CANCEL; //Do not clear the notification
          notification.defaults |= Notification.DEFAULT_LIGHTS; // LED
          notification.defaults |= Notification.DEFAULT_VIBRATE; //Vibration
          notification.defaults |= Notification.DEFAULT_SOUND; // Sound
         
          
    NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    notificationManager.notify(1, notification);

創建 RemoteViews contentView之後,並且設定 notification.contentView = contentView;
其中 R.layout.customview 為任意客製化UI。

在這裡附上 sample code

2014年1月27日 星期一

Android 開發 (十七) Hello GCM

甚麼是GCM


server端可以藉由此通知client端一些消息,例如: line的訊息通知,軟體更新通知,或者某些商品的特賣通知。

如何實現GCM


首先必須先寫出client端程式,client端必須註冊GCM,並且設定一個receiver去接收GCM

以下是範例


  GCMRegistrar.checkDevice(this); 
  GCMRegistrar.checkManifest(this); 
  final String regId = GCMRegistrar.getRegistrationId(this); 
  Log.d("Ted", "regId = " + regId); 
  if (regId.equals("")) { 
     GCMRegistrar.register(this, Project_ID); 
  } else { 
     Log.d("Ted", "Already registered"); 
  }              

上面幾行會註冊GCM,接著在manifest裡面加入以下的程式


   <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.VIBRATE" />

    <permission
        android:name="com.example.momoandroid.permission.C2D_MESSAGE"
        android:protectionLevel="signature" />

    <uses-permission android:name="com.example.momoandroid.permission.C2D_MESSAGE" />
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.gcmsample.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <receiver
            android:name="com.google.android.gcm.GCMBroadcastReceiver"
            android:permission="com.google.android.c2dm.permission.SEND" >
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                <action android:name="com.google.android.c2dm.intent.REGISTRATION" />

                <category android:name="com.example.gcmsample" />
            </intent-filter>
        </receiver>

        <service android:name=".GCMIntentService" />
    </application>

使用receiver接收gcm,並且設定service為 GCMIntentService
然後GCMIntentService繼承 GCMBaseIntentService
接著在onMessage也就是收到訊息時做相關的動作


  NotificationManager notificationManager = (NotificationManager) context
    .getSystemService(NOTIFICATION_SERVICE);

  /*
   * Intent notifyIntent = new Intent(context ,MainActivity.class);
   * notifyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); PendingIntent
   * appIntent = PendingIntent.getActivity(context,0, notifyIntent, 0);
   */

  Notification notification = new Notification();
  notification.icon = R.drawable.ic_launcher;
  notification.tickerText = "notification on status bar.";
  notification.defaults = Notification.DEFAULT_ALL;
  notification.setLatestEventInfo(context, "Title", message, null);
  notificationManager.notify(0, notification);

如上面的程式碼,這樣就可以在收到訊息時產生推撥
然後在 OnRegiter時將 Register_Id記起來之後在server端會使用到

上面的程式碼有個Project_ID必須去google developer console申請
首先去 google console 開啟一個project  https://cloud.google.com/console/project
接著就可以在overview欄位找到Project Number ,也就是我們需要的Project_ID

接著開啟 google cloud messaging for android


接著create key create browser key


接著將API key 記起來  之後在server端需要使用

目前我們已經取得了 server 端需要的API_Key 以及 Register_Id
接著我們只需要開創一個java project
然後key入以下程式碼

 Sender sender = new Sender(API_Key );

            ArrayList<String> devicesList = new ArrayList<String>();
            
            String device =Register_Id;
           devicesList.add(device);

            Message message = new Message.Builder()
                    //.collapseKey("message")
                    //.timeToLive(241000)
                    .delayWhileIdle(true)
                    .addData("message", "Your message send")
                    .build();



          
            MulticastResult result = sender.send(message, devicesList, 1);
      

            System.out.println(result.toString());
            if (result.getResults() != null) {
                int canonicalRegId = result.getCanonicalIds();
                if (canonicalRegId != 0) {
                }
            } else {
                int error = result.getFailure();
                System.out.println(error);
            }

當執行完java project 訊息就會被送出,然後client端就會出現如下圖的推撥


在這邊附上 sample code
附註   java project 必須 import一些jar檔  檔案也會附在sample code裡
GCMSample為 client端
sendMessage為 server端

2014年1月22日 星期三

Robolectric介紹(一) shadow

Why Use Robolectric

因為使用android 模擬器跑test的速度實在是太慢了, Robolectric讓我們可以再IDE上面run TDD
run test的速度也快得多。

安裝的介紹在  Android 開發 (十六) 使用Mockito和Robolectric寫 test case 裡面有詳細的介紹

今天要介紹的是shadowOf的應用


What is shadowOf  

有時候 android並未提供某些method Robolectric的 shadowOf就提供的那些method供我們測試用
舉例來說

在MainActivity的  UI為


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

我們希望測試ImageView 的 src在程式中是否為 R.drawable.ic_launcher
我們寫出下面的測試

 public void TestDrawable()
 { 
  MainActivity activity = Robolectric.buildActivity(MainActivity.class).create().visible().get();
  ImageView img = (ImageView) activity.findViewById(R.id.img);
  ShadowImageView shadowImageView = Robolectric.shadowOf(img);
  assertEquals(Robolectric.shadowOf(
    shadowImageView.getDrawable()).getCreatedFromResId(),
    R.drawable.ic_launcher);
 }


上面是測試案例,先取得imageview接著
使用shadowOf 取得 shadowImageView
然後比較android:src 是否為  R.drawable.ic_launcher
使用test之後可以看到是綠燈。


shadowOf還有其他的功能
例如以下的程式


 @Override
 public void onClick(View arg0) {
  // TODO Auto-generated method stub
  startActivity(RecentActivityActivity.class);
 }

在click的時候切換到名為 RecentActivityActivity 的Activity
我們希望在測試的時候測試當click之後是否會切換到正確的activity


 @Test
 public void TestActivity()
 {
  MainActivity activity = Robolectric.buildActivity(MainActivity.class).create().visible().get();
  Button btn= (Button)activity.findViewById(R.id.btn);
  btn.performClick();
  ShadowActivity shadowActivity = Robolectric.shadowOf(activity);
  Intent startedIntent = shadowActivity.getNextStartedActivity();
     ShadowIntent shadowIntent =Robolectric.shadowOf(startedIntent);
  assertThat(shadowIntent.getComponent().getClassName(), equalTo(RecentActivityActivity.class.getName()));
 }

上面的程式碼使用 btn.performClick() 觸發 onclick event
接著取得 shadowIntent 並且  判斷新的 intent名稱是否為 RecentActivityActivity


另外一個常用的測試項目,判斷字串是否正確


 @Test
 public void TestHelloWorld()
 {
  MainActivity activity = Robolectric.buildActivity(MainActivity.class).create().visible().get();
  TextView txt = (TextView)activity.findViewById(R.id.txt);
  
  ShadowTextView shadowTextView = Robolectric.shadowOf(txt);

  assertEquals(shadowTextView.innerText(),activity.getString(R.string.hello_world));

 }

使用 shadowTextView 的 innerText 來判斷 字串是否相同
最後附上 sample code

2014年1月21日 星期二

Android 開發 (十七) 如何動態加入view

在作專案時,總是有必須動態增加view的情形
在這邊示範一個簡單的範例


  for(int i=0; i<10;i++){

   RadioButton btn  = new RadioButton(this);
   LinearLayout.LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
   btn.setLayoutParams(params);
   rootLayout.addView(btn);
  }

這是簡單的方式將RadioButton 加入 rootLayout裡  其中  rootLayout為RadioGroup

下圖為結果

上面是較為簡單的範例  假設今天我們必須customize我們的layout
就必須取得inflater取得inflater的方式有兩種

LayoutInflater inflater = LayoutInflater.from(this);
LayoutInflater LayoutInflater =  
             (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);

在取得 inflater之後就可以使用 findviewbyId去取得單一的view
下面有簡單的範例供參考

  for(int i=0; i<10;i++){
   LayoutInflater LayoutInflater =  
             (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
   View view = LayoutInflater.inflate(R.layout.custom_layout, null);
   TextView txt = (TextView)view.findViewById(R.id.txtview);
   txt.setText(String.valueOf(i));
   rootLayout.addView(view);
  }

上面的sample code在取得 inflater之後,使用 findviewbyId取得textView,並且設定了text
這樣就完成了客製化


最後附上 sample code

2014年1月19日 星期日

Android 開發 (十六) 使用Mockito和Robolectric寫 test case


Mockito library : https://code.google.com/p/mockito/w/list
Robolectric libary : https://github.com/thecodepath/android_guides/wiki/Robolectric-Installation-for-Unit-Testing

這兩個是支援寫test case的framwork,在這裡稍微描述如何安裝簡介
首先是安裝Robolectric
先下載 robolectric jar,  fest-util.jar  fest-android.jar.
接著再想測試的project 裡建立一個 test folder  我的project 為 simpleApp


接著建立一個 Test 的 Java project 這邊必須非常注意,
是java project , 不是 android  project , 也不是 android Junit project。 
在這裡我創建了一個SimpleTest2 project


接著按下next,然後在src那邊按下右鍵,然後 remove from build path,
接著按下Link Additional Source

接著選取想要測試的 project 的 test folder (也就是剛剛創建的simpleApp的test folder)


接著點到project的 tag 將要測試的project 加入  也就是 simpleApp 


接著將Robolectric的lib加入SimpleTest2 lib的folder


接著設定build path 如下圖


接著使用 add Jars 將lib folder內的 jars 加入


接著add library -> JUnit


 使用Junit4

接著add external Jars   android.jar  and maps.jar


接著點選 run -> run configure


接著將test runner 改成 Junit4



接著切換到Argument Tag 然後選取 other radio button 然後點選 workspace ->  
選取要測試的project  SimpleApp


multiple launcher 那邊 ,選取select one  之後選取 Eclipse Junit Launcher



接著在simpleTest2裡創建一個myActivitytest.java
sample code 為

@RunWith(RobolectricTestRunner.class)
public class MyActivityTest {

    @Test
    public void shouldHaveHappySmiles() throws Exception {
        String hello = new MainActivity().getResources().getString(R.string.hello_world);
        assertThat(hello, equalTo("Hello world!"));
    }
}  

接著使用 run as -> junit test  就會看到綠色條出現了
install Robolectric 就完成了


接著要安裝  mockito  ,mockito Download
我們可以再已經建構Robolectric上使用 mockito,方法很簡單
只需要將library依照剛剛加入library的方式加入就可以了  如下圖


接著我寫了一個class 

public class NetworkData
{
 private String name;
 
 public String getName()
 {
  return name;
 }
}

NetworkData 的資料必須在網路連接時才有辦法取得
所以我們必須使用Mock假裝資料回傳


package com.codepath.example.simpleapp.test;

import static org.junit.Assert.*;

import org.junit.Test;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;

import com.example.simpleapp.NetworkData;
import com.example.simpleapp.MainActivity;
import com.example.simpleapp.R;


@RunWith(RobolectricTestRunner.class)
public class MyActivityTest {

    @Test
    public void shouldHaveHappySmiles() throws Exception {
        String hello = new MainActivity().getResources().getString(R.string.hello_world);
        assertThat(hello, equalTo("Hello world!"));
    }


 @Test
 public void NetworkDataTest(){
  //arrange
  NetworkData i=mock(NetworkData.class);
  when(i.getName()).thenReturn("Hello");
  //act
  String result=i.getName();
  //assert
  assertEquals("Hello", result);
 }
}




在networkTest 裡  可以看到我mock了一個NetworkData,
然後在getName的時候會回傳Hello  ,接著只要確認結果是否跟預期一樣
可以看到綠燈再度亮起。

經過了千辛萬苦的設定,終於就完成了Mockito 和  Robolectric的設定
可以開始寫測試囉~


更多Robolectric的參考資料
http://robolectric.org/customizing.html

2014年1月17日 星期五

Android 開發 (十五) android 撰寫 test case

目標

我們要寫一個會將輸入的數字乘以二輸出的頁面

首先

我們要先create 我們的project
接著我們要create 我們的 test project



接著他要要我們選擇我們希望Test 的project


接著新增一個JUnit Test Case


setup 和 teardown 分別為  test開始前的前置作業以及結束時後置作業

接著開始我們寫test

public class MyTest extends TestCase {
        int expect = 6;
 Calculater cal ;
 public MyTest(String name) {
  super(name);
  
 }

 protected void setUp() throws Exception {
  super.setUp();
  cal = new Calculater();
 }
 
 @SmallTest
 public void testDoubleMul()
 {
  assertEquals(expect ,cal.execute(3));
 }
}


這是一個簡單的 test case 到目前為止
都還沒寫出Calculater這個class
在寫完test case 之後
創建 Calculater class


public class Calculater
{
 public int execute(int val)
 {
  return 0;
 }
}

接著run test case project  這時紅燈出現了......測試會失敗
(當然...我們根本還沒實作execute的功能)

接著我們實作這個class

public class Calculater
{
 public int execute(int val)
 {
  return val*2;
 }
}


之後再run一次 test  就出現綠燈了


恭喜各位
完成了實作簡單的測試,同時也實作了一次TDD的做法

最後稍微提一下寫test有個3A原則

Arrage:初始化目標物件   事前準備
Act:    :執行行為
Assert:檢查結果是否符合預期

TDD的做法是先寫測試案例,然後再去實作class,
在實作上其實有某些程度的難度,需要時間來熟練,
TDD的好處是通常可測試的class,耦合度也會相對較低。

2014年1月15日 星期三

Git(七) pull vs fetch

甚麼是git fetch

git fetch就是將遠端的資料update 到local端,簡單的說就是讓local端知道遠端最新的位置是在哪
舉個例子 如下圖
先不管server端的部分,
origin/hello2 為遠端的本地參照位置,hello2為目前實際的位置。

假設 遠端原本只有 aaa bbb ccc三個commit 
然後user A和B 皆使用 git clone , 他們的origin/hello2都會是在 ccc的位置,
hello2也會在ccc的位置

假設user B  commit 並 push 了 ddd,這時user A的 origin/hello2依然是在 ccc
那 user A 該如何更新origin/hello2到 ddd呢?
user A只需要使用 git fetch origin hello2 
這樣 user A的 origin/hello2就會更新到 ddd的位置

fetch 會去更新origin/hello2(本地參照)

解釋完git fetch之後,要解釋git pull就相對簡單許多



甚麼是git pull


git pull 實際作用為 git fetch  +  git merge

舉個例子

假設目前在遠端上面有人 commit 並push了  aaa 和 bbb
而我在本地端commit 了 ccc的功能

假設打入指令 git fetch origin hello2 這時會將origin/hello2 更新到bbb的位置
接著使用git merge origin hello2就會變成 如下圖
然而這個結果跟直接使用 git pull的結果是一樣的

git pull 不只會更新本地參照還會自動進行merge branch。


比較安全的用法

從server上拉資料之前  
總是先使用git fetch更新本地參照
接著先確認是否有其他的分支
若無其他分支,如下圖  則使用git pull  fast forward到 Origin/hello2


若有其他分支,如下圖
則檢查是否有需要merge 若有則使用 git merge。




結論

git fetch是相對較安全的做法,假設想要知道server上最新的版本但是還不想本地端還有一些功能還沒開發完,或是基於某些原因還不想merge的話可以使用git fetch,這樣本地只會取得server上最新版本但是並不會merge到本地端,

假設使用git pull 代表本地端功能已經開發完成可以進行合併,使用git pull就會直接將本地端跟server上做合併的動作。



2014年1月13日 星期一

Android 開發 (十四) sherlock actionBar library 使用

甚麼是sherlock actionBar   附上  範例App

下面試這個library的網站,在這裡我要簡單的介紹這個library的使用方法

http://actionbarsherlock.com/

why use actionbarsherlock

由於內建的actionbar限制很多,有許多特別的UI都無法達成,所以就找到這個library,他使得客製化變得非常方便。


如何使用actionbar

要使用這個library首先必須先將 project reference  引進,這在Android 開發 (十三) ViewPager 使用方法 有介紹如何引用,接著下面是簡單的範例


public class MainActivity extends SherlockActivity implements OnClickListener {

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  findViewById(R.id.btn).setOnClickListener(this);
  getSupportActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
}

 @Override
 public void onClick(View view) {
  if(view.getId()==R.id.btn){
      LayoutInflater inflator = (LayoutInflater)this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
      View v = inflator.inflate(R.layout.mylayout, null);
      ((TextView)v.findViewById(R.id.txt)).setText("123");
      LayoutParams params = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
      v.setLayoutParams(params);
      v.setBackgroundColor(Color.YELLOW);
      this.getSupportActionBar().setCustomView(v);
  }

 }
}

在onCreate時設定 displayoptions為 DISPLAY_SHOW_CUSTOM ,
接著在button click時將 actionbar設定成任意UI
這樣就完成了

附上  sample code


附註
如果希望能夠在actionbar上面有progressbar的功能
只需要使用

setSupportProgress(Window.PROGRESS_END);
setSupportProgressBarIndeterminateVisibility(true);

要使progressbar消失則使用

setSupportProgressBarIndeterminateVisibility(false);



2014年1月8日 星期三

Android 開發 (十三) ViewPager 使用方法

甚麼是ViewPager

許多使用滑動切換頁面的方式都是使用 ViewPager 完成的

底下是source code的下載網址,裡面也附有許多的sample
https://github.com/JakeWharton/Android-ViewPagerIndicator
要使用該sample
首先必須先將lib import 且 copy 到workspace
(步驟是 import -> exist android project -> 選擇路徑 ->  copy project into workspace)

接著必須將該lib加入reference的列表中
(步驟為在project 按右鍵 -> properties -> 選擇Android項目 ->在library那欄選擇 add 將lib 加入reference)

接著如果出現需要import R的錯誤就必須將local端的 android-support-v4.jar 覆蓋library的 android-support-v4.jar
基本上這樣之後使用 Clean Build 就可以build過了  sample code 的 import 也是相同的步驟


如何使用 ViewPager 


viewpager 需要一個PageAdapter,然後在 instantiateItem 可以客製化自己的UI
,記得必須destroyItem 否則會crash,如果需要顯是title的話就必須override 掉getPageTitle,
底下是PageAdapter的範例


class myPagerAdapter extends PagerAdapter {
 private List<String> mTitles;
 public myPagerAdapter(List<String> titles) {
  mTitles = titles;
 }

 @Override
 public int getCount() {
  // TODO Auto-generated method stub
  return mTitles.size();
 }

 @Override
 public Object instantiateItem(View container, int position) {
  // TODO Auto-generated method stub
  LayoutInflater inflater = (LayoutInflater) container.getContext()
    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
  int id;
  switch (position%3) {
  case 0:
   id = R.layout.firstpage;
   break;
  case 1:
   id = R.layout.secondpage;
   break;
  case 2:
   id = R.layout.thirdpage;
   break;
  default:
   id = R.layout.secondpage;
   break;
  }
  View view = inflater.inflate(id, null);
  ((ViewPager) container).addView(view, 0);
  return view;
 }
 @Override
 public void destroyItem(View container, int position, Object object) {
  // TODO Auto-generated method stub
  ((ViewPager) container).removeView((View)  object);
 }
 @Override
 public CharSequence getPageTitle(int position) {
  // TODO Auto-generated method stub
  return mTitles.get(position);
 }

 @Override
 public boolean isViewFromObject(View arg0, Object arg1) {
  // TODO Auto-generated method stub
  return arg0 == arg1;
 }


然後只需要在MainActivity裡設置 viewpager 和TabPageIndicator 即可

MainActivity.java

  mViewPager.setAdapter(new myPagerAdapter(titles));
  TabPageIndicator indicator = (TabPageIndicator)findViewById(R.id.indicator);  
        indicator.setViewPager(mViewPager); 


activity_main.xml

    <com.viewpagerindicator.TabPageIndicator
        android:id="@+id/indicator"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />

    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:layout_width="wrap_content"
         />


這樣viewpager就完成了
可左右滑動,或者按上方的title 變換頁面
附上  sample Code

2014年1月7日 星期二

Android 開發 (十二) ImageView scaletype介紹

ImageView scaletype 的特性顯示圖如下

    圖片size大於imageview                  圖片size小於imageview

















由結果圖可知

假設要顯示整張圖可使用 fitCenter  或 centerInside

fitCenter:  圖片按比例放大/縮小到view的寬度
centerInside:圖片按比例縮小到view的寬度

上半部  圖 : 452x302   view : 50x50
下半部  圖 : 16x15       view : 50x50



fixXY: 把圖片不按比例放大/縮小到view的大小
fitStart: 把圖片按比例放大/縮小到view的寬度,並顯示在view的上部分
fitEnd:  把圖片按比例放大/縮小到view的寬度,並顯示在view的下部分

上半部  圖 : 452x302   view : 50x50
下半部  圖 : 16x15       view : 50x50















matrix : 可用於矩陣繪製

敏捷開發(二) 站立會議

在敏捷開發中,站立會議是每天必開的會議,
對許多人來說"開會"是個非常可怕的事情,隨便一個議程就要一個小時跑不掉,
對許多人而言會議跟浪費時間掛上等。

然而對我而言,以前開會的確是很浪費時間,但是現在身在敏捷開發中,每一次會議都讓我感到非常的珍貴,其中一項就是站立會議。


甚麼是站立會議

站立會議顧名思義,就是站著開會,其實第一次開會的時候覺得很有趣,
感覺像是一堆人在那邊罰站,然後就從第一位開始一個接著一個報告,
內容基本上都是

昨天我做了甚麼,有沒有遇到甚麼問題,今天我打算做甚麼。
如果是小問題就拿出來大家稍為提供一些意見,沒有問題的話就換下一位



為何要站立會議

站立會議的目的就是讓其他人瞭解每個人目前的進度(尤其是PM),
然後有哪些人目前是比較有空閒,以便於做其他的安排,例如假設小明昨天完成了一項功能,今天沒其他的事情就可以派去幫忙解bug或者refactor,簡單來說這樣可以讓PM對於人力做較適當的安排。



站立會議的時程

我們參加會議的人數大約十個,但是會議時間通常不會超過十五分鐘,
通常都是兩三句話就帶過,如果有其他的問題通常會找幾個相關人員私下討論。



敏捷開發 = RD會變輕鬆?

站立會議讓大家互相了解目前的進度,其實對RD而言也是一種壓力,
假設兩個人負責相同的功能只是負責的平台不同,例如android 版 vs ios 版
假設一個人接近完成,另一個人還有一半功能尚未完成,這時候壓力就大了,
雖然說RD可自行訂定任務完成的時間,但是天有不測風雲,總是會有無法預測的事情會發生,個人認為敏捷開發需要有非常高的自主性,必須對做的事情負責,知道自己的不足就必須自己補足,進而追上平均的速率,然後關於寫story,最近認為寫story也是一門學問,不過這就是另一回事了~~ XD

2014年1月6日 星期一

Android 開發 (十一) include

include是幹嘛用的

include 讓我們可以重複使用相同的UI元件,如果需要使用同樣的UI不需要重覆寫在不同的XML檔,只需要寫一遍,然後使用include就可以重覆使用,這樣的好處是方便管理,而且UI有更改時可以一次改所有的頁面。

如何使用include

include_layout.xml


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

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical" >

  



    <TextView 

        android:id="@+id/txt"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        />

    <ImageView

        android:id="@+id/img"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:background="@drawable/ic_launcher"

        />

</LinearLayout>



首先先寫一份之後會共用的UI,
接著使用 include 在layout這個property中將 include_layout放入

    <include

        android:id="@+id/include"

        layout="@layout/include_layout"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        />



這樣UI就會自行顯示了。


如何取得include內的UI

要取得include內的UI只需要先取得 include 再使用該view去取得想取得的View
如下
View include_layout = (View)findViewById(R.id.include);
TextView txtView = ((TextView)include_layout.findViewById(R.id.txt));
txtView.setText("Include Text");


附上簡單的 sampleCode

Android 開發 (十) ViewStub

為何要使用viewstub

在寫android app時畫layout時必須要十分小心,否則將會導致顯示畫面不順(頓)或者out of memory。
在畫layout時即使設定View.visible(Gone),該View依然會消耗資源,解決的方法就是使用viewstub,ViewStub 是個非常輕量的view, 只有在Viewstub被inflat或visible之後才會佔空間
這也代表著,我們可以決定他何時要被inflat,我們可以在任何我們需要使用時才inflat他,例如button click之後 inflat 我們需要的UI

舉例來說

viewstub 可以用來實現 facebook的  ....更多
當按下時,就可以讀取新的view 進來
使用viewstub可以增加Page在初始化時的效率,避免畫了其他暫時不會被使用到的view,進而增加效率。

如何使用viewStub

<ViewStub
        android:id="@+id/stub"
        android:layout="@layout/mylayout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />
其中 layout 為 當我們inflat時產生的layout   例如

mylayout.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
  
    <TextView
        android:id="@+id/Mytext"
        android:text="Text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</LinearLayout>

接著在任意想要inflat 的時候使用

View view = viewStub.inflate();
TextView txtTextView = (TextView)view.findViewById(R.id.Mytext);
txtTextView.setText("What is this");


附上簡單的 sample code