KotlinでRecyclerViewとAdapterを実装しました

f:id:shogonir:20170608001348p:plain

目次

  1. この記事の目的
  2. JavaのActivityのソース
  3. KotlinのActivityのソース
  4. JavaのAdapterのソース
  5. KotlinのAdapterのソース
  6. まとめ

 

1. この記事の目的

KotlinがAndroid開発言語として正式にサポートされたということで、勉強がてらJavaのソースをKotlinに翻訳します。

翻訳するJavaのソースは下記の「リストとカードの作成」から拝借しました。

developer.android.com

 

2. JavaのActivityのソース

「リストとカードの作成」にあるActivityのソースは下記の通りです。

public class MyActivity extends Activity {
    private RecyclerView mRecyclerView;
    private RecyclerView.Adapter mAdapter;
    private RecyclerView.LayoutManager mLayoutManager;

    @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)
        mAdapter = new MyAdapter(myDataset);
        mRecyclerView.setAdapter(mAdapter);
    }
    ...
}

 
onCreateの時に、mRecyclerViewにmLayoutManagerとmAdapterをセットしています。

 

3. KotlinのActivityのソース

先ほどのJavaのコードをKotlinに翻訳する上で必要になる知識は下記の2つです。

  1. Null許容型
  2. キャスト

3.1. KotlinにおけるNull許容型

KotlinではNullを代入できるかどうかを型で宣言することができます。

// non nullable type, cannot put null
mRecyclerView: RecyclerView  = null // Error!

// nullable type, can put null
mRecyclerView: RecyclerView? = null // Okay!

 
また、Nullチェックも非常にスマートに記述できます。

// if null, setAdapter will not executed, no error will thrown
mRecyclerView?.setAdapter(mAdapter);

// if null, setAdapter will not executed, runtime error will thrown
mRecyclerView!!.setAdapter(mAdapter);

3.2. キャスト

Kotlinではasキーワードを使ってキャストを行います。

// if findViewById returns null, runtime error will thrown
mRecyclerView = findViewById(R.id.recycler) as RecyclerView

// if findViewById returns null, mRecyclerView equals null, no error will thrown
mRecyclerView = findViewById(R.id.recycler) as? RecyclerView

3.3. 最終的に翻訳されたKotlinのActivityのソース

Null許容型とキャストの方法を踏まえて翻訳すると、下記のようになりました。

class MainActivity : AppCompatActivity() {

    private var mTextMessage: TextView? = null

    private var mRecyclerView: RecyclerView? = null
    private var mAdapter: MyAdapter? = null
    private var mLayoutManager: RecyclerView.LayoutManager? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        mTextMessage = findViewById(R.id.message) as TextView

        mRecyclerView = findViewById(R.id.main_recycler) as RecyclerView
        mRecyclerView?.setHasFixedSize(true)

        mLayoutManager = LinearLayoutManager(this)
        mRecyclerView?.layoutManager = mLayoutManager

        val myDataset: Array<String> = Array(10, {i: Int -> (i * i).toString() })
        mAdapter = MyAdapter(myDataset)
        mRecyclerView?.setAdapter(mAdapter)

        val navigation = findViewById(R.id.navigation) as BottomNavigationView
        navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)
    }
}

 

4. JavaのAdapterのソース

「リストとカードを作成する」より拝借したAdapterのソースは下記の通りです。

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
    private String[] mDataset;

    public static class ViewHolder extends RecyclerView.ViewHolder {
        public TextView mTextView;
        public ViewHolder(TextView v) {
            super(v);
            mTextView = v;
        }
    }

    public MyAdapter(String[] myDataset) {
        mDataset = myDataset;
    }

    @Override
    public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext())
                               .inflate(R.layout.my_text_view, parent, false);
        ...
        ViewHolder vh = new ViewHolder(v);
        return vh;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        holder.mTextView.setText(mDataset[position]);
    }

    @Override
    public int getItemCount() {
        return mDataset.length;
    }
}

 
RecyclerView.Adapterを継承したMyAdapterは、インナークラスにViewHolderを持っています。
インナークラスはただ渡されたTextViewを保持するのみです。
MyAdapterは必要があればLayoutInflatorでレイアウトを増やしたり、データをセットします。

 

5. KotlinのAdapterのソース

先ほどのJavaソースコードを翻訳するのに必要なのは下記の2つです。

  1. コンストラクタの書き方
  2. companion object

5.1. Kotlinにおけるコンストラクタの書き方

Kotlinではクラス名のすぐ後にコンストラクタ(プライマリコンストラクタ)を書くことができます。

class SampleClass(message: String) {
    val mMessage: String = message
}

 
このようにすると、フィールド変数に一度Nullが入ることもなく、気分がいいです。
もちろん複数のコンストラクタを定義することもできます。
その際は2つめ以降のコンストラクタをセカンダリコンストラクタといいます。

5.2. Kotlinにおけるcompanion object

Kotlinにはstaticというキーワードがありません。
staticと似たことを実現するのは、companion objectをつかいます。

class SampleClass(message: String) {
    val mMessage: String = message

    companion object {
        val Constants: Int = 0
    }
}

 
このようにクラスの中にcompanion objectを持つことで、Constantsという定数を定義できます。

5.3. 最終的に翻訳されたKotlinのAdapterのソース

class MyAdapter(myDataset: Array<String>): RecyclerView.Adapter<MyAdapter.ViewHolder>()  {

    val mDataset: Array<String> = myDataset

    class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {

        var mTextView: TextView? = itemView.findViewById(R.id.info_text) as? TextView

        companion object Factory {
            fun create(v: TextView): ViewHolder = ViewHolder(v)
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder {
        val v: View = LayoutInflater.from(parent?.context)
                .inflate(R.layout.my_text_view, parent, false)
        val vh: ViewHolder = ViewHolder(v)
        return vh
    }

    override fun onBindViewHolder(holder: ViewHolder?, position: Int) {
        holder?.mTextView?.text = mDataset[position]
    }

    override fun getItemCount(): Int = mDataset.size
}

 

6. まとめ

この記事ではAndroidアプリのJavaのソースを読んで、Kotlinに翻訳しました。
このようなちょっとしたソースコードを書いてみるだけでも、Kotlinのいいところが見つかりました。
Null許容型とNullチェックは簡潔で分かりやすくメリットが大きいです。