태그 보관물: Android

안드로이드 공유 인텐트(Share Intent) 목록 필터링 하기

안드로이드 앱에서 데이터를 공유(Share)하는데, 이 데이터를 받아주는 써드 파티 앱을 실행하는 데는 인텐트(Intent)를 사용한다. 그리고 텍스트 데이터를 공유하는 데 사용하는 코드는 아래와 같다. 아래의 2번째 줄이 공유하는 데이터의 MimeType이다.

Intent intent = new Intent(android.content.Intent.ACTION_SEND);
intent.setType("text/plain");

실제로 위 인텐트를 사용해서 텍스트 데이터를 공유하는 예제를 살펴보면 다음과 같다.

String subject = "메시지 제목";
String text = "메시지 내용은\n다음줄에서..";

Intent intent = new Intent(android.content.Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_SUBJECT, subject);
intent.putExtra(Intent.EXTRA_TEXT, text);
Intent chooser = Intent.createChooser(intent, "타이틀");
startActivity(chooser);

이 예제코드를 사용해서 텍스트를 공유하는데, 이 데이터를 받아주는 앱들을 안드로이드 운영체제에 요청하면 아래와 같은 결과를 보게 된다.

Screenshot_2015-12-23-23-46-06

이 화면을 보면, 개발자가 텍스트를 공유하기 원하는 안드로이드 앱(종종 SNS 서비스 만을 보여주길 원한다)을 선택하기에 너무나 많은 앱을 보게 된다. 그래서 데이터를 공유하는데, 보이기 원하는 앱만을 필터링해서 보여주는 방법을 살펴보자.

여기에서 사용하는 방법은 패키지 매니저를 사용해서 데이터를 공유하는 인텐트를 처리할 수 있는 액티비티 정보를 가져오고, 이 정보를 사용해서 앱 이름 또는 패키지 이름으로 필요한 앱만 화면에 보이게 한다.

아래는 화면에 보이기 원하는 앱 이름 또는 패키지 이름으로 보이도록, 원하는 인텐트를 필터링 하는 예제이다.

/**
 *
 * @param name 패키지나 앱 이름
 * @param subject 제목
 * @param text 내용
 *
 * @return
 */
private Intent getShareIntent(String name, String subject, String text) {
    boolean found = false;

    Intent intent = new Intent(android.content.Intent.ACTION_SEND);
    intent.setType("text/plain");

    // gets the list of intents that can be loaded. 
    List<ResolveInfo> resInfos = getPackageManager().queryIntentActivities(intent, 0);

    if(resInfos == null || resInfos.size == 0)
        return null;

    for (ResolveInfo info : resInfo) {
        if (info.activityInfo.packageName.toLowerCase().contains(name) ||
                info.activityInfo.name.toLowerCase().contains(name) ) {
            intent.putExtra(Intent.EXTRA_SUBJECT, subject);
            intent.putExtra(Intent.EXTRA_TEXT, text);
            intent.setPackage(info.activityInfo.packageName);
            found = true;
            break;
        }
    }

    if (found)
        return intent;

    return null;
}

이제 위에서 살펴본 메서드를 사용해서 원하는 앱을 보여주기 위한 코드를 살펴보자. 아래의 코드를 살펴보면, “facebook”, “twitter”, “com.google.android.apps.plus”, 그리고 “gmail”로 필터링해서 targetedShareIntents에 인텐트 목록을 저장해서, 이 목록에 있는 인텐트 앱을 화면에 보여준다.

String subject = "메시지 제목";
String text = "메시지 내용은\n다음줄에서..";

List targetedShareIntents = new ArrayList<>();

// 페이스북
Intent facebookIntent = getShareIntent("facebook", subject, text);
if(facebookIntent != null)
    targetedShareIntents.add(facebookIntent);

// 트위터
Intent twitterIntent = getShareIntent("twitter", subject, text);
if(twitterIntent != null)
    targetedShareIntents.add(twitterIntent);

// 구글 플러스
Intent googlePlusIntent = getShareIntent("com.google.android.apps.plus", subject, text);
if(googlePlusIntent != null)
    targetedShareIntents.add(googlePlusIntent);

// Gmail
Intent gmailIntent = getShareIntent("gmail", subject, text);
if(gmailIntent != null)
    targetedShareIntents.add(gmailIntent);

Intent chooser = Intent.createChooser(targetedShareIntents.remove(0), "타이틀");
chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedShareIntents.toArray(new Parcelable[]{}));
startActivity(chooser);

자 이제 화면을 살펴보자.

Screenshot_2015-12-23-23-47-01

이제 앱에서 데이터를 전송하기 원하는 앱을 몇 개만 보게된다. 그래서 Tell a Friend와 같은 메뉴가 있다면 SNS 카테고리에 해당하는 앱만을 보여줄 수 있고, MimeType이 이미지인 경우에도 클라우드 서비스(Gdrive, Dropbox, Onedrive, Box 등)만을 데이터 공유에 사용할 수 있다.

이 방식의 단점은 아무래도 필터링을 해야 하니, 반응이 약간 느리다. 이 단점은 미리 캐시해서 해결할 수도 있다.

* Reference

– http://stackoverflow.com/questions/13286358/sharing-to-facebook-twitter-via-share-intent-android

v7 Support Preference 라이브러리 사용하기

구글의 안드로이드는 매년 새로운 버전이 나오면서 새로운 기능을 제공하는 API를 발표한다. 새로운 버전이다 보니 이전의 버전에서 새로운 기능 또는 UI 등을 사용하기 힘든 상황이다. 그래서 구글은 Android Support 라이브러리(Support 라이브러리)들을 제공해서 하위 버전에서도 상위 버전의 기능과 UI를 사용할 수 있게 지원한다. 그래서 안드로이드 개발자들은 기본 API와 더불어 Support 라이브러리를 살펴봐야 하는 이중고(?)를 겪게 있다. 이 글에서는 Support 라이브러리 중의 하나인 v7 버전의 preference 라이브러리를 사용하는 방법과 약간의 팁을 살펴보자.

preference_01
그림 1, Android SDK Manager 툴 일부

그림 1에서 Android Support Library는 23.1.1 버전이 설치된 것을 확인할 수 있다. 그래서 Support 라이브러리 버전은 v4든 v7이든 23.1.1 버전을 사용한다는 것도 알 수 있다. 이 라이브러리는 %android_home%\extras\android\support 폴더에서 확인할 수 있다.

1. 환경설정

그래들(Gradle) 빌드 파일(Build.gradle)에 아래와 같은 의존 라이브러리가 필요하다.

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])

    compile 'com.android.support:support-v4:23.1.1'
    compile 'com.android.support:recyclerview-v7:23.1.1'
    compile 'com.android.support:appcompat-v7:23.1.1'
    compile 'com.android.support:preference-v7:23.1.1'
}

2. Preference xml 파일 추가

안드로이드에서 설정 화면 역시 xml 파일로 구성한다. 다른 UI와 다르게 목록의 형태로만 구성할 수 있도록 되어 있다. 기존의 Preference는 내부에서 ListView를 사용하고 있지만, v7의 경우에는 RecyclerView를 사용한다. 그래서 그래들 빌드 파일의 의존성에 v7의 다른 라이브러리가 필요한 것이다. 그리고 아래와 같은 설정 파일은 app/res/xml/ 아래에 위치해야 한다.

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <android.support.v7.preference.PreferenceCategory android:title="카테고리1">
        <android.support.v7.preference.SwitchPreferenceCompat
            android:key="pf_folder_display_option"
            android:icon="@mipmap/ic_launcher"
            android:title="카테 타이틀 01"
            android:summary="summary"
            android:defaultValue="false" />

        <android.support.v7.preference.SwitchPreferenceCompat
            android:icon="@mipmap/ic_launcher"
            android:title="카테 타이틀 02"
            android:summary="summary"
            android:defaultValue="false" />

        <android.support.v7.preference.SwitchPreferenceCompat
            android:icon="@mipmap/ic_launcher"
            android:title="summary"
            android:summary="summary"
            android:defaultValue="true" />
    </android.support.v7.preference.PreferenceCategory>

    <android.support.v7.preference.PreferenceCategory android:title="카테고리2">
        <android.support.v7.preference.SwitchPreferenceCompat
            android:title="SwitchPreferenceCompat 타이틀"
            android:summary="SwitchPreferenceCompat Summary"
            android:icon="@mipmap/ic_launcher"
            android:defaultValue="true" />
    </android.support.v7.preference.PreferenceCategory>

    <android.support.v7.preference.PreferenceCategory android:title="카테고리3">
        <android.support.v7.preference.Preference
            android:title="타이틀 02"
            android:summary="Summary"
            />

        <android.support.v7.preference.Preference
            android:title="타이틀 03"
            android:summary="Summary"
            />

        <android.support.v7.preference.Preference
            android:title="@string/app_name"
            android:summary="Summary"
            />
    </android.support.v7.preference.PreferenceCategory>
</android.support.v7.preference.PreferenceScreen>

3. 액티비티(Activiy) 예제

예제 프로젝트의 SettingsActivity.java 소스는 아래와 같다. 이 소스는 간단하게 아래의 PreferenceFragmentCompat을 상속해서 구현하고 있는 클래스로 화면을 구성하는 소스이다.

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        getSupportFragmentManager().beginTransaction()
                .replace(android.R.id.content, new SettingsFragment(), null).commit();
    }
}

4. SettingsFragment 예제

이 클래스는 PreferenceFragmentCompat를 상속해서 실제 Preference 화면을 구성하는 xml을 로딩하는 소스이다. 기존에 사용하던 PreferenceFragment에서는 onCreate() 메서드에서 xml을 로딩했지만, PreferenceFragmentCompat 에서는 onCreatePreferences() 메서드를 사용한다.

import android.os.Bundle;
import android.support.v7.preference.PreferenceFragmentCompat;

public class SettingsFragment extends PreferenceFragmentCompat {
    @Override
    public void onCreatePreferences(Bundle bundle, String s) {
        addPreferencesFromResource(R.xml.settings);
    }
}

5. 스타일(styles.xml) 추가

위 과정이 끝나면 테마에 preference 테마를 추가할 필요가 있다. 아래의 수정된 styles.xml 에서 AppTheme의 preferenceTheme 요소의 값으로 @style/PreferenceThemeOverlay를 사용하고 있는 것을 알 수 있다.

<resources>
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <item name="preferenceTheme">@style/PreferenceLocalTheme</item>
    </style>
    <style name="PreferenceLocalTheme" parent="@style/PreferenceThemeOverlay" />
</resources>

이제 위 과정의 결과로 아래와 같은 멋진(?) 화면을 확인할 수 있다.

preference_02
그림 2, v7 Preference 라이브러리를 사용한 설정 화면

그림 2를 살펴보면, 롤리팝의 머터리얼 디자인 테마와 홀로 테마가 섞여 있다는 것을 알 수 있다. 그래서 조금 더 머터리얼 디자인의 모습을 가지도록 UI를 변경해 보자.

6. 스타일(style.xml) 변경

위 결과 화면을 보면, v7 Preference 기본 UI가 머터리얼(Material) 디자인이라고 보기에는 구리다. 그래서 스타일을 변경해서 UI를 개선하는 방법을 살펴보자.

6.1 android.support.v7.preference.PreferenceCategory의 Text 스타일 변경

아래와 같이 PreferenceCategory에 보여지는 TextView의 스타일을 변경한다.

<style name="ListSeparatorTextView">
  <item name="android:textSize">16sp</item>
  <!--item name="android:textStyle">bold</item-->
  <item name="android:textColor">@color/accent</item>
  <item name="android:paddingTop">16dp</item>
  <item name="android:layout_marginBottom">16dp</item>
</style>

6.2 Preference의 Title과 Summary 스타일 변경

이 Preference의 Title은 textAppearanceLarge를 style로 사용하고 있고, Summary는 textAppearanceMedium을 style로 사용하고 있다. 그래서 위 설정화면의 Title과 Summary가 크게 느껴진다. 그래서 이 두개의 스타일을 변경해 보자.

<style name="LargeText">
   <item name="android:textSize">16sp</item>
</style>
<style name="MediumText">
   <item name="android:textSize">11sp</item>
   <item name="android:typeface">sans</item>
</style>

위 과정으로 변경된 전체 styles.xml은 아래와 같다.

<resources>
    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <item name="preferenceTheme">@style/PreferenceLocalTheme</item>
    </style>

    <style name="AppThemePreference" parent="AppTheme">
        <item name="android:listSeparatorTextViewStyle">@style/ListSeparatorTextView</item>
        <item name="android:textAppearanceLarge">@style/LargeText</item>
        <item name="android:textAppearanceMedium">@style/MediumText</item>
    </style>

    <style name="PreferenceLocalTheme" parent="@style/PreferenceThemeOverlay" />

    <style name="ListSeparatorTextView">
        <item name="android:textSize">16sp</item>
        <!--item name="android:textStyle">bold</item-->
        <item name="android:textColor">@color/colorPrimary</item>
        <item name="android:paddingTop">16dp</item>
        <item name="android:layout_marginBottom">16dp</item>
    </style>

    <style name="LargeText">
        <item name="android:textSize">16sp</item>
    </style>

    <style name="MediumText">
        <item name="android:textSize">11sp</item>
        <item name="android:typeface">sans</item>
    </style>

</resources>

위 스타일로 변경된 스타일의 화면은 다음과 같다.
preference_03

위 스타일에서 설정을 가지고 있는 액티비티는 AppThemePreference를 테마로 사용하면 된다. 이 테마안에 보면 android:textAppearanceLarge나 android:textAppearanceMedium이 테마 전역으로 사용하는 요소이기 때문에 이 테마를 사용하는데는 주의를 해야 한다.

안드로이드 앱 개발 동영상 강좌

안드로이드 스튜디오로 안드로이드를 개발하는 동영상 강좌이고, 제목은 “Android Apps Development Tutorial for Beginners” 이다. 이 강좌는 안드로이드 스튜디오 설치부터 시작해서, 안드로이드 구성요소들의 사용방법, 그리고 libGDX를 사용해서 아주 간단한 게임을 만드는 내용으로 구성되어 있다. 유투브에 있는 안드로이드 강좌 중에 가장 훌륭한 퀄리티를 보여준다. 단점으로는 영어로 설명한다는 것이지만 영어도 같이 공부할 겸 보면 아주 유익한 시간이 될 것 같다.

아래는 개별 강좌이다.

Android Apps Development Tutorial for Beginners (1)

Android Apps Development Tutorial for Beginners (2)

Android Apps Development Tutorial for Beginners (3)

Android Apps Development Tutorial for Beginners (4)

Android Apps Development Tutorial for Beginners (5)

Android Apps Development Tutorial for Beginners (6)

안드로이드 Pair 클래스의 이점에 대해서..

안드로이드 API를 살펴보다 보면 Pair 클래스라는 것을 볼 수 있다. 이 클래스는 안드로이드의 java.util 패키지 그리고 android.support.v4.util 패키지에서 확인할 수 있다. 이 클래스들의 소스를 확인해 보면 같은 구현체라는 것을 알 수 있다.

API 문서의 설명을 보면 “두 객체의 튜플을 다른 객체에 전달하기 쉽게 사용하는 컨테이너”라고 정의한다. 이 클래스는 C++(http://www.cplusplus.com/reference/utility/pair/), 자바는 외부 라이브러리인 아파치 Common Lang 패키지(org.apache.commons.lang3.tuple.Pair<L,R>), 그리고 C#의 경우 Tuple 이나 KeyValuePair(http://www.dotnetperls.com/tuple-keyvaluepair) 클래스가 같은 기능을 제공하고 있다.

이 글에서는 Pair 클래스를 사용해서, 어느 부분에 사용하면 좋은지 그리고 이 클래스를 사용해서 기존 코드보다 조금 더 간결한 코드를 만들 수 있는 방안에 대해 살펴보자.

1. I/O(네트워크 등) 요청의 응답 처리

많은 안드로이드 앱이 로직을 처리하기 위해서 네트워크로 데이터를 요청하고 수신한다. 종종 네트워크로 데이터 목록을 요청하는 경우 다음과 같은 코드를 사용하는 경우를 볼 수 있다. 아래에서 사용자 신용카드 목록을 가져오는 로직을 예로 살펴보자.

public ArrayList<CreditCard> getCreditCards() {
    ...
    ...
}

이 메서드를 호출하는 객체가 반환받을 수 있는 결과의 경우 수는 null 또는 ArrayList<CreditCard>의 결과가 될 것이다. 그리고 Activity나 Fragment에서 호출해서 받은 결과가 null 이면, “알수 없는 에러” 또는 “에러가 발생했으니 좀 있다가 다시 해라“등의 모호한 에러 메시지를 전달하게 되는 경우를 쉽게 볼 수 있다.

그래서, 위 문제를 개선하기 위해서 모델(Model) 클래스에 에러코드를 포함하는 형태를 취하는 경우도 종종 볼 수 있다.

public CreditCardsModel {
     int errorCode;
     ArrayList<CreditCard> creditCards;
     ...
     ...
}

public CreditCardsModel getCreditCards() {
     ...
     ...
}

결국 이 상황은 래퍼 모델(Wrapper Model)의 추가하게 해서 불 필요한 클래스를 생성하게 한다. 그래서 위 래퍼 모델을 생성하지 않고도 쉽게 모델로 처리할 수 있는 방법으로 Pair 클래스를 사용할 수 있다.

/**
* Created by mcsong on 10/22/15.
*/
public class GetCreditCardImpl {

    public static GetCreditCardImpl newInstance() {
        return new GetCreditCardImpl();
    }

    public Pair<Integer , ArrayList< CreditCard>> getCreditCards() throws Exception {
        ArrayList <CreditCard> creditCards = new ArrayList<>() ;

        String rUrl = ".........................../data.json" ;

        Pair <Integer, String> responseValue = HttpRequest. doGetString( rUrl);
        if (responseValue == null)
            return Pair.create( HttpConstant.RETURN_CODE_ERROR, creditCards);

        if (responseValue. first != 200 )
            return Pair.create( responseValue.first , creditCards) ;

        JSONObject jObject = new JSONObject(responseValue. second);
        JSONArray jArray = jObject .getJSONArray( "results");

        for (int i = 0; i < jArray .length() ; i++ ) {
            creditCards .add( CreditCard.parse( jArray.getJSONObject (i))) ;
        }

        return Pair.create( responseValue.first , creditCards) ;
    }

}

이 클래스는 신용카드 목록을 가져오는 구현(Impl)클래스이고, getCreditCards() 메서드에서 사용자의 신용카드 목록을 요청(Http 요청)하고, 받은 결과를 Pair<Integer, String> 객체에 담아서, 이 객체의 결과로 받은 결과(Json 문자열)를 앱이 사용하는 모델로 변경(CreditCard)해서 전달하고 있다.
이 소스로 결과 코드를 처리하기 위해서 사용했던 CreditCardsModel을 사용하지 않고 기존에 CreditCard 모델만 사용해서 목록을 온전히(에러 코드에 따른 상황 처리 포함) 처리할 수 있다.

아래 이미지는 개인적으로 자바나 안드로이드 프로젝트를 하면서 로직을 처리하는 기본 구조로 사용하는 간단한 클래스 다이어그램이다.
logic_uml
이 예에서 알 수 있는 것은, 로직을 처리하는 클래스에서 Pair 객체를 사용하면 기존에 사용하던 형태보다 비교적 간결한 형태로 로직을 구현할 수 있다는 것이다.

2. 한 번에 두 개 이상의 결과 처리

자바는 기본으로 반환 결과를 2개 이상 제공하지 않는다. 그래서 2개 이상의 결과를 반환받기 위해서는 객체나 배열의 형태를 보이게 된다. 그중에 2개의 결과가 필요한 경우를 종종 볼 수 있다. 자바와 가장 비슷하다는 C#의 경우에는 이미 out 키워드를 제공해서 두 개 이상의 결과를 쉽게 처리할 수 있다.

이 상황의 간단한 예제로 2개의 숫자를 입력받아서, 입력받은 숫자 중에 하나로 다른 입력값을 나눈 몫과 나머지가 필요하고, 몫과 나머지는 인캡슐되어 있는 클래스가 제공한다고 가정해 보자. 이 상황에서는 일반적으로 몫을 가져오는 메서드, 나머지를 가져오는 메서드 2개를 호출해서 구현하게 된다. 이런 형태를 구현하는 예제를 살펴보자.

public static Pair<Integer , Integer> divide(int src1 , int src2 ) {
    int v1 = src1 /src2;
    int v2 = src1 % src2;

    return Pair.create( v1, v2 );
}

위 소스는 src1을 src2로 나눈 결과(몫과 나머지)를 한꺼번에 반환하는 메서드이다.
Pair <Integer, Integer> result = divide( 5, 2 );
result.first : 몫
result.second : 나머지
위 예제는 divide() 메서드를 호출해서 받은 결과를 사용하는 예를 보여준다.

이 글에서 Pair를 사용해서 기존 코드보다 간결하게 로직을 처리하는 형태와 한 번에 두 개 이상의 결과를 처리하는 형태를 살펴봤다. 이 클래스는 가뜩이나 장황(verbose)하다는 자바의 코드를 약간 이나마 간결하게 사용할 수 있게 도움을 줄 수 있기에 잘 사용하면 코드를 개발하고 유지보수 하는 데 많은 도움을 줄 수 있겠다.

안드로이드 문자열에 개수(단/복수)를 제대로 보여주자.

안드로이드(Android)에서 텍스트뷰(TextView)와 같은 위젯에 문자열(String)을 보여주는데 지역화(Localization)가 잘 되어 있다면 문자열 파일(/res/strings.xml)에서 문자열을 가져와서 보여줄 것이다. 이 문자열 파일을 사용해서 대부분의 지역화를 무리 없이 해결할 수 있다. 하지만 숫자가 문자열에 있는 경우 특정 언어(영어와 같은 단/복수가 구분된 경우)에서는 지역화가 문자열 파일만 사용해서는 로직이 복잡해지게 된다(한글의 경우에는 단/복수를 구분이 명확하지 않기에 비교적 간결한 편임). 그래서 안드로이드에서 어떻게 단/복수를 구분해서 사용하는지 살펴보자.

1. 스트링을 개수로 분리해서 사용하는 방법
– 이 형태를 사용하면 소스 코드에서 개수에 따른 분기가 필요해지는 단점이 있다.

<string name="item_1">I have one item.</string>
<string name="item_2">I have two items.</string>

– 아래와 같이 분기하는 로직이 필요하다.

if(count == 1)
  	tv01.setText(getString(R.string.item_1));

if(count == 2)
	  tv02.setText(getString(R.string.item_2));

2. 한 개의 스트링으로 여러 개수의 메시지를 처리하는 방법
– 이 형태를 사용하면 소스 코드는 간결해지나, 메시지가 모호해서 좋지 않은 UX를 제공하게 된다. 필자의 경우 이런 형태로 처리한 경험이 있다.

<string name="item_count">I have %1$s item(s).</string>

– 분기하는 로직이 필요없어 간결하다.

tv01.setText(String.format(getString(R.string.item_count), count));

3. 스트링 배열의 형태로 plurals 요소를 사용하는 방법
– 이 형태를 사용하면 문자열 파일이 약간 복잡해지나, 이미 로직이 문자열 파일의 plurals 요소에 입력된 형태가 된다. 따라서 로직이 2.번과 같은 모습을 띠고 있고 문자열은 명확하게 1.번의 모습으로 보여줄 수 있다.

<string name="item_count_one">I have %d item.</string>
<string name="item_count_other">I have %d items.</string>

<plurals name="item_counts">
	<item quantity="zero">@string/item_count_other</item>
	<item quantity="one">@string/item_count_one</item>
	<item quantity="two">@string/item_count_other</item>
	<item quantity="other">@string/item_count_other</item>
</plurals>

– 분기하는 로직이 필요 없어 간결하다. 아래의 소스 코드를 보면, getQuantityString 메서드를 사용해서 plurals 요소의 데이터,  plurals의 개수(quantity) 그리고 메시지에 보여주는 개수를 사용해서 완전한 메시지를 보여준다. getQuantityString() 메서드는 여기에서 확인하길 바란다.

tv01.setText(getResources().getQuantityString(R.plurals.item_counts, count, count));

이상 안드로이드에서 문자열에 개수를 표현하는 3가지 방법을 살펴봤다. 개인적으로는 plurals 요소를 사용하는 방법을 강력히 추천한다. 이 요소에 대한 자세한 내용은 https://developer.android.com/guide/topics/resources/string-resource.html#Plurals 에서 살펴볼 수 있다. 아래는 위 링크에서 plurals 요소가 사용할 수 있는 quantity 속성에서 사용할 수 있는 값들이다. 아래 값들에 대한 속성값이 어떤 의미를 가지는지 한 번만 읽어보고 사용하면 아주 쉽게 개수를 문자열에 보여줄 수 있겠다.

Value Description
zero When the language requires special treatment of the number 0 (as in Arabic).
one When the language requires special treatment of numbers like one (as with the number 1 in English and most other languages; in Russian, any number ending in 1 but not ending in 11 is in this class).
two When the language requires special treatment of numbers like two (as with 2 in Welsh, or 102 in Slovenian).
few When the language requires special treatment of “small” numbers (as with 2, 3, and 4 in Czech; or numbers ending 2, 3, or 4 but not 12, 13, or 14 in Polish).
many When the language requires special treatment of “large” numbers (as with numbers ending 11-99 in Maltese).
other When the language does not require special treatment of the given quantity (as with all numbers in Chinese, or 42 in English).

* 레퍼런스
– https://developer.android.com/guide/topics/resources/localization.html
https://developer.android.com/guide/topics/resources/string-resource.html