액티비티에 붙여져 있는 View 를 구하는 방법 3가지에 대해


소개하고자 한다.






1. View 객체에 미리 담는 방법.


View view = getLayoutInflator.from(this).inflate(R.layout.main_activity,null);

setContentView(view);


윗 방법은 View 객체에 미리 view의 정보를 담은 후,


참조하고 싶을때 view라는 변수를 통해서 참조 하면 된다.



2. findViewById를 이용하여 View 객체에 담는 방법.


setContentView(R.layout.main_activity,null);

View view = findViewById(R.id.rootView);


윗 방법은 main_activity.xml 안에 최상위 부모뷰에


id를 rootView로 선언 한 뒤, 사용하면 된다.


위와 똑같이 view로 넣었지만 혹, ViewGrop으로 사용하고 싶을시,


다운캐스팅을 해준다. 아래의 예제를 보자.



ex) 뷰그룹으로 받고 싶을시,


ViewGroup viewGroup = (ViewGroup)findViewById(R.id.rootView);


ex) 부모뷰가 특정 ViewGroup일시 - FrameLayout


FrameLayout frameLayout = (FrameLayout)findViewById(R.id.rootView);



3. Activity의 context 정보로 구하는 방법


View view = context.getWindow().getDecorView() ;



윗 방법은 Activity의 context로 액티비티에 붙여져 있는 view를 구하는 방법이다.


이것 역시 ViewGroup 또는 특정 ViewGroup으로 받고 싶을때는 다운캐스팅 해준다.


아래의 예제를 보자.



ex) 뷰그룹으로 받고 싶을시,


ViewGroup viewGroup = ((ViewGroup)context.getWindow().getDecorView()) ;


ex) 부모뷰가 특정 ViewGroup일시 - FrameLayout


FrameLayout frameLayout = ((FrameLayout)context.getWindow().getDecorView()) ;



포스팅이 도움 되셨다면, 커피 한잔 후원해주세요!
더 좋은 포스팅 작성에 큰 힘이 됩니다.

Buy me a coffeeBuy me a coffee

안드로이드는 기존에 자바에서 제공하는 Thread 뿐만아니라


AsyncTask라는 스레드 객체를 제공한다.


안드로이드가 AsyncTask 라는 객체를 왜 만들어 제공할까?


아래의 글은 Android Developer 에서 발췌한 일부 글이다.


AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.

AsyncTask is designed to be a helper class around Thread and Handler and does not constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the java.util.concurrent package such as ExecutorThreadPoolExecutor and FutureTask.

An asynchronous task is defined by a computation that runs on a background thread and whose result is published on the UI thread.



요약하자면, AsyncTask는 백그라운드 스레드 와 UI 스레드를 같이 쓰기 쉽게 설계했으며, 


일반 스레드와 달리, 간단한 작업에 적합하게 만들었다고 설명되어 있다.


백그라운드 스레드와 UI 스레드를 같이 쓰기 쉽다는 말이 무슨 뜻일까?


아래의 설명을 보자.



안드로이드에서의 일처리는 메인스레드(UI 스레드)가 담당한다. 특히 UI와 관련된( ex) TextView,ImageView )


일처리는 메인스레드만 담당 하게끔 설계를 했다. 그래서 메인스레드를 UI스레드라고도 불린다.



따라서 복잡한 계산은 백그라운드 스레드( 메인 스레드가 아닌 다른 스레드의 총칭)에 맡긴후 


계산된 결과값을 UI스레드에게 일을 시켜야 하는 것이다.


그래서 AysncTask 라는 객체를 만들었고 위에서 설명한 일들을 쉽게 구현 할 수 있도록 메서드를 제공한다.


아래의 AysncTask 예시를 보자.

import android.os.AsyncTask;

public class MommooAsyncTask extends AsyncTask<String,Void,String>{

public String result;

@Override
protected void onPreExecute() {
super.onPreExecute();
}

@Override
protected String doInBackground(String... params) {
return result;
}

@Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
}

@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
}
}


AsyncTask객체는 abstract로 작성되었다. 따라서, 익명클래스로 사용허던가 위와 같이 상속을 통해서 사용해야 한다.


public class MommooAsyncTask extends AsyncTask<String,Void,String>{


제네릭 인자3개를 정해야한다. 


첫번째 인자는 doInBackground 메서드에 선언하는 가변인수 매개변수의 타입을 정한다.


두번째 인자는 onProgressUpdate 메서드에 선언하는 가변인수 매개변수의 타입을 정한다.


세번째 인자는 onPostExecute 메서드에 선언하는 매개변수의 타입을 정한다.


@Override
protected void onPreExecute() {
super.onPreExecute();
}


첫번째 메서드다. 해당 메서드는 이름에서 볼 수 있드시,  background스레드를 실행하기전 준비 단계이다.


변수의 초기화나, 네트워크 통신전 셋팅해야할 것들을 위의 메서드 공간에 작성한다. 



@Override
protected String doInBackground(String... params) {
return result;
}


두번째 메서드다. 해당 메서드가 background 스레드로 일처리를 해주는 곳이다.


보통 네트워크, 병행 일처리등을 위 메서드 공간에 작성한다.


중요한건 마찬가지로 스레드 이므로 UI스레드가 어떤 일을 하고 있는지 상관없이


별개의 일을 진행한다는 점이다. 따라서 AysncTask는 비동기적으로 작동한다.



@Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
}


세번째 메서드는 doInBackground 메서드에서 중간중간에 UI스레드 에게 일처리를 맡겨야 하는 상황일때


쓴다. 매개변수로 Void를 받으므로, doInBackground안에 실제인자가 없이,


 publishProgress( ) 메서드를 호출하면 BackgroundThread 중간에 mainThread에게 일을 시킬 수 있다.



@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
}


마지막 메서드다. background Thread가 일을 끝마치고 리턴값으로 result를 넘겨준다.


그 값을 지금 보고 있는 해당 메서드가 매개변수로 받은후 받은 데이터를 토데로


UI스레드에 일처리를 시킬때 쓰는 메서드이다.


보통 UI변경 ( ex) textview.setText("~~") )할때 많이 사용된다. 왜냐면


위에서도 말했다시피 UI변경은 메인스레드가 아닌 다른 스레드에서의 변경은 막았기 때문이다.


위에서 만든 AysncTask를 사용 하는방법은 아래와 같이 하면된다.


MommooAsyncTask asyncTask = new MommooAsyncTask();
asyncTask.execute();


execute의 아규먼트는 doInBackground에서 받는 String... 가변인수이다. 필요시 넣으면 되겠다.



해당 URL은 저번에 작성한 포스팅인데 AsyncTask를 사용하였다. 예시로 보면 될 것이다.


http://mommoo.tistory.com/5



여기까지가 준비한 AysncTask의 내용이다.


직접 Thread와 Handler 를 구현해보고 작성해봤다면,


안드로이드가 제공해주는 AysncTask가 얼마나 편리한지 알 수 있을 것이다.



포스팅이 도움 되셨다면, 커피 한잔 후원해주세요!
더 좋은 포스팅 작성에 큰 힘이 됩니다.

Buy me a coffeeBuy me a coffee

오늘 포스팅할 내용은 Toolbar 위젯이다.


Toolbar 위젯이 뭔지 아래의 설명을 보자.


해당 내용은 안드로이드 디벨로퍼 API문서이다.




A standard toolbar for use within application content.

A Toolbar is a generalization of action bars for use within application layouts. While an action bar is traditionally part of an Activity's opaque window decor controlled by the framework, a Toolbar may be placed at any arbitrary level of nesting within a view hierarchy. An application may choose to designate a Toolbar as the action bar for an Activity using the setActionBar() method.



요약하자면, 어플리케이션의 제어를 도와주는 액션바는 스크린의 최상위 뷰인 윈도우 decor에 붙여져 있어 다루기 어렵다.

하지만 toolbar는 어느 뷰에서나 종속관계 상관없이 다채롭게 사용가능하며 actionbar 사용하듯 toolbar를 사용할 수 있다는 내용이다.

머터리얼 디자인이 등장함에 따라 UI의 큰 변화가 있었다. ActionBar를 사용시 구현하기 어려운 경우가 많아 

toolbar란 위젯을 만든거 같다. 자세한 내용을 보고 싶다면 아래의 링크를 참조하길 바란다.

http://developer.android.com/intl/ko/reference/android/widget/Toolbar.html 




해당 위젯은 롤리팝 부터 나온 위젯이기 때문에(API level 21) 만약 하위 os에서 쓰고 싶다면 서포트 라이브러리가 필요하다.


build.gradle에서 dependencies에 아래와 같이 추가해준다.

dependencies {

compile 'com.android.support:appcompat-v7:23.2.1'
compile 'com.android.support:design:23.2.1'

}


actionBar 대신에 toolbar를 쓸 것 이므로, 액션바가 없는 테마로 변경해야한다.


values 폴더에 styles.xml 안에다가 해당 테마를 만들어준다.


<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>



Activity에 붙일 레이아웃을 아래와 같이 작성한다. (main_activity.xml)


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:theme="@style/AppTheme.NoActionBar">
<android.support.v7.widget.Toolbar
xmlns:app="http://schemas.android.com/apk/res-auto"
app:titleTextColor="#fff"
android:id="@+id/toolbar"
android:background="@color/colorPrimary"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize">
</android.support.v7.widget.Toolbar>
</LinearLayout>


레이아웃을 작성후 액티비티의 onCreate 메서드에 아래와 같이 작성한다.


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
toolbar.setTitleTextColor(Color.WHITE);
}

아래의 이미지는 결과물이다.








포스팅이 도움 되셨다면, 커피 한잔 후원해주세요!
더 좋은 포스팅 작성에 큰 힘이 됩니다.

Buy me a coffeeBuy me a coffee

오늘은 getColor 메서드에 대해 포스팅 합니다.


getColor 메서드는 안드로이드 value폴더에 colors.xml에 저장된


컬러값들을 가져오는 메서드입니다.


colors.xml에 저장할 시, 마치 전역변수처럼 값들을 호출 할 수 있기때문에


자주 쓰입니다. 쓰는법은 아래와 같습니다.


int color = context.getResource().getColor(R.color.자신의ID값); 


만약 액티비티 메서드 안이라면, 이미 context정보가 있기때문에


int color = (this).getResource().getColor(R.color.자신의ID값);


이렇게 쓰입니다. 하지만 API23에서 getColor 메서드는 depercated 되었습니다.


따라서 getColor 메서드 대신 아래의 메서드를 사용하여 deprecated에 대응해야 합니다.


int color = ContextCompat(context,R.color.자신의ID값);


마찬가지로 액티비티 메서드 안 이라면, context 대신에 this를 쓰면 되겠지요.





포스팅이 도움 되셨다면, 커피 한잔 후원해주세요!
더 좋은 포스팅 작성에 큰 힘이 됩니다.

Buy me a coffeeBuy me a coffee

해당 메서드는 액티비티를 상속 받았을시 사용가능하다.


비슷한 메서드로는 startActivity가 있다. 해당 메서드는 매개변수로 intent를 받는다.


기존 액티비에에서 새로운 액티비티를 생성할시 사용한다. 이때 기존 액티비티의 정보를


새로운 액티비티로 전달 할 시 intent객체에 putExtra 메서드로 정보들을 전달한다. 


오늘 알아볼 메서드 startActivityForResult는 메서드 이름에서도 알 수 있드시, 액티비티를 생성한 후


생성한 액티비티가 종료됬을시, 기존 액티비티에게 신호를 주기 위한 메서드 이다.


처음 액티비티의 이름을 MainActivity, 생성할 액티비티 이름을 SubActivity라 칭하겠다.


startActivityForResult 메서드의 매개변수는 intent와,


하나의 메인 액티비티에서 여러개의 액티비티를 실행 시켰을 경우 구분지을 int형 변수 


이렇게 2개를 받는다. 신호를 받은 후 처리 하는곳은 onActivityResult 메서드 이다. 


사용방법은 이렇다. 


MainActivity.java


....


...

Intent intent = new Intent(MainActivity.this,SubActivity.class);

intent.putExtra("code", 1);    //이런식으로 액티비티에 정보를 보낸다.

startActivityForResult(intent , 0);


@Override

protected void onActivityResult(int requestCode, int resultCode, Intent data){

super.onActivityResult(requestCode, resultCode, data);


// ....... 여기서 하고싶은 행위를 작성하면 된다.


}



SubActivity.java


....


...


@Override

protected void onDestroy(){      //액티비티가 종료될 때의 메서드

super.onDestroy();

setResult( 0 ); // 여기에 넣는 int형 정수는 MainActivity의 onActivityResult안에서 requestCode로 들어간다.

}




포스팅이 도움 되셨다면, 커피 한잔 후원해주세요!
더 좋은 포스팅 작성에 큰 힘이 됩니다.

Buy me a coffeeBuy me a coffee




  안드로이드 UI의 상태나 가벼운 정보등은 SharedPreference(이하 S.P로 칭한다.)를 사용한다. 


db와 달리 번거롭지않고 사용하기 간편하기 때문이다. 


하지만 단점이 있다. xml형태로 저장하므로, 데이터 누적이 많아 지면 DB에 비해 성능이 떨어진다.


성능이 떨어지는 이유는 당연 인덱싱 문제가 아닐까 싶다. db같은 경우는 query로 손쉽게 원하는 데이터를 뽑아와


작업하지만, S.P 방식은 데이터 전부를 뽑아와 key값을 하나 하나 비교하기 떄문이라고 생각한다.


즉, 사용목적에 따라 두가지 기능(S.P , DB)을 적절히 사용 할 필요가 있겠다.


이번 포스팅은 db를 사용 할 것이다. 안드로이드는 SQLite이라는 내부 db를 제공해준다.


기본적인 셋팅은 아래와 같다.


public class MommooDB extends SQLiteOpenHelper {
public MommooDB(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
@Override
public void onCreate(SQLiteDatabase db) {

}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
onCreate(db);
}

} 


SQLiteOpenHelper클래스를 상속받은 자신의 클래스를 만든다.


필수적으로 부모생성자와, onCreate, onUpgrade 이렇게 3가지를 적어줘야 한다.


필수 부모 생성자로 Context, 디비명, 커서, 버전을 아규먼트로 넘겨줘야한다.


여기서 Context는 안드로이드의 Context정보를 의마하고, 디비명은 자신의 데이터베이스 파일명을 의미한다.


커서는 db데이터를 다룰 때 쓰는데, 


자신의 만든 커스텀 커서가 있을경우 선언해주고 기본 커서를 사용할 경우는 null로 해준다.


마지막 매개변수는 버전이다. 같은 디비명이라도 버전이 틀리면 다른 디비로 간주 할 수 있다.


onCreate메서드는 혹, 자신이 호출하는 db명이 없는 경우 호출된다. 즉 새로운 디비를 만들때 


CREATE DB명 느낌보다는, db이름을  생성자에의 아규먼트로 넣어 실행했을 경우에


db명이 없을경우 onCreate메서드가 호출되고, db명이 있는경우는 호출하지 않는다.


따라서 db안의 table을 onCreate안에다가 관리하면 편하다.


onUpgrade메서드는 db명은 있지만 버전이 다른 경우에 호출돤다. 


즉 자신이 만든 db에 새로운 table을 추가한다거나


삭제같은 경우가 있을때, 자신의 디비의 버전을 올려주고 메서드 안에다가 새로운 작업을 하면 되겠다.


필자 같은 경우에 필요한 DB 정보는 id와 pass 데이터다. id와 pass 칼럼을 가지는 테이블을 작성해보겠다.


다음 포스팅때 마저 하겠다.




포스팅이 도움 되셨다면, 커피 한잔 후원해주세요!
더 좋은 포스팅 작성에 큰 힘이 됩니다.

Buy me a coffeeBuy me a coffee




오늘은 HttpURLConnection 에 대해 포스팅 해보겠다. 


포스팅 하게 된 이유는 저번에 잘 사용하던 httpClient(아파치 라이브러리)가 deprecated 되었기 때문이다.


갑자기 사라져서 당황했었다... 아래 내용은 Google Developer 에서 발췌한 android 6.0 변화에서 httpClient에 대한 이야기다. 


Apache HTTP Client Removal


Android 6.0 release removes support for the Apache HTTP client. If your app is using this client and targets Android 2.3 (API level 9) or higher, use the HttpURLConnection class instead. This API is more efficient because it reduces network use through transparent compression and response caching, and minimizes power consumption. To continue using the Apache HTTP APIs, you must first declare the following compile-time dependency in your build.gradle file:

android {
    useLibrary
'org.apache.http.legacy'
}


요약하자면, 위와 같이 gradle에 해당 라이브러리를 추가하면 httpClient 쓸수는 있다. 또 말하기를, httpClient의 기능은 많지만 무겁다, 


그리고 안드로이드에서는 조금 오바(?)스러운 httpClient보다는,


안드로이드에 적합한 API인 HttpURLConnection을 사용하라는 이야기다.(해당 이슈는 예전부터 나오던 이슈긴 하다.)


뭐.. httpClient가 편리하지만 저렇게 권장하지 않는 API를 쓰기에는 유지보수와 기능에 문제가 있다고 판단, 바로 HttpURLConnection으로 옮겼었다.


서론은 여기까지 하고, 시작해 보자. HttpURLConnection도 굉장히 쉽다.


먼저 인터넷 환경이 필요한 작업이므로 AndroidManifest.xml 파일에 


<uses-permission android:name="android.permission.INTERNET"/> 을 작성해 인터넷 환경을 만들어준다.


이제 MainActivity에 네이버의 웹 정보를 받아오는 코드를 작성해보자.


new AsyncTask<Void,Void,Void>(){
@Override
protected void onPreExecute() {
super.onPreExecute();
strUrl = "http://www.naver.com"; //탐색하고 싶은 URL이다.
}

@Override
protected Void doInBackground(Void... voids) {
try{
Url = new URL(strUrl); // URL화 한다.
HttpURLConnection conn = (HttpURLConnection) Url.openConnection(); // URL을 연결한 객체 생성.
conn.setRequestMethod("GET"); // get방식 통신
conn.setDoOutput(true); // 쓰기모드 지정
conn.setDoInput(true); // 읽기모드 지정
conn.setUseCaches(false); // 캐싱데이터를 받을지 안받을지
conn.setDefaultUseCaches(false); // 캐싱데이터 디폴트 값 설정

strCookie = conn.getHeaderField("Set-Cookie"); //쿠키데이터 보관

InputStream is = conn.getInputStream(); //input스트림 개방

StringBuilder builder = new StringBuilder(); //문자열을 담기 위한 객체
BufferedReader reader = new BufferedReader(new InputStreamReader(is,"UTF-8")); //문자열 셋 세팅
String line;

while ((line = reader.readLine()) != null) {
builder.append(line+ "\n");
}

result = builder.toString();

}catch(MalformedURLException | ProtocolException exception) {
exception.printStackTrace();
}catch(IOException io){
io.printStackTrace();
}
return null;
}

@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
System.out.println(result);
}
}.execute();

AsynTask는 통신을 할 때 필요한 클래스이다. 이해하면 사용하기 쉽지만, 모르는 사람은 일단 따라 써보기로 하자.


protected void onPreExecute() 메서드 안에는 통신을 하기전 필요한 정보를 셋팅하는 구간이다.


우리가 탐색할 네이버 주소를 적었다.


protected Void doInBackground(Void... voids) 이 메서드가 실제로 통신 할때 작동하는 부분이다.


왠만한 설명은 주석처리 해놨다. 좀더, 정리를 해주자면..


Url = new URL(strUrl); // URL화 한다.
HttpURLConnection conn = (HttpURLConnection) Url.openConnection(); // URL을 연결한 객체 생성.


이 부분이 해당 URL(예시는 네이버)의 정보를 담고 있는 객체를 생성하는 부분이다.


conn.setRequestMethod("GET"); // get방식 통신
conn.setDoOutput(true); // 쓰기모드 지정
conn.setDoInput(true); // 읽기모드 지정
conn.setUseCaches(false); // 캐싱데이터를 받을지 안받을지
conn.setDefaultUseCaches(false); // 캐싱데이터 디폴트 값 설정


이 부분이 해당 통신을 위한 셋팅 부분이다. HttpURLConnection은 스트림 기반이라 셋팅 할 것이 많다.


strCookie = conn.getHeaderField("Set-Cookie"); //쿠키데이터 보관

이 부분은 세션지정을 위한 쿠키값 저장을 하는 부분이다.


httpClient는 쿠키값을 알아서 저장해줬지만 HttpURLConnection은 따로 저장 관리를 해줘야한다.


InputStream is = conn.getInputStream(); //input스트림 개방

StringBuilder builder = new StringBuilder(); //문자열을 담기 위한 객체
BufferedReader reader = new BufferedReader(new InputStreamReader(is,"UTF-8")); //문자열 셋 세팅
String line;

while ((line = reader.readLine()) != null) {
    builder.append(line+ "\n");
}

 이 부분은 웹 정보를 파싱하는 부분이다. 중요한 점은 웹 정보 마다 다른 문자열 셋일 수 있으므로 맞는 문자열 셋팅을 해줘야 한다.


protected void onPostExecute(Void aVoid)

이 메서드 부분이 해당 통신이 끝난 후 통신작업이 아닌 마무리 작업(UI작업, 사용자 정의 작업 등)을 하는 부분이다.


예시에서는 콘솔에 출력하는 메서드를 썻다.


실행해보면 네이버의 정보를 콘솔에 출력하는 것을 볼 수 있다.


oRTK : { "t": "2015-11-17T12:25Z", "s": "2015.11.17. (화) 12:25 PM", "d": [ { "k": "오마이비너스", "c": "up", "a": "상승", "n": "72" }, 

{ "k": "신민아", "c": "up", "a": "상승", "n": "99" }, { "k": "진세연", "c": "up", "a": "상승", "n": "225" }, 

{ "k": "이정재", "c": "up", "a": "상승", "n": "216" }, { "k": "육룡이 나르샤 결방", "c": "up", "a": "상승", "n": "174" }, 

{ "k": "다이나믹듀오", "c": "up", "a": "상승", "n": "216" }, { "k": "풍선껌", "c": "up", "a": "상승", "n": "63" }, 

{ "k": "첼시 리", "c": "up", "a": "상승", "n": "348" }, { "k": "먹는존재", "c": "up", "a": "상승", "n": "150" }, 

{ "k": "명지전문대", "c": "up", "a": "상승", "n": "93" } ] },


콘솔에 찍힌 정보 중간에 실시간 검색어 정보를 발견했다. (실시간 정보는 네이버에서 제공하는 API가 있다.)


시간대가 틀려 찍히는 정보가 틀리겠지만... 무튼 성공 한걸 알 수 있다.


해당 예시는 get 방식으로 웹 정보를 받아오는 방식이다.


추후에 post방식으로 웹 정보를 이용하는 방법도 포스팅 하겠다.



해당 프로젝트는 https://github.com/Mommoo/ExampleCode/tree/master/HttpURLConnection 에서 받을 수 있다.






포스팅이 도움 되셨다면, 커피 한잔 후원해주세요!
더 좋은 포스팅 작성에 큰 힘이 됩니다.

Buy me a coffeeBuy me a coffee




http://mommoo.tistory.com/1 ( RecyclerView , CardView 사용하기.(1) )




2편이다. 처음 읽는 독자는 위의 경로의 포스팅을 먼저 읽기를 권한다.


RecyclerView에 필요한 Adpater를 선언할 것이다.

참조한 구글 문서는 단순히 TextView만을 이용한 예제이다.


본 게시물은 추가적으로 image를 넣어 image와 text로 구성된 예제임을 밝힌다.



MyAdapter.java


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
    private ArrayList<MyData> mDataset;
 
    // Provide a reference to the views for each data item
    // Complex data items may need more than one view per item, and
    // you provide access to all the views for a data item in a view holder
    public static class ViewHolder extends RecyclerView.ViewHolder {
        // each data item is just a string in this case 
        public ImageView mImageView;
        public TextView mTextView;
 
        public ViewHolder(View view) {
            super(view);
            mImageView = (ImageView)view.findViewById(R.id.image);
            mTextView = (TextView)view.findViewById(R.id.textview);
        }
    }
 
    // Provide a suitable constructor (depends on the kind of dataset)
    public MyAdapter(ArrayList<MyData> myDataset) {
        mDataset = myDataset;
    }
 
    // Create new views (invoked by the layout manager)
    @Override
    public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
                                                   int viewType) {
        // create a new view
        View v = LayoutInflater.from(parent.getContext())
                               .inflate(R.layout.my_view, parent, false);
        // set the view's size, margins, paddings and layout parameters
        ...
        ViewHolder vh = new ViewHolder(v);
        return vh;
    }
 
    // Replace the contents of a view (invoked by the layout manager)
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        // - get element from your dataset at this position
        // - replace the contents of the view with that element
        holder.mTextView.setText(mDataset.get(position).text);
        holder.mImageView.setImageResource(mDataset.get(position).img);
    }
 
    // Return the size of your dataset (invoked by the layout manager)
    @Override
    public int getItemCount() {
        return mDataset.size();
    }
}
 
class MyData{
    public String text;
    public int img;
    public MyData(String text, int img){
        this.text = text;
        this.img = img;
    }
}
cs



MyAdpater.class를 선언하고, RecyclerView.Adapter를 상속 받았다.


MyAdpater안에 직접 구현한 ViewHolder 클래스를 제네릭으로 받는 구조이다.  물론 직접 만드는 ViewHolder도 


ReclerView.ViewHolder를 상속받아야 한다.


(ViewHolder는 ListView를 사용할 당시, RecylerView처럼 View를 효율적으로 쓰기 위한 디자인 패턴중 하나였다. 

리소스를 제법 잡어먹는 findViewById()를 적게 호출하기 위함이다. RecyclerView 에서는 적극적으로 ViewHolder 패턴을 사용하라고 권장한다. )


ViewHolder 클래스는 RecyclerView의 item에 들어갈 view를 받은후 그 view 안에 있는 ImageView와 TextView를 초기화 한다.


그다음은 MyAdapter 생성자가 보이는데, 매개변수로 ArrayList<MyData> 객체를 받는다. MyData 클래스는 data-set을 위한 클래스로


해당 예제에 맨 아래에 생성했다.




중요한 내용은 지금부터이다.


onCreateViewHolder 메서드안의 내용을 잘 바꾸어야 내가 원하는 결과물이 나온다.


첫번째로, View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.my_view, parent, false); 는


앞서 말한 ViewHolder에 넣어줄 view를 찾는 과정이다. my_view라는 xml에는 cardview가 들어갈 예정이다.


이 view를 넣기위한 ViewHolder를 다음과 같이 선언한 후 넣는다.


 ViewHolder vh = new ViewHolder(v);


그다음 onBindViewHolder 메서드를 보자. 이 메서드는 RecyclerView의 item의 셋팅과 사용자가 스크롤링 할때, 호출되는 메서드이다.


우리가 원하는 데이터가 포지션별로 ArrayList<MyData>에 저장되어 있다. 이러한 데이터를 포지션별로 보여주는 것을 보장해준다.


마지막으로 MyData 클래스이다. 우리가 원하는 image와 text 데이터를 보관하기 위한 데이터 저장소 개념으로 보면 된다.



다음은 위에서 소개한 my_view.xml을 코딩 해보자.



my_view.xml


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <android.support.v7.widget.CardView
        android:id="@+id/cardview"
        card:cardCornerRadius="3dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
 
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">
        <ImageView
            android:scaleType="fitXY"
            android:layout_width="match_parent"
            android:layout_height="200dp"
            android:id="@+id/image"/>
        <TextView
            android:id="@+id/textview"
            android:gravity="center|left"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="10dp"/>
        </LinearLayout>
    </android.support.v7.widget.CardView>
</RelativeLayout>
cs



위의 CardView 위젯을 보면 android: 대신 card: 인자가 보이는데, 


이 인자는 부모뷰에 xmlns:card="http://schemas.android.com/apk/res-auto"


등록을 해주면 된다. cardCornerRadius 값은 모서리 둥근 정도를 결정한다.


해당 카드뷰 view 아래는 데이터를 보여줄(image,text) 하위 view들을 지정했다.


cardview 자체는 FrameLayout를 상속 받기 때문에 따로 LinearLayout을 선언하여 수직 정렬 하였다.


다 끝났다. 이제 MyActivity로 돌아가 데이터를 넣어본다.



MyActivity.java


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class MyActivity extends Activity {
    private RecyclerView mRecyclerView;
    private RecyclerView.Adapter mAdapter;
    private RecyclerView.LayoutManager mLayoutManager;
    private ArrayList<MyData> myDataset;    
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.my_activity);
        mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
 
        // use this setting to improve performance if you know that changes
        // in content do not change the layout size of the RecyclerView
        mRecyclerView.setHasFixedSize(true);
 
        // use a linear layout manager
        mLayoutManager = new LinearLayoutManager(this);
        mRecyclerView.setLayoutManager(mLayoutManager);
 
        // specify an adapter (see also next example)
        myDataset = new ArrayList<>();
        mAdapter = new MyAdapter(myDataset);
        mRecyclerView.setAdapter(mAdapter);
        
        myDataset.add(new MyData("#InsideOut", R.mipmap.insideout));
        myDataset.add(new MyData("#Mini", R.mipmap.mini));
        myDataset.add(new MyData("#ToyStroy", R.mipmap.toystory));
 
    }
    ...
}
cs


컴파일을 해보면...




멋진 카드효과와 함께 잘 나온다.


해당 프로젝트는 https://github.com/Mommoo/ExampleCode/tree/master/RecyclerView  에서 받을 수 있다.




포스팅이 도움 되셨다면, 커피 한잔 후원해주세요!
더 좋은 포스팅 작성에 큰 힘이 됩니다.

Buy me a coffeeBuy me a coffee

+ Recent posts