【Android】画像タップで拡大表示。さらにピンチインアウト可能にする。

これは何

Android 開発で ImageView をタップしたら画像を拡大表示し、それをピンチインアウトでズームできるようにする方法です。

利用ライブラリ

https://github.com/davemorrissey/subsampling-scale-image-view

ライブラリインストール方法は上にあるので割愛します

修正内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 拡大対象のImageViewにタップ時のリスナーをセット
mImageView.setOnClickListener(v -> {
SubsamplingScaleImageView imageViewEnlarged = new SubsamplingScaleImageView(getContext());
Bitmap bitmap = ((BitmapDrawable) mImageView.getDrawable()).getBitmap();
imageViewEnlarged.setImage(ImageSource.bitmap(bitmap));
// 画面の横幅を取得
Display display = ((Activity) getContext()).getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
float width = size.x;
float factor = width / bitmap.getWidth();
Dialog dialog = new Dialog(getContext());
// 画像をセットして表示する
dialog.setContentView(imageViewEnlarged);
dialog.getWindow().setLayout((int)(bitmap.getWidth()*factor), (int)(bitmap.getHeight()*factor));
dialog.show();
});

てか Android 開発、情報少なすぎません?泣
ただ画像をタップして拡大表示してピンチインアウトするだけのことがなんでこんなに情報がないのか?
なかなかライブラリに辿り着けなくて、結構ハマっちゃいました、、
ツライけどお互い頑張りましょう、、w

参考

[Android]ImageView の scaleType を秒で決める
[Android]メモリリークを起こさずに大きな画像を表示させるライブラリ「Subsampling Scale Image View」

【Android】ProGuardからR8に変更したらアプリが落ちるようになった原因と解決策

事象

ProGuard から R8 に乗り換えた後、アプリをビルドして起動したところ、
いくつかの画面がクラッシュしてアプリが落ちる不具合が発生しました

原因

GSON を利用していた場合、文字列をそのまま利用する必要があるが、難読化されてしまったため

解決策

proguard-project.txt に以下のコードを追加する

1
2
3
4
5
### for Gson in R8
-keepclassmembers,allowobfuscation class * {
@com.google.gson.annotations.SerializedName <fields>;
}
-keep,allowobfuscation @interface com.google.gson.annotations.SerializedName

これ結構重要なところなのに情報が少なすぎてビビる、、
Android 開発の情報もっと増えてくれ〜

参考

R8 互換性 FAQ

【Android】BuildConfigが見つからないエラー

BuildConfig が見つからないエラーになった時の解決策です

  1. Android Studio >ファイル>キャッシュの破棄>再起動
  2. Android Studio >ビルド>プロジェクトをクリーンにする
  3. Android Studio >ビルド>プロジェクトの再ビルド

参考

BuildConfig not getting created correctly (Gradle Android)

AndroidアプリでiBeaconを受信する

利用ライブラリ

https://github.com/AltBeacon/android-beacon-library

設定を変更

app/build.gradle に以下を追加

1
2
3
dependencies {
implementation 'org.altbeacon:android-beacon-library:2+'
}

受信コード

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import org.altbeacon.beacon.*
import kotlin.collections.ArrayList

class HogeActivity : AppCompatActivity(), BeaconConsumer {
private val TAG: String = "HogeActivity"
// ビーコンマネージャ宣言
private lateinit var mBeaconManager: BeaconManager
// iBeaconのデータを認識するためのParserフォーマット
private val IBEACON_FORMAT: String = "m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"

private val mRegion = Region("unique-id-001", null, null, null)

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_Hogee)
// ビーコンマネージャのインスタンス作成
mBeaconManager = BeaconManager.getInstanceForApplication(this)
// iBeaconのフォーマットをParserへ渡す
mBeaconManager.beaconParsers.add(BeaconParser().setBeaconLayout(IBEACON_FORMAT))
}

override fun onPause() {
super.onPause()
mBeaconManager.unbind(this)
Log.d(TAG, "ビーコンサービスを解除した")
}

override fun onResume() {
super.onResume()
mBeaconManager.bind(this)
Log.d(TAG, "ビーコンサービスを起動した")
}

override fun onBeaconServiceConnect() {

// 初期化。これがないと画面を2回以上開いた時に2重でデータを受信してしまう
mBeaconManager.removeAllMonitorNotifiers()
mBeaconManager.removeAllRangeNotifiers()
mBeaconManager.rangedRegions.forEach {region ->
mBeaconManager.stopRangingBeaconsInRegion(region)
}

mBeaconManager.addMonitorNotifier(object : MonitorNotifier {
// ビーコン信号の領域内へ侵入した際に呼ばれる
override fun didEnterRegion(region: Region) {
Log.d(TAG, "ビーコンが近くにいる!ビーコンが発信するデータの受信を開始する")
}

// ビーコン信号の領域外へ退出した際に呼ばれる
override fun didExitRegion(region: Region) {
Log.d(TAG, "ビーコンが近くにいなくなった。。ビーコンが発信するデータの受信を停止する")
}

// 領域への侵入/退出のステータスが変化した際に呼ばれる
// 一見didEnterRegionやdidExitRegionで十分に見えるが、実際にはこっちしか呼ばれないことがある
override fun didDetermineStateForRegion(state: Int, region: Region) {
Log.d(TAG, "ビーコンの侵入/退出ステータスが変化: $state")
try {
if (state == 1) {
mBeaconManager.startRangingBeaconsInRegion(region)
} else {
mBeaconManager.stopRangingBeaconsInRegion(region)
}
} catch (e: RemoteException) {
Log.d(TAG, "didDetermineStateForRegion e = " + e.message)
}
}
})

try {
mBeaconManager.startMonitoringBeaconsInRegion(mRegion)
Log.d(TAG, "ビーコンが近くにいるかどうかの監視を開始した。")
} catch (e: RemoteException) {
Log.e(TAG, "Exception", e)
e.printStackTrace()
}

mBeaconManager.addRangeNotifier(RangeNotifier { beacons, region ->
// ビーコンにデータが入っていない場合はbeaconsは[]となる
// また、設定から位置情報を許可した状態でない場合もbeaconsは[]となる
Log.d(TAG, "ビーコンデータ:${beacons}")
for (beacon in beacons) {
Log.d(TAG, "UUID:" + beacon.id1 + ", major:" + beacon.id2
+ ", minor:" + beacon.id3 + ", Distance:" + beacon.distance
+ ",RSSI" + beacon.rssi)
}
})
}
}

その他

検索したら一番上に出てくる qiita の記事とかだと removeAllMonitorNotifiers がなかったりして動かなかくて結構ハマった。。
以下の参考記事のおかげで解決した!
ありがてえ!

参考

[Android]AltBeacon を使って iBeacon を受信する

【Android】aabファイルをリバースエンジニアリングしてみた

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# abb→apk
brew install bundletool
bundletool build-apks --mode=universal --bundle=./app-release.aab --output=./app-release.apks
unzip app-release.apks

# apk→dex
unzip universal.apk

# dex→jar
# dexをjarに変換するためのツールをダウンロードする
# https://sourceforge.net/projects/dex2jar/files/
chmod 777 dex2jar-2.0/d2j-dex2jar.sh
./dex2jar/d2j-dex2jar.sh classes.dex

# jar→class
unzip classes-dex2jar.jar
# classファイルの中身が見れるツールをダウンロード
wget https://github.com/java-decompiler/jd-gui/releases/download/v1.6.6/jd-gui-1.6.6.jar
java -jar jd-gui-1.6.6.jar

# あとは見たいclassファイルを選択すればOK

こんな感じ

良いね!

参考
Android の APK を逆コンパイルする
Java Decompiler project の JD-GUI で JAR ファイルからソースコードを生成する

git-secrets削除後のエラーの解決策

エラーメッセージ

Git: git: ‘secrets’ is not a git command. See ‘git –help’.

原因

git-secrets がインストールされていないが、git-secrets を利用する設定だけ残ってしまっている

解決策

git-secrets を利用する設定を削除する

1
2
3
4
rm .git/hooks/commit-msg
rm .git/hooks/pre-commit
rm .git/hooks/prepare-commit-msg
git config --global --unset-all commit.template

参考

git secrets を削除する時の注意

【Android開発】[Java]許可ダイアログを出すシンプルな方法

Androidで許可ダイアログを出す方法です。

利用ライブラリ
https://github.com/permissions-dispatcher/PermissionsDispatcher

1. AndroidManifestの設定

AndroidManifest.xml

1
2
<!-- この行を追加する -->
<uses-permission android:name="android.permission.CAMERA" />

2. 許可が必要な機能を持っているクラスにアノテーションを付与する

1
2
3
4
5
6
@RuntimePermissions // この行を追加する
public class HogeFragment {

// いろいろな処理

}

3. 許可が必要な機能を実行する関数を修正する

関数の後ろにWithPermissionCheckを追加するだけ

1
2
3
4
5
6
7
8
9
@RuntimePermissions
public class HogeFragment {

public void onClick(final View v) {
// needPermission()
needPermissionWithPermissionCheck(this)
}

}

4. 許可が必要な関数にアノテーションを付与する

1
2
3
4
5
6
7
8
9
@RuntimePermissions
public class HogeFragment {

@NeedsPermission(Manifest.permission.CAMERA) // この行を追加する
public void piyo() {
// 許可が必要な処理
}

}

5. 許可の結果を受け取る関数を作成する

1
2
3
4
5
6
7
8
9
10
11
12
@RuntimePermissions
public class HogeFragment {

// 以下の関数を追加する
@SuppressLint("NeedOnRequestPermissionsResult") // この行はライブラリのバージョンが古い時に出るワーニング対策
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (PermissionUtils.verifyPermissions(grantResults)) {
piyo();
}
}
}

以上です

参考
PermissionsDispatcherによる権限管理

フォームボタンを連打したら多重送信されてしまうバグを防ぐ方法

フォームボタンを連打したら多重送信されてしまうバグを防ぐ方法です。

1
2
3
$('form').submit(function() {
$('input[name="submit"]').prop('disabled', true);
})

iPad向けCSS対応

端末サイズによって font-size を変えたい時、rem を使うと思いますが、意外と rem は万能ではなく、iPad だとかなり小さく見えます。

なので以下のように端末サイズによって分岐して指定した方が良いです。

1
2
3
4
5
6
7
8
9
10
11
12
13
/* for iPad */
@media screen and (min-width: 768px) {
html {
font-size: 1.2rem;
}
}

/* for iPhone */
@media screen and (max-width: 767px) {
html {
font-size: 0.7rem;
}
}

【Vue.js】モーダルコンポーネントのサンプル

Vue.js のモーダルコンポーネントのサンプルです。

モーダルコンポーネント

MyModal.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<template>
<transition name="b2t">
<div class="modal-wrapper" @click.self.stop="$emit('hide')" v-if="isOpened">
<div id="modal" class="modal-container">
<div class="hide" @click="$emit('hide')">
<i class="far fa-times"></i>
</div>
</div>
</div>
</transition>
</template>

<script>
export default {
props: {
isOpened: false,
},
}
</script>

<style scoped="" lang="scss">
.modal {
&-wrapper {
position: fixed;
top: 0;
bottom: 0;
right: 0;
left: 0;
background: rgba(0, 0, 0, 0.6);
z-index: 10000;
}
&-container {
position: fixed;
top: 20vh;
left: 10vw;
width: 80vw;
background-color: white;
border-radius: 15px 15px 0px 0px;
padding: 15px;
padding-top: 5vh;
overflow: auto;
}
}

.hide {
position: absolute;
top: 0;
right: 2vw;
font-size: 2.5rem;
}

.b2t-enter-active,
.b2t-leave-active {
transition: opacity 300ms;
}
.b2t-enter-active > .modal-container,
.b2t-leave-active > .modal-container {
transition: all 300ms;
}
.b2t-enter > .modal-container {
transform: translateX(100vw);
}
.b2t-leave-to > .modal-container {
transform: translateX(100vw);
}
</style>

モーダルコンポーネントを呼び出すコンポーネント

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
<MyModal
:isOpened="isOpenModal"
@hide="isOpenModal = false"
/>
</template>

<script>
import MyModal from '@/components/MyModal.vue';

export default {
components: {
MyModal,
},
data() {
return {
isOpenModal: false,
};
},
}
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×