KotlinでマテリアルデザインのViewを実装する

  • 8
    いいね
  • 0
    コメント

ディップ Advent Calendarの20日目です。

前回の続きになります。

せっかくなので、Kotlinでマテリアルデザイン関連のViewを実装するにはどうすればいいのか調べてみました。
基本的にはJavaで書いたソースコードをKotlinのプラグインに食わせるとKotlinに変換してくれるので、変換されたコードをKotlinっぽく書き直せるところは書き直してみます。
⇒というテーマで記事を書こうとしたのですが、KotlinプラグインのJava⇒Kotlinコード変換機能は大変優秀だったので、Null安全にかかわる変数宣言の辺り以外はほとんど直すところなかったです、Kotlinえらい。

Floating Action Button

Javaで書く場合

レイアウトファイルは基本的にJavaだろうとKotlinだろうと変わらないはず。

fragment_layout.xml
<android.support.design.widget.CoordinatorLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/blue_grey_600"
        android:transitionGroup="false">

    <include layout="@layout/map_description" />

    <android.support.design.widget.FloatingActionButton
            android:id="@+id/fab"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:srcCompat="@drawable/ic_panorama"
            android:contentDescription="@string/action_panorama"
            android:visibility="invisible"/>

</android.support.design.widget.CoordinatorLayout>
MyFragment.java
package com.samples.fragment;

import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.transition.ChangeBounds;
import android.transition.Fade;
import android.transition.Transition;
import android.transition.TransitionInflater;
import android.transition.TransitionManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class MyFragment extends Fragment {

    public static final String TAG = "MyFragment";
    private FloatingActionButton mFloatingActionButton;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View contentView = inflater.inflate(R.layout.fragment_layout, container, false);
        mFloatingActionButton = (FloatingActionButton) contentView.findViewById(R.id.fab);
        mFloatingActionButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Log.d(TAG, "FAB Pushed");
            }
        });
        return contentView;
    }
}

Kotlinで書く場合

Kotlinのプラグインに食わせると以下のコードを吐き出してくれますが、直せるところは直してみる。

MyFragment.kt(プラグインが変換した直後)
class DetailFragment : Fragment(), DataView<Detail> {
    private var mFloatingActionButton: FloatingActionButton? = null

    override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        val contentView = inflater!!.inflate(R.layout.fragment_detail, container, false)
        mFloatingActionButton = contentView.findViewById(R.id.fab) as FloatingActionButton
        mFloatingActionButton!!.setOnClickListener(View.OnClickListener {
            Log.d(TAG, "FAB Pushed")
        })
        return contentView
    }
}

View.OnClickListener自体はJavaのコードなので、SAM変換が使えます。
View.OnClickListenerをview ->に変えても大丈夫です。

MyFragment.kt(SAMに修正)
class DetailFragment : Fragment(), DataView<Detail> {
    private var mFloatingActionButton: FloatingActionButton? = null

    override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        val contentView = inflater!!.inflate(R.layout.fragment_detail, container, false)
        mFloatingActionButton = contentView.findViewById(R.id.fab) as FloatingActionButton

        // View.OnClickListenerをview ->に変える
        mFloatingActionButton?.setOnClickListener {view ->
            Log.d(TAG, "FAB Pushed")
        }
        return contentView
    }
}

Navigation Drawer

Navigation DrawerはJavaとKotlinの実装の差異はほとんどない様子。

Javaで書く場合

MainActivity.java
package com.sample.activity;

import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.view.View;
import android.support.design.widget.NavigationView;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;

public class MainActivity extends AppCompatActivity
        implements NavigationView.OnNavigationItemSelectedListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
                this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        drawer.setDrawerListener(toggle);
        toggle.syncState();

        NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
        navigationView.setNavigationItemSelectedListener(this);
    }

    @Override
    public void onBackPressed() {
        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START);
        } else {
            super.onBackPressed();
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    @SuppressWarnings("StatementWithEmptyBody")
    @Override
    public boolean onNavigationItemSelected(MenuItem item) {
        // Handle navigation view item clicks here.
        int id = item.getItemId();

        if (id == R.id.nav_camera) {
            // Handle the camera action
        } else if (id == R.id.nav_gallery) {

        } else if (id == R.id.nav_slideshow) {

        } else if (id == R.id.nav_manage) {

        } else if (id == R.id.nav_share) {

        } else if (id == R.id.nav_send) {

        }

        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        drawer.closeDrawer(GravityCompat.START);
        return true;
    }
}

Kotlinで書く場合

MainActivity.kt
package com.sample.activity

import android.os.Bundle
import android.support.design.widget.FloatingActionButton
import android.support.design.widget.Snackbar
import android.view.View
import android.support.design.widget.NavigationView
import android.support.v4.view.GravityCompat
import android.support.v4.widget.DrawerLayout
import android.support.v7.app.ActionBarDrawerToggle
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.Toolbar
import android.view.Menu
import android.view.MenuItem

class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val toolbar = findViewById(R.id.toolbar) as Toolbar
        setSupportActionBar(toolbar)

        val drawer = findViewById(R.id.drawer_layout) as DrawerLayout
        val toggle = ActionBarDrawerToggle(
                this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close)
        drawer.setDrawerListener(toggle)
        toggle.syncState()

        val navigationView = findViewById(R.id.nav_view) as NavigationView
        navigationView.setNavigationItemSelectedListener(this)
    }

    override fun onBackPressed() {
        val drawer = findViewById(R.id.drawer_layout) as DrawerLayout
        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START)
        } else {
            super.onBackPressed()
        }
    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        // Inflate the menu; this adds items to the action bar if it is present.
        menuInflater.inflate(R.menu.main, menu)
        return true
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        val id = item.itemId


        if (id == R.id.action_settings) {
            return true
        }

        return super.onOptionsItemSelected(item)
    }

    override fun onNavigationItemSelected(item: MenuItem): Boolean {
        // Handle navigation view item clicks here.
        val id = item.itemId

        if (id == R.id.nav_camera) {
            // Handle the camera action
        } else if (id == R.id.nav_gallery) {

        } else if (id == R.id.nav_slideshow) {

        } else if (id == R.id.nav_manage) {

        } else if (id == R.id.nav_share) {

        } else if (id == R.id.nav_send) {

        }

        val drawer = findViewById(R.id.drawer_layout) as DrawerLayout
        drawer.closeDrawer(GravityCompat.START)
        return true
    }
}

Activity Transition

Javaで書く場合

こちらを参照。
基本的にstyle.xmlとかAndroidManifest.xmlの設定、layout.xml周りはKotlinで書くにしても特に変わりはない様子。

Kotlinで書く場合

ActivityOptionsCompat.makeSceneTransitionAnimationを実行する箇所はJavaとはそれほど変わりがないですね。
Intentのインスタンス化もJavaでは
Intent intent = new Intent(MainActivity.this, ImaveDetailActivity.class);ですが、
Kotlinではval intent = Intent(this, ImageDetailActivity::class.java)でいけるようです。

MainActivity.kt

class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {

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

        //imageview
        val imageview = findViewById(R.id.imageview_content) as ImageView
        imageview.setOnClickListener { view ->
            val options = ActivityOptionsCompat.makeSceneTransitionAnimation(
                    this,
                    findViewById(R.id.imageview_content),
                    getString(R.string.trans_name))
            val intent = Intent(this, ImageDetailActivity::class.java)
            ActivityCompat.startActivity(this, intent, options.toBundle())

        }
    }
}

さいごに

まずはJavaで無駄な処理を書いていない綺麗なコードで実装して、プラグインで変換するとKotlinのコードができるので、それを見て学習するのがいいかもですね。
Kotlinかわいい。