티스토리 뷰
안드로이드의 RecyclerView를 사용하여 간단한 리스트를 구현해 보는 학습을 하면서 진행 내역을 간단하게 기록해 보고자 합니다.
(사용한 안드로이드 추가 패키지) *아래는 build.gradle파일의 dependancies에 추가된 내용입니다.
1 2 3 4 5 6 7 8 9 10 11 | //추가로 사용한 패키지 START compile 'com.android.support:support-v4:25.3.1' compile 'com.android.support:recyclerview-v7:25.3.1' //RecyclerView compile 'com.android.support:cardview-v7:25.3.1' //CardView compile 'com.github.bumptech.glide:glide:4.0.0-RC1' //url을 통한 이미지 출력 기능 제공 패키지 compile 'com.squareup.retrofit2:retrofit:2.2.0' //http(s) 통신 기능 제공 패키지 compile 'com.squareup.retrofit2:converter-gson:2.2.0' //json 파일 파싱 기능 제공 패키지 compile 'com.squareup.okhttp3:logging-interceptor:3.8.0' //http 통신 내용 log로 확인할 수 있는 interceptor 제공 패키지 compile 'com.facebook.stetho:stetho:1.5.0' //okhttp가 통신하는 것을 chrome 개발자 도구에서 확인할 수 있도록 해 주는 패키지 compile 'com.facebook.stetho:stetho-okhttp3:1.5.0' //okhttp가 통신하는 것을 chrome 개발자 도구에서 확인할 수 있도록 해 주는 패키지 //추가로 사용한 패키지 END |
(사용한 온라인 리소스)
[작업내역]
1. 네트웍에서 접근할 수 있는 json 파일 준비
*현실에서는 리소스를 서버에서 수신하여 처리해야 하므로 네트웍에 json파일을 올려 놓고 놓습니다.
*기본 script가 변경될 수 있으므로 첨부파일(json_generator_default_script.txt)로 파일첨부하였습니다.
*제가 upload한 URL은 다음과 같습니다. http://www.json-generator.com/api/json/get/cqfvIltLma?indent=2
--> json-generator에서는 해당 파일을 언제까지 유지하는지는 모르겠는데, 이와 유사하게는 gist를 활용해도 될 듯 합니다.
a. json generator(http://json-generator.com)로 이동하여 기본 제공 스크립트에서 몇가지 수정
b. 생성된 json파일을 upload한 후 upload된 URL확인
[그림] json-generator를 통해 샘플 json파일 생성
[그림] json-generator에서 생성한 샘플 json파일을 json-generator에서 제공하는 서버로 upload
2. 안드로이드 프로젝트 생성
empty activity로 설정하여 프로젝트 생성
[그림] 안드로이드 기본 프로젝트 생성
3. Layout 생성
아래의 그림과 같이 간단하게 목록을 표시하려 합니다.
[그림] recyclerview 디자인
a. activity_main.xml에 RecyclerView 추가
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <? xml version = "1.0" encoding = "utf-8" ?> < android.support.constraint.ConstraintLayout 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" android:layout_width = "match_parent" android:layout_height = "match_parent" tools:context = "com.createall.recyclerviewtest.MainActivity" > < android.support.v7.widget.RecyclerView android:id = "@+id/recycler_view" android:layout_width = "match_parent" android:layout_height = "wrap_content" /> </ android.support.constraint.ConstraintLayout > |
b. ViewHolder에서 사용할 각 아이템별 layout생성
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 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | <? xml version = "1.0" encoding = "utf-8" ?> < LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android" xmlns:card_view = "http://schemas.android.com/apk/res-auto" android:layout_width = "match_parent" android:layout_height = "wrap_content" > < RelativeLayout android:layout_width = "match_parent" android:layout_height = "wrap_content" > < ImageView android:id = "@+id/image_view" android:layout_width = "match_parent" android:layout_height = "wrap_content" android:adjustViewBounds = "true" android:src = "@drawable/default_image" /> < LinearLayout android:layout_width = "match_parent" android:layout_height = "wrap_content" android:orientation = "horizontal" > < android.support.v7.widget.CardView xmlns:card_view = "http://schemas.android.com/apk/res-auto" android:layout_width = "0dp" android:layout_height = "wrap_content" android:layout_marginLeft = "3dp" android:layout_marginTop = "3dp" android:layout_weight = "7" card_view:cardBackgroundColor = "#7F000000" card_view:cardCornerRadius = "4dp" > < LinearLayout android:layout_width = "match_parent" android:layout_height = "wrap_content" android:orientation = "vertical" > < LinearLayout android:layout_width = "match_parent" android:layout_height = "wrap_content" android:orientation = "horizontal" > < TextView android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:layout_margin = "2dp" android:textColor = "@android:color/white" android:text = "Name : " /> < TextView android:id = "@+id/user_name" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:layout_margin = "2dp" android:textColor = "@android:color/white" android:text = "Alex Lee" /> </ LinearLayout > < LinearLayout android:layout_width = "match_parent" android:layout_height = "wrap_content" android:orientation = "horizontal" > < TextView android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:layout_margin = "2dp" android:textColor = "@android:color/white" android:text = "Email : " /> < TextView android:id = "@+id/user_email" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:layout_margin = "2dp" android:textColor = "@android:color/white" android:text = "dialup71@gmail.com" /> </ LinearLayout > < LinearLayout android:layout_width = "match_parent" android:layout_height = "wrap_content" android:orientation = "horizontal" > < TextView android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:layout_margin = "2dp" android:textColor = "@android:color/white" android:text = "Phone : " /> < TextView android:id = "@+id/user_phone" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:layout_margin = "2dp" android:textColor = "@android:color/white" android:text = "+82 10 3xxx xxxx" /> </ LinearLayout > </ LinearLayout > </ android.support.v7.widget.CardView > < LinearLayout android:layout_width = "0dp" android:layout_height = "wrap_content" android:layout_weight = "3" /> </ LinearLayout > </ RelativeLayout > </ LinearLayout > |
4. 모델 클래스 생성
json으로 전달된 정보를 담을 모델 클래스를 생성합니다. 빠르게 테스트를 하기 위해서 위에서 생성한 json으로 https://jsonutils.com/에서 모델 클래스를 자동으로 생성합니다.
[그림] jsonutils 사이트에서 생성된 json파일이 upload된 URL을 지정하여 모델 클래스 생성
*자동생성된 클래스는 getter setter를 생성하는 옵션을 지정하지 않았으므로, 모두 public으로 생성되는데, 이를 안드로이드 스튜디오에서 private으로 변경하고 getter setter 생성
*하나의 파일로 처리하기 위해서 Friend 클래스는 inner class로 처리
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 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 | public class UserModel { private String guid; private int index; private String favoriteFruit; private double latitude; private String company; private String email; private String picture; private List<String> tags; private String registered; private String eyeColor; private String phone; private String address; private List<Friend> friends; private boolean isActive; private String about; private String balance; private String name; private String gender; private int age; private String greeting; private double longitude; private String _id; public String getGuid() { return guid; } public void setGuid(String guid) { this .guid = guid; } public int getIndex() { return index; } public void setIndex( int index) { this .index = index; } public String getFavoriteFruit() { return favoriteFruit; } public void setFavoriteFruit(String favoriteFruit) { this .favoriteFruit = favoriteFruit; } public double getLatitude() { return latitude; } public void setLatitude( double latitude) { this .latitude = latitude; } public String getCompany() { return company; } public void setCompany(String company) { this .company = company; } public String getEmail() { return email; } public void setEmail(String email) { this .email = email; } public String getPicture() { return picture; } public void setPicture(String picture) { this .picture = picture; } public List<String> getTags() { return tags; } public void setTags(List<String> tags) { this .tags = tags; } public String getRegistered() { return registered; } public void setRegistered(String registered) { this .registered = registered; } public String getEyeColor() { return eyeColor; } public void setEyeColor(String eyeColor) { this .eyeColor = eyeColor; } public String getPhone() { return phone; } public void setPhone(String phone) { this .phone = phone; } public String getAddress() { return address; } public void setAddress(String address) { this .address = address; } public List<Friend> getFriends() { return friends; } public void setFriends(List<Friend> friends) { this .friends = friends; } public boolean isActive() { return isActive; } public void setActive( boolean active) { isActive = active; } public String getAbout() { return about; } public void setAbout(String about) { this .about = about; } public String getBalance() { return balance; } public void setBalance(String balance) { this .balance = balance; } public String getName() { return name; } public void setName(String name) { this .name = name; } public String getGender() { return gender; } public void setGender(String gender) { this .gender = gender; } public int getAge() { return age; } public void setAge( int age) { this .age = age; } public String getGreeting() { return greeting; } public void setGreeting(String greeting) { this .greeting = greeting; } public double getLongitude() { return longitude; } public void setLongitude( double longitude) { this .longitude = longitude; } public String get_id() { return _id; } public void set_id(String _id) { this ._id = _id; } public class Friend { private int id; private String name; public int getId() { return id; } public void setId( int id) { this .id = id; } public String getName() { return name; } public void setName(String name) { this .name = name; } } } |
5. REST 통신을 위한 인터페이스와 클래스 생성
*REST 통신을 위해서 retrofit2 패키지와 okhttp3 패키지를 사용
a. 서비스별로 생성해야 하는 메소드를 IApiService라는 인터페이스에 정의
1 2 3 4 | public interface IApiService { @GET ( "api/json/get/cfJljnapcO?indent=2" ) Call<ArrayList<UserModel>> getUserList(); } |
b. retrofit 객체 관리용 클래스 생성
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 | public class RestClient { private static IApiService IApiService; private static OkHttpClient client; public RestClient(String baseUrl) { //json 파싱 시, formatting을 하기 위한 Gson객체 설정 Gson gson = new GsonBuilder().create(); //통신 data 로깅을 위한 인터셉터 설정 HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor(); httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); //http client로 사용할 okhttp 빌드 client = new OkHttpClient().newBuilder() .connectTimeout( 5 , TimeUnit.SECONDS) .writeTimeout( 5 , TimeUnit.SECONDS) .readTimeout( 10 , TimeUnit.SECONDS) .addInterceptor(httpLoggingInterceptor) .addInterceptor( new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request newRequest = chain.request().newBuilder() .addHeader( "User-Agent" , "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36" ) .build(); return chain.proceed(newRequest); } }) .addNetworkInterceptor( new StethoInterceptor()) .build(); Retrofit retrofit = new Retrofit.Builder() .baseUrl(baseUrl) .addConverterFactory(GsonConverterFactory.create(gson)) .client(client) .build(); IApiService = retrofit.create(IApiService. class ); } public IApiService getApiService() { return IApiService; } } |
6. ViewHolder 생성
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public class UserViewHolder extends RecyclerView.ViewHolder { public ImageView mImageView; public TextView mUserName; public TextView mUserEmail; public TextView mUserPhone; public UserViewHolder(View itemView) { super (itemView); mImageView = (ImageView)itemView.findViewById(R.id.image_view); mUserName = (TextView) itemView.findViewById(R.id.user_name); mUserEmail = (TextView) itemView.findViewById(R.id.user_email); mUserPhone = (TextView) itemView.findViewById(R.id.user_phone); } } |
7. Adapter 생성
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 | public class UserAdapter extends Adapter<userviewholder> { private ArrayList<usermodel> mList; public UserAdapter(ArrayList<usermodel> list) { mList = list; } @Override public UserViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.user_item, parent, false ); return new UserViewHolder(v); } @Override public void onBindViewHolder(UserViewHolder holder, int position) { Glide.with(holder.itemView.getContext()) .load(mList.get(position).getPicture() + "#" + position + System.currentTimeMillis()) //Glide가 동일한 URL일 때, 캐싱한 것을 보여주기 때문에 각각 URL을 틀리게 하기 위해 position과 현재 시각을 추가함 .into(holder.mImageView); holder.mUserName.setText(mList.get(position).getName()); holder.mUserEmail.setText(mList.get(position).getEmail()); holder.mUserPhone.setText(mList.get(position).getPhone()); } @Override public int getItemCount() { return mList.size(); } } </usermodel></usermodel></userviewholder> |
8. Activity 코드 작성
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 | public class MainActivity extends AppCompatActivity { private String mClassName = getClass().getName().trim(); private RecyclerView mRecyclerView; @Override protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); Stetho.initializeWithDefaults( this ); setContentView(R.layout.activity_main); mRecyclerView = (RecyclerView)findViewById(R.id.recycler_view); mRecyclerView.setLayoutManager( new LinearLayoutManager( this )); IApiService iApiService = new RestClient( "http://www.json-generator.com/" ).getApiService(); Call<ArrayList<UserModel>> call = iApiService.getUserList(); try { call.enqueue( new Callback<ArrayList<UserModel>>() { @Override public void onResponse(Call<ArrayList<UserModel>> call, Response<ArrayList<UserModel>> response) { if (response.isSuccessful()) { try { UserAdapter ua = new UserAdapter(response.body()); mRecyclerView.setAdapter(ua); } catch (Exception e) { Log.d(mClassName, e.toString()); } } } @Override public void onFailure(Call<ArrayList<UserModel>> call, Throwable t) { Log.d(mClassName, t.getMessage()); } }); } catch (Exception e) { Log.d(mClassName, e.getMessage()); } } } |
9. AndroidManifest 수정
*인터넷 연결이 필요하므로 해당 퍼미션 요청 구문 추가
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <? xml version = "1.0" encoding = "utf-8" ?> < manifest xmlns:android = "http://schemas.android.com/apk/res/android" package = "com.createall.recyclerviewtest" > < uses-permission android:name = "android.permission.INTERNET" /> < application android:allowBackup = "true" android:icon = "@mipmap/ic_launcher" android:label = "@string/app_name" android:roundIcon = "@mipmap/ic_launcher_round" android:supportsRtl = "true" android:theme = "@style/AppTheme" > < activity android:name = ".MainActivity" > < intent-filter > < action android:name = "android.intent.action.MAIN" /> < category android:name = "android.intent.category.LAUNCHER" /> </ intent-filter > </ activity > </ application > </ manifest > |
전체 소스는 첨부하며, 이후에는 당겨서 게시물을 추가하는 기능을 작성하고 공유해 보겠습니다.
감사합니다.
'안드로이드' 카테고리의 다른 글
[버그] Mac 안드로이드 스튜디오 Logcat filter bar 사라짐 (1) | 2019.05.28 |
---|