티스토리 뷰

안드로이드의 RecyclerView를 사용하여 간단한 리스트를 구현해 보는 학습을 하면서 진행 내역을 간단하게 기록해 보고자 합니다.


(사용한 안드로이드 추가 패키지) *아래는 build.gradle파일의 dependancies에 추가된 내용입니다.

    //추가로 사용한 패키지 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


(사용한 온라인 리소스)

  - http://beta.json-generator.com : 샘플로 사용할 json 생성 및 http로 access
  - http://lorempixel.com/ : 임의의 사진을 온라인으로 제공
  - https://jsonutils.com/ : json에 대응하는 모델 생성


[작업내역]

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 추가

<?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생성

<?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로 처리

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라는 인터페이스에 정의

public interface IApiService {
    @GET("api/json/get/cfJljnapcO?indent=2")
    Call<ArrayList<UserModel>> getUserList();
}

 b. retrofit 객체 관리용 클래스 생성

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 생성

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 생성

public class UserAdapter extends Adapter {
    private ArrayList mList;

    public UserAdapter(ArrayList 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();
    }
}


8. Activity 코드 작성

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 수정

 *인터넷 연결이 필요하므로 해당 퍼미션 요청 구문 추가

<?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>


전체 소스는 첨부하며, 이후에는 당겨서 게시물을 추가하는 기능을 작성하고 공유해 보겠습니다.


RecyclerViewTest.zip



감사합니다.

댓글
댓글쓰기 폼
공지사항
Total
3,970
Today
0
Yesterday
1
링크
«   2021/06   »
    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      
글 보관함