AndroidのViewPagerで表示ページによって画面の向きを変更する

ソースコード

ViewPagerの表示している画面によって画面の向きを固定・回転を変更するアプリです。

最初の画面では、画面は縦向きに固定しています。
2番目の画面では、画面は回転でき、縦向き・横向きの表示ができます。

*https://github.com/gesource/ViewPagerRotationSample

解説

画面を回転できるようにActivityを設定します。

AndroidManifest.xml

android:configChanges="orientation"

FragmentPagerAdapterのコンストラクタの第2引数にFragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENTを指定します。

MainActivity.java

    ViewPager viewPager = (ViewPager) findViewById(R.id.view_pager);
    viewPager.setAdapter(
            new MyFragmentPagerAdapter(
                    getSupportFragmentManager(),
                    FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT
            )
    );

FragmentのonResume()メソッドで画面の向きを設定します。

Fragment1.java

@Override
public void onResume() {
    super.onResume();
    Activity activity = getActivity();
    if (activity != null) {
        // 縦向き固定
        activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    }
}

Fragment2.java

@Override
public void onResume() {
    super.onResume();
    Activity activity = getActivity();
    if (activity != null) {
        // センサーの状態に従う
        activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
    }
}

以上。

Androidアプリでスワイプで画面を切り替える

ソースコード

ViewPagerを使って、スワイプでFragmentを切り替えるサンプルアプリです。

解説

Fragment1とFragment2の2つのフラグメントを作成します。

Fragment1.java

package com.example.viewpagersample;

import android.os.Bundle;

import androidx.fragment.app.Fragment;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class Fragment1 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                            Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_1, container, false);
    }
}

fragment_1.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/colorAccent"
    tools:context=".Fragment1">
</FrameLayout>

Fragment2.java

package com.example.viewpagersample;

import android.os.Bundle;

import androidx.fragment.app.Fragment;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class Fragment2 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                            Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_2, container, false);
    }
}

fragment_2.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/colorPrimary"
    tools:context=".Fragment2">

</FrameLayout>

FragmentPagerAdapterを継承したクラスを作成し、
表示するFragmentを指定します。

package com.example.viewpagersample;

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;

public class MyFragmentPagerAdapter extends FragmentPagerAdapter {
    public MyFragmentPagerAdapter(@NonNull FragmentManager fm) {
        super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
    }

    @NonNull
    @Override
    public Fragment getItem(int position) {
        switch (position) {
            case 1:
                return new Fragment2();
            default:
                return new Fragment1();
        }
    }

    @Override
    public int getCount() {
        return 2;
    }
}

メイン画面にViewPagerを配置します。

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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=".MainActivity">

    <androidx.viewpager.widget.ViewPager
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </androidx.viewpager.widget.ViewPager>

</androidx.constraintlayout.widget.ConstraintLayout>

ViewPagerにFragmentPagerAdapterを設定します。

MainActivity.java

package com.example.viewpagersample;

import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager.widget.ViewPager;

import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

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

        ViewPager viewPager = (ViewPager) findViewById(R.id.view_pager);
        viewPager.setAdapter(new MyFragmentPagerAdapter(getSupportFragmentManager()));
    }
}

以上。

Android StudioのGradleで”Bad file descriptor (connect failed)”エラーになったときの対応

Android Studioで「Sync Project with Gradle Files」を実行するとエラーになった。

Bad file descriptor (connect failed)

詳細を見ると、maven.google.comの接続に失敗しているようだ。

FAILURE: Build failed with an exception.

* What went wrong:
A problem occurred configuring root project 'XXXXXX'.
> Could not resolve all artifacts for configuration ':classpath'.
   > Could not download builder.jar (com.android.tools.build:builder:3.3.2)
      > Could not get resource 'https://maven.google.com/com/android/tools/build/builder/3.3.2/builder-3.3.2.jar'.
         > Could not HEAD 'https://maven.google.com/com/android/tools/build/builder/3.3.2/builder-3.3.2.jar'.
            > Connect to maven.google.com:443 [maven.google.com/172.217.26.3, maven.google.com/2404:6800:4004:801:0:0:0:2003] failed: Bad file descriptor (connect failed)

build.gradle (Project: XXXX) は次のようになっていた。

buildscript {
    repositories {
        jcenter()
        maven {
            url 'https://maven.google.com/'
            name 'Google'
        }
        google()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.3.2'
    }
}

allprojects {
    repositories {
        google()
        jcenter()
        maven {
            url 'https://maven.google.com/'
            name 'Google'
        }
    }
}

buildscriptのrepositoriesの順番を変更した。

buildscript {
    repositories {
        google()
        maven {
            url 'https://maven.google.com/'
            name 'Google'
        }
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.3.2'
    }
}

allprojects {
    repositories {
        google()
        jcenter()
        maven {
            url 'https://maven.google.com/'
            name 'Google'
        }
    }
}

「Sync Project with Gradle Files」を実行すると成功した。

repositories は信頼度の高い順に並べると良さそうだ。

Laravel5.5からLaravel6へバージョンアップしたときのメモ

composer.jsonを編集して、Laravel 5.5からLaravel 6に変更する

変更前

{
    "require": {
        "laravel/framework": "5.5.*",
    },
}

変更後

{
    "require": {
        "laravel/framework": "6.*",
    },
}

composer updateを実行して、Laravelを更新する

composer update

エラーが表示された

$ composer update laravel/framework
Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Conclusion: remove fideloper/proxy 3.3.4
    - Conclusion: don't install fideloper/proxy 3.3.4

composer.jsonを編集して、fideloper/proxyのバージョンを更新する。

{
    "require": {
        "fideloper/proxy": "^4.0",
    }
}

composer updateを実行して、Laravelを更新する

composer update

エラーが表示された

$ composer update 

    - laravelcollective/html v5.5 requires illuminate/view 5.5.* -> satisfiable by laravel/framework[v5.5.45], illuminate/view[v5.5.0, v5.5.16, v5.5.17, v5.5.2, v5.5.28, v5.5.33, v5.5.34, v5.5.35, v5.5.36, v5.5.37, v5.5.39, v5.5.40, v5.5.41, v5.5.43, v5.5.44].
    - laravelcollective/html v5.5 requires illuminate/view 5.5.* -> satisfiable by laravel/framework[v5.5.45], illuminate/view[v5.5.0, v5.5.16, v5.5.17, v5.5.2, v5.5.28, v5.5.33, v5.5.34, v5.5.35, v5.5.36, v5.5.37, v5.5.39, v5.5.40, v5.5.41, v5.5.43, v5.5.44].

composer.jsonを編集して、laravelcollective/htmlのバージョンを更新する。

変更前

{
    "require": {
        "laravelcollective/html": "5.5.*",
    },
}

変更後

{
    "require": {
        "laravelcollective/html": "6.0"
    },
}

composer updateを実行して、Laravelを更新する

composer update

エラーが表示された

Generating optimized autoload files
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover

Script @php artisan package:discover handling the post-autoload-dump event returned with error code 1
In cache.php line 91:

Call to undefined function str_slug()  

Laravel 6.x アップグレードガイド」の「文字列と配列のヘルパパッケージ」の項目によると、

アプリケーションへ新たにlaravel/helpersパッケージを追加すれば、こうしたヘルパを今までどおり利用できます。

ということで、laravel/helpersをインストールする

composer require laravel/helpers

composer updateを実行して、Laravelを更新する

composer update

無事に更新できた

$ composer update
Generating optimized autoload files
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover
Discovered Package: barryvdh/laravel-ide-helper
Discovered Package: fideloper/proxy
Discovered Package: intervention/image
Discovered Package: laravel/tinker
Discovered Package: laravelcollective/html
Discovered Package: nesbot/carbon
Package manifest generated successfully.

Laravelのバージョンを確認する

$ php artisan --version
Laravel Framework 6.0.4

composer.jsonを編集して、phpunit/phpunitのバージョンを更新する。

{
    "require-dev": {
        "phpunit/phpunit": "^8.0",
    }
}

PHPUnitのバージョンを確認する

$ vendor/bin/phpunit --version
PHPUnit 8.5.4 by Sebastian Bergmann and contributors.

テストを実行すると、エラーが発生した

$ vendor/bin/phpunit 
PHP Fatal error:  Declaration of Tests\Unit\XXXTest::setUp() must be compatible with Illuminate\Foundation\Testing\TestCase::setUp(): void in XXX.php on line 223

PHPUnitのバージョンが上がり、setup()メソッドが変更されている。
テストコードのsetUp()メソッドを修正する。

class XXXTest extends TestCase
{
    public function setUp() :void
    {
    }
}

テストを実行すると、警告が出た

The @expectedException, @expectedExceptionCode, @expectedExceptionMessage, and @expectedExceptionMessageRegExp annotations are deprecated. They will be removed in PHPUnit 9. Refactor your test to use expectException(), expectExceptionCode(), expectExceptionMessage(), or expectExceptionMessageMatches() instead.

アノテーションがdeprecatedなので、代わりのメソッドに置き換える。

変更前

/**
 * @expectedException InvalidArgumentException
 */
public function testHoge()
{

変更後

public function testHoge()
{
    $this->expectException(InvalidArgumentException::class);

ログファイルに下記のメッセージが出力されていた。

laravel.EMERGENCY: Unable to create configured logger. Using emergency logger. {"exception":"[object] (InvalidArgumentException(code: 0): Log [] is not defined. at /vagrant/app/vendor/laravel/framework/src/Illuminate/Log/LogManager.php:175)

このメッセージは、Laravel5.6からログの設定がconfig/logging.phpファイルに保存されるようになったため。
デフォルト設定ファイルをコピーしてconfig/logging.phpに配置する。

.envファイルにログの設定を追記する

LOG_CHANNEL=daily

Laravelのバージョンが最終バージョンでないため、更新する

composer.jsonを編集する

-        "laravel/framework": "6.*",
+        "laravel/framework": "^6.2",

更新する

composer update

エラーが発生した

- laravelcollective/html v6.0 requires illuminate/view 6.0.* -> satisfiable by laravel/framework[v6.0.4], illuminate/view[v6.0.0, v6.0.1, v6.0.2, v6.0.3, v6.0.4].

composer.jsonを編集する

-        "laravelcollective/html": "6.0"
+        "laravelcollective/html": "^6.0"

バージョンを確認する

$ php artisan --version
Laravel Framework 6.18.15

テストを実行して問題がないことを確認する

$ vendor/bin/phpunit 
PHPUnit 8.5.5 by Sebastian Bergmann and contributors.

OK (266 tests, 1833 assertions)

以上