TabStrip ?

TabWidget의 Strip(줄) 이미지를 "TabStrip"이라고 한다. 


    android:tabStripEnabled="false"

Default로 "true"로 세팅되어 있고, "false를 주게 되면 하단이 사라지게 된다. 


Android Level 8 API 이기 때문에,  2.1 이하 사용할 수 없는 점을 주의해야 한다. 

<?xml version="1.0" encoding="utf-8"?>

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

    android:orientation="vertical"

    android:layout_width="fill_parent"

    android:layout_height="fill_parent">


<Button

android:id="@+id/button"          

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="Button"

>

</Button>

<Button

android:id="@+id/button"          

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="Button"

/>

</LinearLayout>


위와 같이 android:id 가 동일한게 연속적으로 있을 때, 동작할까? 


동작한다 !

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

        button.setText("First");


위와 같은 Code로 접근하면 어떤 Button의 값이 바뀔까 ? 


그럼 두번쨰 버튼을 접근하려면 ?

<Button

android:id="@+id/button"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="Button"

>

</Button>


<LinearLayout

android:id="@+id/linear"

    android:orientation="vertical"

    android:layout_width="fill_parent"

    android:layout_height="wrap_content">

<Button

android:id="@+id/button"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="Button"

>

</Button>

</LinearLayout>



두번째 Button을 ViewGroup으로 묶어서, " ContentView -> ViewGroup -> 두번째 Button " 으로 묶어서 접근한 것이다. 
findViewsById() 같은 녀석이 있으면 좋을것 같은데, 일단 이렇게 해서 접근 가능합니다.




android:src="@android:drawable/ic_input_delete"


위와 같이 @drawble이 아닌 "@android:drawable"과 같이  접근하면 된다. 

사용가능한 리소스


참조 : http://androiddrawableexplorer.appspot.com/

What is a "Preference Activity" ? 

App. 등 설정에 관련된 UI 구성에 특화 되어 있는 Activity
설정 Item 구성 , Scrolling등을 지원합니다. 

Write XML Layouts

1. Activity Layout 


Class 관계를 살펴보면 PreferenceActivity는 ListActivity를 상속받는다. 
ListActivity에서 ListView를 접근할때,"@android:id/list"로 접근하기 때문에, Layout XML 작성시 유의 해야 한다. 

 <ListView android:id="@android:id/list"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:layout_weight="1"

        android:drawSelectorOnTop="false"/>


2. Preference Items 

addPreferenceFromResource() / addPreferenceFromIntent() 등을 통해 Preference Item을 삽입
나는 addPreferenceFromResource() 를 사용하는 것을 선호한다. 


위와 같이 Preference 관련 resource를 추가 한다. 

<PreferenceScreen

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

    android:title="@string/settings">


    <CheckBoxPreference

        android:key="alarm_in_silent_mode"

        android:title="@string/alarm_in_silent_mode_title"

        android:summary="@string/alarm_in_silent_mode_summary" />


    <VolumePreference

        android:title="@string/alarm_volume_title"

        android:summary="@string/alarm_volume_summary"

        android:dialogTitle="@string/alarm_volume_title"

        android:persistent="false"

        android:streamType="alarm" />


    <ListPreference

        android:key="snooze_duration"

        android:title="@string/snooze_duration_title"

        android:entries="@array/snooze_duration_entries"

        android:entryValues="@array/snooze_duration_values"

        android:defaultValue="10"

        android:dialogTitle="@string/snooze_duration_title" />


    <ListPreference

        android:key="volume_button_setting"

        android:title="@string/volume_button_setting_title"

        android:dialogTitle="@string/volume_button_dialog_title"

        android:entries="@array/volume_button_setting_entries"

        android:entryValues="@array/volume_button_setting_values"

        android:summary="@string/volume_button_setting_summary"

        android:defaultValue="2" />


</PreferenceScreen>


각각의 Entry는 View Entry등과 마찬가지로, Android Class와 Match 된다. 

Write your Code

1. 구현해야 할것들

다른 Activity 사용법과 마찬가지로, 만약 "Setting"화면이 필요하다고 하면,
"PreferenceActivity"를 상속받는 "SettingActivity"를 생성합니다. 

onCreate 
- addPreferenceFromResource를 통해 Preference Item을 구성
onPreferenceTreeClick
- PreferenceClick Event 처리
onPreferenceChanged
- Preference Value Change 처리

2. Source Code

 @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        addPreferencesFromResource(R.xml.settings);

    }



    @Override

    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,

            Preference preference) {

        if (KEY_ALARM_IN_SILENT_MODE.equals(preference.getKey())) {

            CheckBoxPreference pref = (CheckBoxPreference) preference;

            int ringerModeStreamTypes = Settings.System.getInt(

                    getContentResolver(),

                    Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0);


            if (pref.isChecked()) {

                ringerModeStreamTypes &= ~ALARM_STREAM_TYPE_BIT;

            } else {

                ringerModeStreamTypes |= ALARM_STREAM_TYPE_BIT;

            }


            Settings.System.putInt(getContentResolver(),

                    Settings.System.MODE_RINGER_STREAMS_AFFECTED,

                    ringerModeStreamTypes);


            return true;

        }


        return super.onPreferenceTreeClick(preferenceScreen, preference);

    }


    public boolean onPreferenceChange(Preference pref, Object newValue) {

        final ListPreference listPref = (ListPreference) pref;

        final int idx = listPref.findIndexOfValue((String) newValue);

        listPref.setSummary(listPref.getEntries()[idx]);

        return true;

    }


위의 코드는 실제로 addPreferenceFromResource를 통해서, Preference를 구성하고,
onPreferenceTreeClick을 이용하여, Preference Click 이벤트를 처리 하는 부분이다. 

이처럼 PreferenceActivity를 이용하면, 쉽게 Setting 화면을 구성할 수 있다. 



@required

iPhone 3GS or before : 57px 

@optional
iTunesArtWork : 512px - Project에 포함시키지 않아도, App등록시 가능함.
iPhone 4 or later : 114px
Ipad : 72px

Spotlight for iPhone 3GS or before : 29px
Spotlight for iPhone 4 or later : 58px
Spotlight for iPad : 50px

iOS가 점점 지저분해진다. 
Accessibility Inspector는 iOS Simulator에서만 사용이 가능하다. 

설정(Settings) -> 일반(General) -> 손쉬운사용(Accessibility) 에서 설정 가능하다. 


Accessibility Inspector를 사용하면, 위와 같이 나오는데, 현재 Inspecting은 Disable상태이다. 
좌측의 "x" 버튼을 누르면 Inspector View가 펼쳐지면서 활성화가 된다.


Inspector 를 활성화 한 상태에서 "버튼"등의 UI Component를 터치하면, 아래와 같이 정보가 나온다. 


UIAccessibilityProtocol 에 따라 정의 된 값이 아래와 같이 나오면,
프로그래밍 적으로 지정해준적이 없는 경우에는 보통 Default가 나온다. (e.g) UILabel의 꼬리표는 UILabel의 Text가 Default다.)

Voice Over를 지원하는 App을 만들거나 Instruments를 이용한 UI Automation Script를 작성할 때, 유용하게 사용할 수 있다. 


Preface

테스트 자동화에 대한, 필요성, 중요성은 이제 아무리 말해도 입아프다.

Instruments를 이용한 UI Test 자동화는 TDD로 사용하기에는 조금 부족하다. 
Xcode에 Integrated되어 있지 않기 때문에, Test Code와 Real Code간의 전환등이 쉽지 않고, 
Build & Run의 Step이 자동화 되어 있지 않기에 여러모로 불편함이 많다.

개발 부서에서도 당연히 사용하게 되겠지만, QA부서에서의 사용성이 더 부각 된다고나 할까,
여튼 한번 시작해보겠습니다.

UI Automation with Instruments

UIKit 기반 Application용 Test 자동화
Accessibility를 통한 UI Element 접근
Test Script로 JavaScript사용

Instruments에서는 JavaScript를 사용한다. 
JavaScript는 이해하기 쉽고, 널리 알려져 있는 Language다. 즉 그만큼 접근성이 좋다는 뜻이다.

How to automate an Application

1. Automation Template을 이용한 Trace Document 생성


Instruments Template중에 Automation Template을 사용하여 Trace Document를 생성한다. 

2. Target 설정

Target으로는 실제 Device , iOS Simulator둘다 사용할 수 있는데, 이포스트에서는 iOS Simulator를 사용한다. 


"Choose Target"을 선택하여, Test 대상이 될 Application Package(XXXX.app) 을 선택
 Target을 선택한 후 Record 버튼을 누르면 iOS Simulator가 나타남과 동시에 App이 실행된다.

이 포스트에서 사용한 Sample Project는 Apple Developer 사이트 에서 제공하는 Recipes Sample이다.


3. Recipes를 추가하는 Test Script 만들기


Script를 불러와서 Edit는 가능하지만, Create할 수 없다.
적당한곳에서 Test.js파일을 생성하고 불러 온후 "Edit"를 눌러서 Editor를 켠다.


우리가 수행할 Test Phase는 아래와 같다. 

"+" UIBarButtonItem을 터치 
Recipe Name 입력
"Done" UIBarButtonItem 터치
"Recipes" 터치
추가한 Recipe 확인

"+" UIBarButtonITem 터치하기

   UIATarget.localTarget().frontMostApp().mainWindow().navigationBar().buttons()["Add"].tap();

    app.navigationBar().withName("Add Recipe");


Recipe Name 입력

UIATarget.localTarget().frontMostApp().mainWindow().textFields()[0].setValue(name);

"Done" UIBarButtonItem 터치

target.delay(2);

app.navigationBar().rightButton().tap();


"Recipes" 터치

    target.delay(2);

    app.navigationBar().leftButton().tap();


추가한 Recipe 확인

 app.navigationBar().withName("Recipes");

    var cell = app.mainWindow().tableViews()[0].scrollToElementWithPredicate("name beginswith '"+name+"'");

    

    if(cell.isValid() ) {

        UIALogger.logPass(testName);

    }

    else {

        UIALogger.logFail(testName);

    }


TestScript작성법은 다루지 않았는데, 다음 포스트에 할 예정이다. 


"UI Automation Reference" "Instruments User Guide" 을 참고 하면 어렵지 않게 이해할 수 있다. 


4. UI Test




5. Test Result



스크립트 로그 창에, Log Pass/Fail이 표기 된다. 






부팅시에 코드를 실행하는것은 간단합니다.

Android는 Booting시에 BOOT_COMPLETED라는 BroadCast를 생성합니다.
그걸 Catch하는 Receiver를 구현하면됩니다.

1. Permission 설명

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />


2. Receiver에 IntentFilter 걸기

 <intent-filter>

                <action android:name="android.intent.action.BOOT_COMPLETED" />

 </intent-filter>


3. Receiver 구현

@Override

    public void onReceive(Context context, Intent intent) {

}



BroadcastReceiver Class를 상속받는 Receiver Class를 하나 만들고,


onReceiver Method를구현해주면 됩니다. 


참 쉽죠? ㅎㅎ



[2009-11-02 16:28:51 - Android]Android Launch!
[2009-11-02 16:28:51 - Android]adb is running normally.
[2009-11-02 16:28:51 - Android]Could not find ********.apk!

라는 녀석이 개발하다 보면 가끔 발생된다. 

이거 같은 경우는 Build 가 제대로 안되는 경우인데,

이경우 Android Project의 .classpath 파일이 꼬인 경우가 많다. 

<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry kind="output" path="bin"/>
</classpath>

Android Project의 경우 일반적으로 위와 같이 되어야 하고,

Android Test Project의 경우는 아래와 같이 Dependancy가 추가 되어야한다.

<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry combineaccessrules="false" kind="src" path="/SomeAndroidProject"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry kind="output" path="bin"/>
</classpath>

수정하고, 나면 apk 파일이 잘 생성됨을 확인 할 수 있따. 

왜 Sprint Demo를 해야 하는 가 ?

Demo를 한다는것은 "지속적으로 완성되어 가고 있는 Product를 보여주는 것" 이다.

Project  구성원들은 이루어 낸 결과물에 만족감을 느낄 수 있다. 
만약 만족스럽지 못하다면 자극을 받을 것이다. 결국 동기 부여가 된다는 이야기다. 

Project구성원들이 Project를 확인 할 수 있다. 맡은 부분이외의 것들을 Demo를 통해 확인 할 수 있다.

Product에 대한 Feedback을 받을 수 있다. 매 Sprint 마다 피드백을 받음으로,  Product가 Agile해 질 수 있다. 
프로젝트가 산으로 가는 지 바다로 가는지 확인 할 수 있다.

점진적으로 프로젝트 완성도가 높아진다. 
Demo를 위해선 부분 기능들을 100% 완료 해야한다.  90%해놓고 나중에 하는 일이 없어진다.
왜냐하면 고객에게 보여줘야 하기 떄문이다.

Sprint Demo는 어떻게 해야 할까?

Sprint Demo는 해당 Sprint 목표에 충실한다. 
간단한 버그, 사소한 개선사항등은 구지 보여 줄 필요 없다. 
해당 목표를 명확히 프로젝트 관계자들에게 보여주면 된다. 

데모는 Presentation이 아니다. 
정교하게 보여주기 위해, 시간을 많이 들일 필요 없가 없다. 돌아가는 Product만으로 충분하다. 
기술적인 내용도 자제하고, Product가 어떻게 돌아 가는지만 보여주면 충분하다. 그것이 Point다. 

+ Recent posts