[android] MVVM – 4. ListView

리스트뷰는 아무래도 어댑터가 들어가기 때문에 버튼 같은 것들과는 사용법이 조금 다르다. Activity에서는 어댑터 관련 작업만 해주면 되는데, 누군가 적은 글에보니 뷰를 직접 컨트롤하는 코드는 뷰모델에 넣지 말라고 한다. 나처럼 상상의 단계가 짧은 사람은 일단 실제로 프로젝트를 MVVM으로 진행해가면서 몸으로 느껴보는 게 좋을 것 같다. 그러고보니 어디서 본 코드에는 어댑터 관련 코드를 다른 클래스에서 하는 걸 본 기억이 있다. 내일 다시 찾아봐야겠다.

 

먼저 유저 목록을 갖고 있는 뷰모델을 만든다.

public class UsersViewModel implements BaseViewModel {
  public final ObservableArrayList<UserViewModel> users = new ObservableArrayList<>();

  @Override
  public void onCreate() {}

  @Override
  public void onResume() {}

  @Override
  public void onPause() {}

  @Override
  public void onDestroy() {}

  public void newUser() {
    users.add(new UserViewModel("name " + random(), "email " + random()));
  }

  private int random() {
    return new Random().nextInt();
  }
}

 

 

액티비티용 화면을 추가하고

<layout 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">

  <data>

    <variable
      name="model"
      type="kr.susemi99.testmvvm.list_view.UsersViewModel"/>
  </data>

  <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:onClick="@{()-> model.newUser()}"
      android:text="add"/>

    <ListView
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      app:items="@{model.users}"/>
  </LinearLayout>
</layout>

 

 

 

액티비티를 만든다.

public class ListActivity extends AppCompatActivity {
  private UsersViewModel usersViewModel = new UsersViewModel();

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ActivityListBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_list);
    binding.setModel(usersViewModel);

//    ViewDataBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_list);
//    binding.setVariable(BR.model, usersViewModel);
  }

  @BindingAdapter("app:items")
  public static void setUserList(ListView listView, ObservableArrayList<UserViewModel> users) {
    UserListViewAdapter adapter;

    if(listView.getAdapter() == null) {
      adapter = new UserListViewAdapter();
      listView.setAdapter(adapter);
    }
    else {
      adapter = (UserListViewAdapter) listView.getAdapter();
    }

    adapter.addAll(users);
  }
}

오늘 배운 건데, 주석처리한 저 부분처럼 사용해도 잘된다.

setUserList()는 액티비티에 들어가도 되고 뷰모델에 들어가도 되는데, 만약 같은 뷰모델을 여기서는 리스트뷰, 다른 곳에서는 RecyclerView를 쓴다면 아무래도 액티비티에 넣는 게 나을 것 같다.

 

 

그 다음에는 리스트뷰의 한 줄을 표시할 용도의 뷰모델을 만든다.

public class UserViewModel {
  public final ObservableField<String> name = new ObservableField<>();
  public final ObservableField<String> email = new ObservableField<>();

  public UserViewModel(String name, String email) {
    this.name.set(name);
    this.email.set(email);
  }
}

 

 

리스트뷰의 한 줄을 표시할 레이아웃을 만든다.

<layout xmlns:android="http://schemas.android.com/apk/res/android">

  <data>

    <variable
      name="user"
      type="kr.susemi99.testmvvm.list_view.UserViewModel"/>
  </data>

  <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="@{user.name}"/>

    <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="@{user.email}"/>

  </LinearLayout>
</layout>

 

 

리스트뷰에 사용할 어댑터를 만들어주면 끝난다.

public class UserListViewAdapter extends BaseAdapter {
  private ObservableArrayList<UserViewModel> users = new ObservableArrayList<>();

  public void addAll(ObservableArrayList<UserViewModel> users) {
    this.users = users;
    notifyDataSetChanged();
  }

  @Override
  public int getCount() {
    return users.size();
  }

  @Override
  public UserViewModel getItem(int i) {
    return users.get(i);
  }

  @Override
  public long getItemId(int i) {
    return i;
  }

  @Override
  public View getView(int i, View view, ViewGroup viewGroup) {
    ListItemUserBinding binding;

    if(view == null) {
      LayoutInflater inflater = LayoutInflater.from(viewGroup.getContext());
      binding = DataBindingUtil.inflate(inflater, R.layout.list_item_user, viewGroup, false);
      view = binding.getRoot();
      view.setTag(binding);
    } else {
      binding = (ListItemUserBinding) view.getTag();
    }

    binding.setUser(users.get(i));

    return view;
  }
}

이렇게 하니까 좋은 게, ViewHolder 패턴도 아주 쉽게 사용할 수 있다.

 

 

 

알아서 스크롤바도 생기고, 번쩍임도 없이 잘 추가된다.