Intent적용하기(feat.메뉴달기)
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);
}
...
댓글남기기