Intent적용하기(feat.메뉴달기)

9 분 소요

intent 란

intent는 다른 activity를 시작하기 위한 개체로서, 어떻게 작동시킬지 사용자의 의도에 따라 명시적 intent와 암시적 intent로 나뉜다.

explicit intent

먼저 명시적 intent에 대해 알아보자. 일반적으로 하나의 앱 안에서 구성 요소를 시작할 때 쓴다. 내가 만들어 놓은 액티비티 클래스의 이름을 정확히 알고 있기 때문이다. 이 경우 사용하는 생성자는 Intent([인텐트가 수행될 액티비티],[열릴 액티비티.class])이다.

이를테면 main activity oncreat()안에 아래와 같이 이름없는 onClick listener와 함께 새로운 activity를 시작하는 기능을 넣을 수 있다.

동시에 Intent.putExtra(name,value) 를 이용해 간단한 데이터를 열리는 activity에 넘겨줄 수 있다. name에 쓰일 Constant값은 Intent클래스 내 모두 정의되어 있다.

	...
	mDoSomethingCoolButton = (Button) findViewById(R.id.b_do_something_cool);

        mDoSomethingCoolButton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {

                Context context = MainActivity.this;

                Class destinationActivity = ChildActivity.class;

                Intent startChildActivityIntent = new Intent(context, destinationActivity);

                startChildActivityIntent.putExtra(Intent.EXTRA_TEXT,"text to be delivered");

                startActivity(startChildActivityIntent);
            }
        });
    ...

이러면 이제 받아주는 액티비티에서는 아래와 같이 데이터를 받을 수 있다.

		...
        mDisplayText = (TextView) findViewById(R.id.tv_display);

        //일단 .getItent를 이용해 전달된 인테트 객체를 잡고 
        Intent intent = getIntent();

        // 인텐트 객체가 putExtra값을 가진 지 확인을 먼저 한 후 
        if (intent.hasExtra(Intent.EXTRA_TEXT)) {
        	//해당 값을 추출해 이용한다. 
            String extractedString = intent.getStringExtra(Intent.EXTRA_TEXT);

            mDisplayText.setText(extractedString);
        }
        ...
  • back button

implicit intent

반면 암시적 인텐트는 어떤 액티비티가 열릴지 지정하지 않고, 어떤 ‘행동’을 할 지를 지정해준 후 그에 맞는 앱이 시스템 내에 어디 있는지 찾아서 선택지를 열어준다.

웬만한 인텐트는 공홈의 common intent페이지에서 찾을 수 있다. https://developer.android.com/guide/components/intents-common

웹브라우징을 할 수 있는 인텐트를 만드는 예시를 보자.

    //레이아웃
    ...
    onclick="onClickOpenWebpageButton"
    ...
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    //layout xml에 연결된 메소드
    public void onClickOpenWebpageButton(View v) {
        String urlNaver = "https://www.naver.com";
        openWebPage(urlNaver);
    }

    public void openWebPage (String url) {

        //string url값을 Uri 객체로 파싱한 후 
        Uri uri = Uri.parse(url);

        // "ACTION_VIEW"행위를 하는 
        // uri객체를 인수로 받는 intent 인스턴스를 만들어
        Intent intent = new Intent(Intent.ACTION_VIEW, uri);

        // 시스템 내 해당 행위를 resolve할 수 있는 앱이 있는지 검사 한 후에
        // 인텐트를 startActivity한다. 
        if(intent.resolveActivity(getPackageManager()) != null) {
            startActivity(intent);
        }
    }

이러면 해당 레이아웃에 있는 객체를 클릭했을 때, 같이 넘겨진 Uri객체 종류가 url임을 파악한 후 https://www.naver.com 로 연결될 수 있는 앱 리스트들 - 브라우져들 - 이 뜨게 된다. 즉 같은 행위(ACTION_VIEW)라도 어떤 URI가 넘어오냐에 따라서 열리는 앱이 달라지게 된다. 그럼 URI는 무엇인가?

Uri 이해하기

URI란 Unifrom Resource Identifier의 약자로서 아래의 형식으로 표현되는 모든 데이터 형태이다.

scheme:[//[user:password@]host[:port]][/]path[?query][#fragment]

시작은 scheme인데, 이를 통해 어떤 타입의 리소스인지 알 수 있다. 웹의 가장 많이 쓰이는 스킴은 http나 https, mailto, FTP, file, geo..등등이 있다. 어떤 스킴이냐에 따라 두개의 슬래쉬(//)와 authority part([user:password@]host[:port]) 가 나올 수 있다. host는 도메인이나 IP주소가 될 수 있고 port넘버가 추가로 붙을 수도 있다. http스킴에서는 명시되어있지 않으면 브라우저는 port는 80으로 가정한다.

예를 들면 https://www.google.com/search?q=pizza 라는 url에서 https는 scheme, www.google.com 은 authority part(여기서는 host만 존재), / 이후는 path 인 것이다. query?로 시작되어야 하는 것 이외의 제약조건은 없다.

authority part를 지나면 반드시 / 가 나온 후 path로 연결되야 한다. URI의 마지막 요소는 fragment이다. #로 시작하는 fragment는 부차적인 정보를 가지는데 예를 들면 아래 vimeo링크에서 #t=10s부분을 통해 비디오의 10초부분에서 시작하라는 것을 나타낼 수 있다. https://vimeo.com/156006196#t=10s

  • menu

지도 intent

이제위에서 배운 URI를 이해했으니, 이를 통해 간단히 주소를 지정하여 지도앱을 여는 인텐트를 작성해 보자. 상황은 위쪽 인터넷 intent와 같은 레이아웃이라고 하자.

공식홈페이지 예제 클릭

    ...
    // layout의 onclick 속성에 지정될 method
    public void onClickOpenAddressButton(View v) {
        
        // get scheme을 갖는 uri 만들기 
        String addressString = "1600 Amphitheatre Parkway, CA";

        Uri.Builder builder = new Uri.Builder();
        // scheme, path, parameter가 뭔지 알고 있을 것이다. 
        builder.scheme("geo")
                .path("0,0")
                .appendQueryParameter("q", addressString);
        Uri addressUri = builder.build();

        showMap(addressUri);
    }

        private void showMap(Uri geoLocation) {
        
        //웹브라우징때와 마찬가지로 ACTION_VIEW를 사용
        //어떤 URI객체를 보고 싶은 것이기 때문
        Intent intent = new Intent(Intent.ACTION_VIEW);

        //이후 URI를 지정해 주고
        intent.setData(geoLocation);
        //앱이 있는지 확인 후 실행 
        if (intent.resolveActivity(getPackageManager()) != null) {
            startActivity(intent);
        }
    }

데이터 Sharing 하기 - Sharecompat 클래스

요새는 거의 모든 앱에 데이터 공유 버튼이 있다. 그런데 데이터를 공유하려면 생각보다 먼저 고려해야 할 점들이 많다. 어떤 데이터 타입인가? 파일 개수는 얼마나 되는가..? 이런 골치아픈 것들을 고려하며 intent를 만들기 어렵기에, 안드로이드는 sharecompat클래스의 intentbuilder 메쏘드를 통해 쉽게 만들수 있도록 해 놨다.

그럼 먼저 데이터 타입에 대해 알아보자.

Media type

MIME타입으로도 불리는데, 이는 Multipurpose Internet Mail Extention의 약자다. RFC 2046에 최초로 정의된 때, media type이 있기에 서로 다른 타입의 attachment를 가진 multi-part로 구성된 email을 쓸 수 있게 된 것이다. 한 이메일에 이미지, 비디오, 그 외 다른 타입의 attachment를 가질 수 있게 되었고 email client가 어떻게 각각의 파일을 interpret하는 지 알 수 있었다.

Media Type String은 아래와 같이 이뤄진다.

top-level type name / subtype name [;parameters]

대부분의 웹페이지의 Media type string은 text/html;charset=UTF-8으로 나타내진다. 또다른 예시로는 text/plain, text/rtf, image/png, video/mp4등등 이 있다.

아무튼 어떤 데이터든지간에 앱 간에 공유를 하고 싶을 때는 이 테이터(파일)의 미디어타입을 결정해야한다. 그래야 안드로이드가 어떻게 해당 요청을 수행할 지 결정하기 때문이다.

아래의 간단한 text를 share하는 예제를 보자.

    ...
    public void onClickShareTextButton(View v) {

        String shareString = "This will be shared";
        shareText(shareString);
    }

    public void shareText(String textToBeShared) {
        //먼저 mimeType을 정하고
        String mimeType = "text/plain";
        //chooser window(여러 개의 앱 중 하나를 선택하라는 창) 의 제목을 정한다 
        String title = "title?";
        //ShareCompat.IntentBuilder를 이용해 intent를 만들고, 실행까지 할 수 있다. 
        ShareCompat.IntentBuilder.from(this)
                .setType(mimeType)
                .setChooserTitle(title)
                .setText(textToBeShared)
                .startChooser();
    }
    ...

메뉴 달기

공유 하는 법을 배웠으니 메뉴 아이템을 통해 공유 메쏘드를 실행해보도록 하자.

일단 메뉴(오른쪽 위에 있는 버튼)를 달기 위해서는 res폴더 밑에 menu폴더를 새로 만들어야 한다.

이후 .xml파일을 만들고 아래와 같이 입력하자 액티비티 당 메뉴 xml파일이 하나씩 있어야 한다. 아래는 ‘ExampleActivity’에 추가할 메뉴 파일이다.

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context="com.example.android.ExampleActivity">
    <item
        android:id="@+id/action_share"
        android:icon="@drawable/abc_ic_menu_share_mtrl_alpha"
        android:title="@string/action_share"
        app:showAsAction="always"/>
</menu>

showAsAction은 보통 ifroom이 많이 쓰인다. 아이콘 같은 경우는 material design 홈페이지에서 사이즈별로 다운받을 수 있다. 바로가기클릭

메뉴 xml파일을 만들었으면 이것을 달아보도록 하자.

해당 액티비티에서 아래 두 가지 메쏘드를 override해야 한다.

    
    ...
    //메뉴를 달아줄 메쏘드 
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        //메뉴를 만들기 위한 인플레이터 
        MenuInflater inflater = getMenuInflater();
        //res/menu폴더에 있는 xml파일 불러오기 
        inflater.inflate(R.menu.forecast, menu);
        // true반환을 통해 달아줌 
        return true;
    }

    //메뉴가 선택될 시 어떤 액션이 수행될 지 명시해 줄 메쏘드 
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();

        if (id == R.id.action_share) {
            //어떤 액션을 할지 추가 
            onClickShareTextButton
            return true;

        return super.onOptionsItemSelected(item);
    }
    ...

댓글남기기