LoginSignup
9
9

More than 5 years have passed since last update.

ScalaでAndroid開発【ArrayAdapter編】

Last updated at Posted at 2015-09-06

Scalaを使ってAndroid開発をしてみよう、ArrayAdapter編です。
ArrayAdapterの仕様も忘れかけてたので、そこら辺も一緒に説明していきます。

ArrayAdapter(Java)

自作のクラスを使ったArrayAdapterは以下のように継承クラスを作る必要があります。
ソース引用元

public class UsersAdapter extends ArrayAdapter<User> {
    // View lookup cache
    private static class ViewHolder {
        TextView name;
        TextView home;
    }

    public UsersAdapter(Context context, ArrayList<User> users) {
       super(context, R.layout.item_user, users);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
       // Get the data item for this position
       User user = getItem(position);    
       // Check if an existing view is being reused, otherwise inflate the view
       ViewHolder viewHolder; // view lookup cache stored in tag
       if (convertView == null) {
          viewHolder = new ViewHolder();
          LayoutInflater inflater = LayoutInflater.from(getContext());
          convertView = inflater.inflate(R.layout.item_user, parent, false);
          viewHolder.name = (TextView) convertView.findViewById(R.id.tvName);
          viewHolder.home = (TextView) convertView.findViewById(R.id.tvHome);
          convertView.setTag(viewHolder);
       } else {
           viewHolder = (ViewHolder) convertView.getTag();
       }
       // Populate the data into the template view using the data object
       viewHolder.name.setText(user.name);
       viewHolder.home.setText(user.hometown);
       // Return the completed view to render on screen
       return convertView;
   }
}

Viewの生成

まずはViewを生成している所をScalaで書き直してみましょう。
コードを見るとgetViewの引数であるconvertViewはnullの場合があるみたいですね。
ソースの引用元にも解説は載っていますがざっくり説明すると
Androidでのリストのアイテム部分となるViewは初回時は生成する必要がありますが、スクロールなどした場合Viewは使いまわされるためです。
Javaでは単純にnullチェックでif分岐しているようですが、Scalaでnullチェックなんてナンセンスなので修正してみます。

val view = Option(convertView) getOrElse LayoutInflater.from(getContext).inflate(R.layout.item_user, parent, false)

1行にまとまりましたね。
とりあえずconvertViewはOptionで包んでnullの場合はgetOrElseで処理するようにしちゃいます。
以降の処理ではこのviewを使っていきます。

ViewHolder

これはソースコード見て思い出しました、そういえばそんなのもありましたねぇ・・・。
前述にある通りはViewは使いまわされるので一度findViewByIdしたのを毎回取得しなおすのはコストの無駄じゃない?って考えだったはずです。
なので初回時にViewHolderと呼ばれるクラスに格納してsetTagでView自体に紐づけして次回以降はgetTagで取得しているわけですね。
これもサクッとScalaにしちゃいましょう。

まずはViewHolderクラスを定義します。

case class ViewHolder(text1: TextView, text2: TextView)

case classは偉大ですね、格納用のクラス定義が楽です。
で、ViewHolderの生成部分を書きます。

val viewHolder = Option(view.getTag.asInstanceOf[ViewHolder]) getOrElse ViewHolder(view.findViewById(R.id.tvName).asInstanceOf[TextView], view.findViewById(R.id.tvHome).asInstanceOf[TextView])

ちょっと横に長いですがこんな感じです・・・、implicitとか使えばもう少し綺麗になるかもですね。
さっきと一緒でOptionで包んで処理しています。
Javaではクラスのキャスト時にnullだと例外発生しちゃうんで、nullチェックが面倒だった記憶があるのですが
asInstanceOfはモナドになっているみたい?なのでnullでもオッケーです。

ArrayAdapter(Scala)

ということで以下が完成版です。

class UsersAdapter(context: Context, users: java.util.List[User]) extends ArrayAdapter[User](context, R.layout.item_user, users) {

  case class ViewHolder(text1: TextView, text2: TextView)

  override def getView(position: Int, convertView: View, parent: ViewGroup): View = {
    val view = Option(convertView) getOrElse LayoutInflater.from(getContext).inflate(R.layout.item_user, parent, false)
    val viewHolder = Option(view.getTag.asInstanceOf[ViewHolder]) getOrElse ViewHolder(view.findViewById(R.id.tvName).asInstanceOf[TextView], view.findViewById(R.id.tvHome).asInstanceOf[TextView])

    viewHolder.text1.setText(getItem(position).name)
    viewHolder.text1.setText(getItem(position).hometown)

    view.setTag(viewHolder)
    view
  }
}

空白行を除いてJavaが27行、Scalaが10行ぐらい

圧倒的じゃないか

何?見づらい?
慣れです!

9
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
9