日本国内にランダムにマーカーを配置する

っていうのをやりたい。見つけたのが

https://qiita.com/masato_hokota/items/0ed329ce387748700405

http://home.a00.itscom.net/hatada/c01/algorithm/polygon01.html

https://www.nttpc.co.jp/technology/measurement.html

https://www.nttpc.co.jp/technology/number_algorithm.html

こちらなのだけど、正直手に負えない。

そんで次に見つけたのが

https://shimz.me/blog/google-map-api/5116

https://suryu.me/post/evaluate_geo_points/

こちらでは、turf.jsというものを使うとできるということらしい。ただJavascript製なのでAndroidで使えない。で、もうちょっと探すと

https://www.mapbox.com/android-docs/java/overview/turf/

https://github.com/mapbox/mapbox-java

mapboxというところがそのturf.jsを作っていて、でそれをJavaも扱えるように移植もしてるみたい。mapboxはGoogleMapみたいな地図の会社で、Android向けのSDKとかAPIとかもあるみたい。ふむふむ。

リファレンス…説明書みたいな?が

  • sdk

https://www.mapbox.com/android-docs/api/map-sdk/6.7.2/index.html

  • turf

https://www.mapbox.com/android-docs/api/mapbox-java/libjava-turf/4.1.1/index.html

  • geojson

https://www.mapbox.com/android-docs/api/mapbox-java/libjava-geojson/4.1.1/index.html

  • api

https://www.mapbox.com/android-docs/api/mapbox-java/libjava-services/4.1.1/index.html

とかあった。GeoJSONあるやんけ…!こういうのってGoogleMapの方でも使えたりしないのかな。ていうか地図をMapboxのにすればいいのか、な?

StackOverflowの質問、公式からリンクされとる。

https://stackoverflow.com/questions/tagged/mapbox+android

日本語で解説してるとこないかなーと探したけれど

https://qiita.com/gupuru/items/41bd429127980d3d6937

http://tokyo.supersoftware.co.jp/technology/6981

http://tokyo.supersoftware.co.jp/code/7547

http://smellman.hatenablog.com/entry/2015/09/21/080057

https://cpoint-lab.co.jp/article/201810/%E3%80%90android%E3%80%91mapbox%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E5%9C%B0%E5%9B%B3%E4%B8%8A%E3%81%AB%E3%83%94%E3%83%B3%E3%82%92%E7%AB%8B%E3%81%A6%E3%82%8B%E6%96%B9%E6%B3%95/

とか。導入とかは見つかるんだけど、turfとの連携となるとちょっと見つからなかった。

うむむ、時間かかるかもだけど、挑戦してみよう。

カテゴリー: したい | タグ: , , , , , | コメントする

こまごま

こまごま備忘録

自分の場合、なので他の機種でもそうなのか分からん。

フルスクリーンにする

AndroidMnifest.xml

android:theme="@style/Theme.AppCompat.Light.NoActionBar"

onCreate内

getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

2つ追加した。人によって違いすぎて、何が正しいのか分からん。自分の場合は、これでできた。

参考

https://jirosoft.com/wordpress/android-memo/memo001/

http://pentan.info/android/app/status_bar_hidden.html

https://akira-watson.com/android/theme-notitlebar.html

https://qiita.com/furu8ma/items/4328c793250b10313cd7

http://ntoshi1900.hatenablog.jp/entry/2018/01/21/%E3%82%A2%E3%83%97%E3%83%AA%E3%81%AE%E3%82%BF%E3%82%A4%E3%83%88%E3%83%AB%E3%83%90%E3%83%BC%E3%82%92%E6%B6%88%E3%81%99%EF%BC%88Android_Studio_3.0.1%EF%BC%89

https://qiita.com/Mocacamo/items/9849d5590d2109eaf4b8

アプリの背景に画像を設定

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

    <ImageView
        android:id="@+id/imageView2"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:scaleType="centerCrop"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@drawable/bg_snow_diamond_dust"
        tools:layout_editor_absoluteX="0dp" />

レイアウト内にImageViewを入れてandroid:scaleType=”centerCrop”を設定する。これもIDEさんに怒られてるので正しいのかわからない。

参考

http://hakoniwadesign.com/?p=5890

ImgButtonの背景を透過する

android:background="@null"

参考

https://gendosu.jp/archives/670

android:onClickでエラー

Method has incorrect signature less… (Ctrl+F1)
Checks if the method specified in onClick XML attribute is declared in related activity

というエラーがxmlで出た。typoが原因かなと思ったけどそうじゃなく、Activity内の当該メソッドに

public void onClick(View v){
    Toast.makeText(this,"test",Toast.LENGTH_SHORT).show();
}

View vが入ってなかったのが原因だった。

カテゴリー: のーと | タグ: , , , , | コメントする

GeoJSONから逃げない

地図を扱う時にGeoJSONが使えると便利だそうなので調べる。

GeoJSON

{}がオブジェクト。[]が配列。JSONっていう形式があって、その派生みたい。FeatureCollection > Feature > Point > coordinatesみたいなのを作りたい。

JSONObject・JSONArray

この2つを使うとJSON形式が作れるようで、GeoJSONは特定のname, valueを含めてけばいいようだ。

public class GeoJASONMainActivity extends AppCompatActivity {
    TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_geo_jasonmain);

        tv = findViewById(R.id.editText);
        makeJSON();
    }

    void makeJSON() {
        String[] list = {"138.7306,35.3625", "141.936667,45.5225", "123.805056,24.049806", "145.816278,43.385056", "123.004722,24.468333"};
        JSONObject json = new JSONObject();
        JSONObject nullobj = new JSONObject();
        String[] listr = new String[5000];

        for (int i = 0; i <= 4999; i++) {
            Random r = new Random();
            double randomValueLat = 123.004722 + (145.816278 - 123.004722) * r.nextDouble();
            double randomValueLon = 24.049806 + (45.5225 - 24.049806) * r.nextDouble();
            listr[i] = String.valueOf(randomValueLat) + "," + String.valueOf(randomValueLon);
        }

        try {
            json.put("type", "FeatureCollection");
            JSONArray featureList = new JSONArray();

            for (String obj : listr) {
                JSONObject point = new JSONObject();
                point.put("type", "Point");

                JSONArray coord = new JSONArray("[" + obj + "]");
                point.put("coordinates", coord);

                JSONObject feature = new JSONObject();
                feature.put("type", "Feature");
                feature.put("properties", nullobj);
                feature.put("geometry", point);
                featureList.put(feature);

                json.put("features", featureList);
            }

        } catch (JSONException e) {
            e.printStackTrace();
        }

        tv.setText(json.toString());

    }
}

で、こうなった。editTextに表示させてる。

そんで、いったんPCに送ってgeojson.ioで表示してみた。

富士山+東西南北端

100個

1000個

5000個

10000個もやってみたけど重すぎて途中でやめた。

参考

https://s.kitazaki.name/docs/geojson-spec-ja.html

https://ja.wikipedia.org/wiki/GeoJSON

https://ja.wikipedia.org/wiki/%E6%97%A5%E6%9C%AC%E3%81%AE%E7%AB%AF%E3%81%AE%E4%B8%80%E8%A6%A7

https://stackoverflow.com/questions/19311526/how-to-convert-java-object-in-to-geojson-required-by-d3-graph

https://tools.m-bsys.com/development_tooles/json-beautifier.php

http://geojson.io/

https://ameblo.jp/miwahika93/entry-11522370629.html

カテゴリー: のーと | タグ: , , , , , | コメントする

double型で指定の範囲からランダム

Random r = new Random();
double randomValue = rangeMin + (rangeMax - rangeMin) * r.nextDouble();

テスト

for (int i = 1 ; i <=5 ; i++){
    Random r = new Random();
    double randomValue = 123.004722 + (145.816278 - 123.004722) * r.nextDouble();
    System.out.println(randomValue);
}

結果

132.99636946543728
134.89191866434484
139.8226971022699
124.47873288915928
142.50470752520263

参考

https://stackoverflow.com/questions/3680637/generate-a-random-double-in-a-range

カテゴリー: のーと | タグ: , | コメントする

現在地を表示したい

現在地を取得して地図上に表示したいので調べた。

現在地の取得&表示

うまく自分の中でまとまってないけど、できはした。権限の話やらなんやら。

うまく言葉で書けないから、ソースだけ書いとこ。

double lat = 0;
double lon = 0;
LocationManager locationmanager = (LocationManager) getSystemService(LOCATION_SERVICE);
GPSLocationListener locationlistener = new GPSLocationListener();
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {

    String[] permissions = {Manifest.permission.ACCESS_FINE_LOCATION};
    ActivityCompat.requestPermissions(this, permissions, 1000);
    return;
}
locationmanager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 1000, locationlistener);
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);

    if (requestCode == 1000 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
        GPSLocationListener locationlistener = new GPSLocationListener();
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            return;
        }
        locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 1000, locationlistener);
    }
}
private class GPSLocationListener implements LocationListener {
    @Override
    public void onLocationChanged(Location location) {
        lat = location.getLatitude();
        lon = location.getLongitude();

        LatLng latlon = new LatLng(lat, lon);

        marker.setPosition(latlon);
        mMap.moveCamera(CameraUpdateFactory.newLatLng(latlon));
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
    }

    @Override
    public void onProviderEnabled(String provider) {
    }

    @Override
    public void onProviderDisabled(String provider) {
    }
}

色々あった。

参考

[affi id=2]

https://qiita.com/hotdrop_77/items/bb8c97a3c197ee4f44df#4-gps%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%97%E3%81%A6%E7%8F%BE%E5%9C%A8%E4%BD%8D%E7%BD%AE%E3%82%92%E5%88%9D%E6%9C%9F%E8%A1%A8%E7%A4%BA%E3%81%99%E3%82%8B%EF%BC%92%E5%9B%9E%E7%9B%AE

https://developer.android.com/reference/android/location/LocationManager.html#requestLocationUpdates(java.lang.String,%20long,%20float,%20android.location.LocationListener)

http://www.asahi-net.or.jp/~gq4r-msm/html/k-age63/android/html/1800a-gps.html

カテゴリー: のーと | タグ: , , , , , | コメントする

GoogleMap+AndroidStudioでマーカーをgifアニメっぽくする

すごいぞ、できたぞ。若干興奮して目が冴えている。

マーカーをアニメにしたい

public class MapsActivity extends FragmentActivity implements
        OnMapReadyCallback, GoogleMap.OnMapLoadedCallback {

    Marker marker;
    Handler handler = new Handler();
    BitmapDescriptor iconSanta;
    BitmapDescriptor iconSanta01;
    BitmapDescriptor iconSanta02;
    BitmapDescriptor iconSanta03;
    BitmapDescriptor iconSanta04;
    BitmapDescriptor iconSanta05;
    BitmapDescriptor iconSanta06;
    BitmapDescriptor iconSanta07;
    BitmapDescriptor iconSanta08;
    BitmapDescriptor iconSanta09;
    BitmapDescriptor iconSanta10;
    Runnable runnable = new Runnable() {
        @Override
        public void run() {

            switch (marker.getTag().toString()) {
                case ("i"):
                    marker.setIcon(iconSanta01);
                    marker.setTag("1");
                    break;

                case ("1"):
                    marker.setIcon(iconSanta02);
                    marker.setTag("2");
                    break;

                case ("2"):
                    marker.setIcon(iconSanta03);
                    marker.setTag("3");
                    break;

                case ("3"):
                    marker.setIcon(iconSanta04);
                    marker.setTag("4");
                    break;

                case ("4"):
                    marker.setIcon(iconSanta05);
                    marker.setTag("5");
                    break;

                case ("5"):
                    marker.setIcon(iconSanta06);
                    marker.setTag("6");
                    break;

                case ("6"):
                    marker.setIcon(iconSanta07);
                    marker.setTag("7");
                    break;

                case ("7"):
                    marker.setIcon(iconSanta08);
                    marker.setTag("8");
                    break;

                case ("8"):
                    marker.setIcon(iconSanta09);
                    marker.setTag("9");
                    break;

                case ("9"):
                    marker.setIcon(iconSanta10);
                    marker.setTag("10");
                    break;

                case ("10"):
                    marker.setIcon(iconSanta01);
                    marker.setTag("1");
                    break;
            }
            handler.postDelayed(runnable, 200);

        }
    };
    private GoogleMap mMap;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_maps);
        // Obtain the SupportMapFragment and get notified when the map is ready to be used.
        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);

        iconSanta = BitmapDescriptorFactory.fromResource(R.drawable.christmas_santa_sori_i);
        iconSanta01 = BitmapDescriptorFactory.fromResource(R.drawable.christmas_santa_sori_01);
        iconSanta02 = BitmapDescriptorFactory.fromResource(R.drawable.christmas_santa_sori_02);
        iconSanta03 = BitmapDescriptorFactory.fromResource(R.drawable.christmas_santa_sori_03);
        iconSanta04 = BitmapDescriptorFactory.fromResource(R.drawable.christmas_santa_sori_04);
        iconSanta05 = BitmapDescriptorFactory.fromResource(R.drawable.christmas_santa_sori_05);
        iconSanta06 = BitmapDescriptorFactory.fromResource(R.drawable.christmas_santa_sori_06);
        iconSanta07 = BitmapDescriptorFactory.fromResource(R.drawable.christmas_santa_sori_07);
        iconSanta08 = BitmapDescriptorFactory.fromResource(R.drawable.christmas_santa_sori_08);
        iconSanta09 = BitmapDescriptorFactory.fromResource(R.drawable.christmas_santa_sori_09);
        iconSanta10 = BitmapDescriptorFactory.fromResource(R.drawable.christmas_santa_sori_10);

    }


    /**
     * Manipulates the map once available.
     * This callback is triggered when the map is ready to be used.
     * This is where we can add markers or lines, add listeners or move the camera. In this case,
     * we just add a marker near Sydney, Australia.
     * If Google Play services is not installed on the device, the user will be prompted to install
     * it inside the SupportMapFragment. This method will only be triggered once the user has
     * installed Google Play services and returned to the app.
     */
    @Override
    public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;

        LatLng fuji = new LatLng(35.362859, 138.730883);

        BitmapDescriptor iconSanta = BitmapDescriptorFactory.fromResource(R.drawable.christmas_santa_sori_i);
        MarkerOptions option = new MarkerOptions();

        option.icon(iconSanta).position(fuji);
        marker = mMap.addMarker(option);
        mMap.moveCamera(CameraUpdateFactory.newLatLng(fuji));

        mMap.setOnMapLoadedCallback(this);

    }

    @Override
    public void onMapLoaded() {

        marker.setIcon(iconSanta);
        marker.setTag("i");
        handler.post(runnable);
    }

    protected void onDestroy() {
        super.onDestroy();

        handler.removeCallbacks(runnable);
    }

}

マーカーを画像にしたのはいいけど、動きが無いのでなんか味気なかった。動かし方を調べていたんだけど、全然見つからなかった。そんな折、StackOverflowに同じように悩んでる人を見つけ、そのポストの内容を参考にやってみた。コードは自分でも汚ねぇコードだなと思うが、できた喜びがすごい。

画像を用意する

コマ撮り画像を作成する

コマ撮りアニメの要領で、一枚ずつ画像を用意する。大変めんどくさい。

画像をdrawableにぶっこむ

ふつーにエクスプローラーでコピーしてフォルダ右クリックのペースト。なんかのダイアログが出るのでv24って付いてるやつを選んだ。

画像を扱う準備をする

BitmapDescriptor iconSanta;
BitmapDescriptor iconSanta01;
BitmapDescriptor iconSanta02;
BitmapDescriptor iconSanta03;
…

枚数分BitmapDescriptor型の変数を宣言してonCreate内で

iconSanta01 = BitmapDescriptorFactory.fromResource(R.drawable.christmas_santa_sori_01);
iconSanta02 = BitmapDescriptorFactory.fromResource(R.drawable.christmas_santa_sori_02);
iconSanta03 = BitmapDescriptorFactory.fromResource(R.drawable.christmas_santa_sori_03);
…

リソースから読み込む。10枚分書いてると心が折れそうになるけど頑張る。

マーカーの画像を変える

Marker型の変数を宣言&代入

Marker marker;
marker = mMap.addMarker(option);

GoogleMap.addMarkerの戻り値がMarkerということで、addしたマーカーを扱えるように変数に代入しとく。StackOverflowの人は、MarkerOptionsとaddMarkerを利用しようとして、できなかったから質問したみたい。あなたのおかげです、ありがとう!

OnMapLoadedCallbackを使う

public class MapsActivity extends FragmentActivity implements
        OnMapReadyCallback, GoogleMap.OnMapLoadedCallback {
mMap.setOnMapLoadedCallback(this);
@Override
public void onMapLoaded() {

    marker.setIcon(iconSanta);
    marker.setTag("i");
    handler.post(runnable);
}

onMapReady内でhandler使うと、いつまで経っても準備が終わらないとかになったりするかな(※試してない)と思ったので、onStart()的なものがないかと探したら、OnMapLoadedCallbackなるものを見つけた。Mapの準備が終わるとこの中の処理が呼ばれるみたい。ここら辺よくわかってない。

で、そこでsetTag()でマーカーに初期値を付けといてあげる。そしてまたあのHandlerさんの出番や!

Handler.postDelayed使う

Runnable runnable = new Runnable() {
    @Override
    public void run() {

        switch (marker.getTag().toString()) {
            case ("i"):
                marker.setIcon(iconSanta01);
                marker.setTag("1");
                break;

            case ("1"):
                marker.setIcon(iconSanta02);
                marker.setTag("2");
                break;

…

            case ("10"):
                marker.setIcon(iconSanta01);
                marker.setTag("1");
                break;
        }
        handler.postDelayed(runnable, 200);
    }
};

StopWatch作っててよかった、何となく分かるので臆さなかったぞ。Markerに付けたTagを調べて、コマ送りの順繰りにswitch文で処理を回してあげる。MarkerのsetIconで画像を変えられるので10枚分書いた、気が狂いそうになるけど頑張った。そんで最後のpostDelayedでコマ送りの間隔を設定した。小さくすれば早く動くし、大きくすればゆっくりになる。

所見

とにかくできて嬉しい!動いてるとやっぱかわいいね。ただ、このやり方はあんまり良いやり方じゃないみたい。こう、端末の計算資源を大きく使う感じになるらしい。Googleがgifアニメに対応してくれればいいのにな。いやー満足満足。引き続き地図をグリグリしてこっと。

参考

https://stackoverflow.com/questions/32715920/gif-type-animation-for-marker-in-google-map-api-android

https://developers.google.com/maps/documentation/android-sdk/marker

https://seesaawiki.jp/w/moonlight_aska/d/%C3%CF%BF%DE%A4%CE%C9%C1%B2%E8%A4%AC%B4%B0%CE%BB%A4%B9%A4%EB%A4%C8….

https://dalomo.net/blog/2018/11/25/183/

カテゴリー: のーと | タグ: , , , , , | コメントする

GoogleMapの任意の位置にマーカーを立てる

地図を表示させるぞ

…と思ってもそもそやっていたのだけれど、途中のコード残しとくの忘れた。とりあえず最終的なやつ。

public class MapsActivity extends FragmentActivity implements OnMapReadyCallback {

    private GoogleMap mMap;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_maps);
        // Obtain the SupportMapFragment and get notified when the map is ready to be used.
        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);
    }


    /**
     * Manipulates the map once available.
     * This callback is triggered when the map is ready to be used.
     * This is where we can add markers or lines, add listeners or move the camera. In this case,
     * we just add a marker near Sydney, Australia.
     * If Google Play services is not installed on the device, the user will be prompted to install
     * it inside the SupportMapFragment. This method will only be triggered once the user has
     * installed Google Play services and returned to the app.
     */
    @Override
    public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;

        LatLng fuji = new LatLng(35.362859,138.730883);

        BitmapDescriptor iconSanta = BitmapDescriptorFactory.fromResource(R.drawable.christmas_santa_sori_s);
        MarkerOptions option = new MarkerOptions();
        option.icon(iconSanta).position(fuji);
        mMap.addMarker(option);
        mMap.moveCamera(CameraUpdateFactory.newLatLng(fuji));
    }
}

めちゃめちゃ簡単なんだなぁ。ついでにマーカーの画像も変えてみた。

参考

https://developers.google.com/maps/documentation/android-sdk/start

https://developers.google.com/android/reference/com/google/android/gms/maps/model/MarkerOptions

https://seesaawiki.jp/w/moonlight_aska/d/%A5%DE%A1%BC%A5%AB%A1%BC%A4%F2%A5%AB%A5%B9%A5%BF%A5%DE%A5%A4%A5%BA%A4%B9%A4%EB

https://www.irasutoya.com/2012/10/blog-post_24.html

カテゴリー: のーと | タグ: , , , , , | コメントする

音楽を鳴らす

音楽を鳴らしたい

rawフォルダを作って素材を入れる

 

ファイル名は「_」と小文字しかダメらしい。

ボタンを押して音楽を鳴らす

public class BGM extends AppCompatActivity {
    Button btPlay;
    MediaPlayer player;
    int r = (int) (Math.random() * 10);
    String mediaFileUriStr;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bgm);

        btPlay = findViewById(R.id.button);
        player = new MediaPlayer();

        if (r <= 4) {
            mediaFileUriStr = "android.resource://" + getPackageName() + "/" + R.raw.wakuwaku_tanoshii_christmas;
        }else{
            mediaFileUriStr = "android.resource://" + getPackageName() + "/" + R.raw.thats_a_xmas;
        }

        Uri mediaFileUri = Uri.parse(mediaFileUriStr);
        try {
            player.setDataSource(this, mediaFileUri);
            player.setOnPreparedListener(new PlayerPreparedListener());
            player.setOnCompletionListener(new PlayerCompletionListener());
            player.prepareAsync();
        }
        catch (IOException e) {
        }

    }

    public void onPlayButtonClick(View v) {
        if(!player.isPlaying()) {
            player.start();
            player.setLooping(true);
            btPlay.setText("Pause");
        }else {
            player.pause();
            btPlay.setText("Play");
        }
    }

    protected void onDestroy() {
        super.onDestroy();

        if(player.isPlaying()) {
            player.stop();
        }

        player.release();
        player = null;
    }

    private class PlayerPreparedListener implements MediaPlayer.OnPreparedListener {

        @Override
        public void onPrepared(MediaPlayer mp) {
            btPlay.setEnabled(true);
        }
    }

    private class PlayerCompletionListener implements MediaPlayer.OnCompletionListener {

        @Override
        public void onCompletion(MediaPlayer mp) {
            btPlay.setText("Pause");
        }
    }

MediaPlayerってのを使うと簡単にできた。try-catchはサンプルに書いてあったんだけど、これは何をするんだろう。リスナが2つもあってめんどい。asynchというのが出てきた。また非同期処理の話かー。あとにしよう。

起動したら勝手に音を鳴らす

public class BGM extends AppCompatActivity {
    MediaPlayer player;
    int r = (int) (Math.random() * 10);
    String mediaFileUriStr;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bgm);

        player = new MediaPlayer();

        if (r <= 4) {
            mediaFileUriStr = "android.resource://" + getPackageName() + "/" + R.raw.wakuwaku_tanoshii_christmas;
        } else {
            mediaFileUriStr = "android.resource://" + getPackageName() + "/" + R.raw.thats_a_xmas;
        }

        Uri mediaFileUri = Uri.parse(mediaFileUriStr);
        try {
            player.setDataSource(this, mediaFileUri);
            player.setOnPreparedListener(new PlayerPreparedListener());
            player.prepareAsync();
        } catch (IOException e) {
        }

    }

    @Override
    protected void onResume() {
        super.onResume();
        if (!player.isPlaying()) {
            player.start();
            player.setLooping(true);
        }
    }

    protected void onPause() {
        super.onPause();
        if (player.isPlaying()) {
            player.pause();
        }
    }

    protected void onDestroy() {
        super.onDestroy();

        if (player.isPlaying()) {
            player.stop();
        }

        player.release();
        player = null;
    }

    private class PlayerPreparedListener implements MediaPlayer.OnPreparedListener {

        @Override
        public void onPrepared(MediaPlayer mp) {
            player.start();
            player.setLooping(true);
        }
    }

ボタンを取っ払って、onResumeとonPauseを追加。そんでPlayerPreparedListenerにstartを書く。ふむふむ。

画面遷移しても音を鳴らす

Serviceというものが必要だそうなので、まずそのクラスを作る。

簡単。

BGM.java

public class BGM extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bgm);

        Intent intent = new Intent(this, BGMService.class);
        startService(intent);
    }

    public void onClickMove(View view) {

        Intent intent = new Intent(this, Nanimonai.class);
        startActivity(intent);
    }

    protected void onDestroy() {
        super.onDestroy();

        Intent intent = new Intent(this, BGMService.class);
        stopService(intent);
    }

}

BGMService.java

public class BGMService extends Service {
    MediaPlayer player;
    String mediaFileUriStr;
    int r = (int) (Math.random() * 10);

    @Override
    public void onCreate() {
        super.onCreate();

        player = new MediaPlayer();
    }

    public int onStartCommand(Intent intent, int flags, int startId) {

        if (r <= 4) {
            mediaFileUriStr = "android.resource://" + getPackageName() + "/" + R.raw.wakuwaku_tanoshii_christmas;
        } else {
            mediaFileUriStr = "android.resource://" + getPackageName() + "/" + R.raw.thats_a_xmas;
        }

        Uri mediaFileUri = Uri.parse(mediaFileUriStr);
        try {
            player.setDataSource(this, mediaFileUri);
            player.setOnPreparedListener(new PlayerPreparedListener());
            player.prepareAsync();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return super.onStartCommand(intent, flags, startId);
    }

    private class PlayerPreparedListener implements MediaPlayer.OnPreparedListener {
        @Override
        public void onPrepared(MediaPlayer mp) {
            mp.start();
            mp.setLooping(true);
        }
    }

    public void onDestroy() {
        super.onDestroy();
        if(player.isPlaying()) {
            player.stop();
        }
        player.release();
        player = null;
    }

    public BGMService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }
}

えーと、画面を遷移するときはIntent使う。Serviceの場合もそれと似た感じ。Serviceクラスに処理を書いて、メインからstartService(Intent), stopService(Intent)で開始・終了。だよね。

コード減ったなぁ。Serviceも非同期処理の一種らしい。違いがよくわかんないな。下2つのブロックは何に使うんだろう。

とりあえず、やりたいことはできることが分かったぞ。今度は地図の使い方勉強しよう。

参考

http://www.hurtrecord.com/bgm/35/wakuwaku-tanoshii-christmas.html

http://www.music-note.jp/bgm/xmas.html

https://codezine.jp/article/detail/9762

https://codezine.jp/article/detail/9529

https://docs.oracle.com/javase/jp/7/api/java/util/Random.html

https://codezine.jp/article/detail/9529

https://codezine.jp/article/detail/9817

https://developer.android.com/guide/components/activities

https://qiita.com/tk_daze/items/bc83c69750e5f2e4015c

[amazon asin=”4798152021″ kw=”Android”]

カテゴリー: のーと | タグ: , , , , | コメントする

くやしいのでAndroidStudioでストップウォッチ作る

リバーシの時間表示の時、訳わかんなくて悔しかったので、ストップウォッチ単体で作ってみる。つっても大体コピペコード。

ストップウォッチ色々

Handlerだけ

動いてるとこ

ソース

public class StopWatch extends AppCompatActivity {
    Button btStart;
    Button btStop;
    TextView disp;
    Handler handler = new Handler();
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            count++;
            disp.setText(dataFormat.format(count*period));
            handler.postDelayed(this, period);
        }
    };
    int count, period;
    private SimpleDateFormat dataFormat =
            new SimpleDateFormat("mm:ss.S", Locale.US);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_stop_watch);

        btStart = findViewById(R.id.button);
        btStop = findViewById(R.id.button2);
        disp = findViewById(R.id.textView);

        count = 0;
        period = 100;

    }

    public void onClickStart(View v){
        handler.post(runnable);
    }

    public void onClickStop(View v){
        handler.removeCallbacks(runnable);
        disp.setText(dataFormat.format(0));
        count = 0;
    }

所感

処理の順序が全然わからんかったのがちょっと分かった。

参考

https://akira-watson.com/android/timertask.html

https://dev.classmethod.jp/smartphone/android/android-tas/

Timer・TimerTask使う

動いてるとこ

そーす

public class StopWatch extends AppCompatActivity {
    Button btStart;
    Button btStop;
    TextView disp;
    Handler handler = new Handler();
    int count;
    MyTimerTask timerTask = null;
    Timer mTimer = null;
    private SimpleDateFormat dataFormat = new SimpleDateFormat("mm:ss.S", Locale.US);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_stop_watch);

        btStart = findViewById(R.id.button);
        btStop = findViewById(R.id.button2);
        disp = findViewById(R.id.textView);

        count = 0;

    }

    public void onClickStart(View v) {
        if (mTimer == null) {
            timerTask = new MyTimerTask();
            count = 0;
            mTimer = new Timer(true);
            mTimer.schedule(timerTask, 100, 100);
        }
    }

    public void onClickStop(View v) {
        if (mTimer != null) {
            mTimer.cancel();
            mTimer = null;
            disp.setText(dataFormat.format(0));
        }
    }

    class MyTimerTask extends TimerTask {

        @Override
        public void run() {
            handler.post(new Runnable() {
                public void run() {

                    count++;
                    disp.setText(dataFormat.format(count * 100));
                }
            });
        }
    }
}

所感

スタートボタン押すとmTimerが空かどうかチェック。mTimerにTimerをnewする時にtrueでデーモンスレッドにする。mTimer.schedule(timerTask, delay, period)でスケジュールをセット。timerTaskの中身は、handlerでメインスレッドに投げる処理(1個目のrun)、カウントアップしてUIに表示させる処理(2個目のrun)、でこの2つをdelay, periodを100msの設定でセットしてるので、その間隔で2つの処理が実行される。この時本当は、scheduleではなく、scheduleAtFixedRateを使った方が良いみたい。

scheduleメソッド:前回のタスク実行後を基準にperiodミリ秒後が次のタイミング
scheduleAtFixedRateメソッド:初回のタスク実行時間を基準にperiodミリ秒 x n回後が次のタイミング

なので、scheduleだとタスク処理の時間分、次回タスク処理の開始が遅れるからかな?そうすると多分ちょっとずつ時間が遅れるんだろう。他にscheduleWithFixedDelayっていうのがあるみたい。

これ使った場合でも、Pauseした時にその時の値を保持しておいて、次回実行時にその値から始める、みたいにすれば一時停止ができるのかなぁ…なんとなく。

参考

https://techbooster.org/android/application/934/

https://blog.mumei-himazin.info/?p=474

https://qiita.com/opengl-8080/items/ee8e926cf75e4d6058a2

ScheduledExecutorService使う

動いてるとこ

ソース

public class StopWatch extends AppCompatActivity {
    Button btStart;
    Button btStop;
    Button btPause;
    TextView disp;
    Handler handler = new Handler();
    int count;
    MyTimerTask timerTask = null;
    ScheduledExecutorService scheduler = null;
    ScheduledFuture<?> sf = null;
    private SimpleDateFormat dataFormat = new SimpleDateFormat("mm:ss.S", Locale.US);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_stop_watch);

        btStart = findViewById(R.id.button);
        btStop = findViewById(R.id.button2);
        btPause = findViewById(R.id.button3);
        disp = findViewById(R.id.textView);

        count = 0;
    }

    public void onClickStart(View v) {
        if (scheduler == null) {
            scheduler = Executors.newScheduledThreadPool(1);
            timerTask = new MyTimerTask();
            sf = scheduler.scheduleAtFixedRate(timerTask, 100, 100, MILLISECONDS);
            count = 0;
        }
    }

    public void onClickPause(View v) {

        if (sf == null) {
            return;
        }

        if (sf.isCancelled()) {
            sf = scheduler.scheduleAtFixedRate(timerTask, 100, 100, MILLISECONDS);
            btPause.setText("Pause");
        } else {
            sf.cancel(true);
            btPause.setText("Resume");
        }
    }

    public void onClickStop(View v) {
        if (scheduler != null) {
            scheduler.shutdown();
            scheduler = null;
            sf = null;
            disp.setText(dataFormat.format(0));
        }
    }

    class MyTimerTask implements Runnable {

        @Override
        public void run() {
            handler.post(new Runnable() {
                public void run() {
                    count++;
                    disp.setText(dataFormat.format(count * 100));
                }
            });
        }
    }
}

所感

使い方は大体Timer・TimerTaskと似てるみたい。初期化する時にスレッド数を指定するのと、scheduleAtFixedRateの引数が、RunnableなのでMyTimerTaskをextends TimerTaskからimplements Runnableに変更、あとTimeUnitが必要なのと、止める時shutdownになるのとかが違う。shutdownするとやっぱり破棄されるのかな?

ScheduledFuture<?>が肝で、そういえばこの後ろの<?>ってなんだろう、scheduleAtFixedRateの戻り値がScheduledFuture<?>なので変数sfを用意して受けてあげる。そんでcancel(boolean)でスケジュールを保留状態にできる。保留状態の時、sf.isCancelledがtrueになるので、それで処理を分けてあげると。難しいのう。

参考

http://www.02.246.ne.jp/~torutk/javahow2/timer.html#doc1_id147

https://developer.android.com/reference/java/util/concurrent/ScheduledExecutorService

https://docs.oracle.com/javase/jp/8/docs/api/java/util/concurrent/Future.html

https://oshiete.goo.ne.jp/qa/111300.html

うぬぬ

これを1から自分で作れと言われた場合、絶対できないと思う。まだなんとなーく、ふわーっとした理解だなぁ。

補記:activity_stop_watch.xml

<?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=".StopWatch">

    <TextView
        android:id="@+id/textView"
        android:layout_width="0dp"
        android:layout_height="166dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:text="00:00.00"
        app:autoSizeTextType="uniform"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="12dp"
        android:layout_marginBottom="8dp"
        android:onClick="onClickStart"
        android:text="Start"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView"
        app:layout_constraintVertical_bias="0.141" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="12dp"
        android:layout_marginEnd="8dp"
        android:layout_marginBottom="8dp"
        android:onClick="onClickStop"
        android:text="Stop"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView"
        app:layout_constraintVertical_bias="0.141" />

    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginBottom="8dp"
        android:onClick="onClickPause"
        android:text="Pause"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/button2"
        app:layout_constraintStart_toEndOf="@+id/button"
        app:layout_constraintTop_toBottomOf="@+id/textView"
        app:layout_constraintVertical_bias="0.153" />
</android.support.constraint.ConstraintLayout>

 

カテゴリー: のーと | タグ: , , , | コメントする

リバーシのCOM戦を作る

COM戦を作る前に検討してみたけど、今から手番の選択をしたり、対人戦・対COM戦を分けたりがめんどくさいので、プレイヤーの手番は常に黒番かつCOM戦のみという形で実現したい。

COMくん

思考方法

マスを調べていって、置ける場所が見つかったら石を置く。簡単だけどこれで動くと思った。このあたり色んなアルゴリズムがあるらしく、奥深いみたい。あと自分にできそうなものと言ったら、置ける場所のひっくり返せる石の量を調べていって、一番大きい所に置くとかかなぁ。

実装

void comPutStone(){

    for (TextView[] cc : CellArray) {
        for (TextView c : cc) {
            if(c.getText().toString().equals("*")){
                putStone(c);
                return;
            }
        }
    }
}

そんでonClickの所を

switch(id) {

        if(board.PlayerTurn == 1) {
            TextView tv = findViewById(id);
            board.putStone(tv);
            board.setTurn();
            board.scanBoard();
            board.StoneCountOnBoard();
            board.checkFinish();
            break;
        }
}

while (board.PlayerTurn == -1){
    board.comPutStone(); 
    board.setTurn(); 
    board.scanBoard(); 
    board.StoneCountOnBoard(); 
    board.checkFinish();
}

こう変えて、できあがり。

気を抜いてやってると、普通に負けたりもする。あれだけなのに。

一段落

ちょっと飽きてきてるからか、なーんか達成感がない。まぁ、それだけじゃなくて、最初は新しくComクラス作ろうとしたり、Boardを継承してみたり、でもなんか上手くいかなくてこれに落ち着いたっていうのがね、なんかね。継承するとデフォルトコンストラクタがないよって言われるんだよね、なんでなんだろ。どうすればもっとスパッと流れよく作れるのかなぁ…。

とりあえずリバーシはここまでにしよう、一ヶ月やりっぱなしだもんな。なんか違うのやりたい。ちょっと思いついたやつがあるので、それを作っていこー!

参考

https://www.javadrive.jp/start/for/index7.html

カテゴリー: したい | タグ: , , , | コメントする