20201125のAndroidに関する記事は4件です。

ViewBindingに移行していきます

この記事の概要

Viewにアクセスする最新の方法をメモしています。これまではfindViewByIdKotlin Android ExtensionなどがViewにアクセスする方法としてありましたが、現在はViewBindingを使った方法が推奨されております。Android Developers | ViewBinding

作るもの

PeerLodgeのオリジナルアプリPeerRadioのAndroid版を作成しています。PeerRadioの開発ブログです。

これまでのViewへのアクセス方法

findViewbyIdを使ってViewにアクセスする方法は以下が例です。

 val titleInputText = findViewById<EditText>(R.id.title_input_text_view)
 postTitle = titleInputText.text.toString()

また、Kotlin Android ExtensionでViewにアクセスする方法は以下が例です。

btn_job_register.setOnClickListener {
        val intent = Intent(this, MainActivity::class.java)
        startActivity(intent)
}

上記は実際に現時点での開発中のAndroid版PeerRadioの中のコードです。他の部分にもまだまだfindviewByIdKotlin Android Extensionを使ってしまっているところがあるので、少しづつviewBindingに変えていく必要があります。頑張ります!

ViewBindingを使うための前準備

app/build.gradleにviewBindingの利用を記述します。

app/build.gradle
android {
    // 省略
    viewBinding {
        enabled = true
    }
}

変更できたらsyncしましょう。

ViewBindingでViewにアクセスする(Activity)

前準備ができたらActivityでは以下のようにアクセスします。
やるべきことは以下の3点です。

UserRegisterActivity.kt
class UserRegisterActivity : AppCompatActivity() {

    // TIPS: bindingクラスをlateinit varで宣言する
    private lateinit var binding: ActivityUserRegisterBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // TIPS: bindingの初期化とついでにsetContentViewも行います
        binding = ActivityUserRegisterBinding.inflate(layoutInflater)
            .apply { setContentView(this.root) }

        // TIPS: アクセスしたいViewのidはロワーキャメルケースにしたものに勝手に代わります
        binding.btnUserRegister.setOnClickListener {
            val intent = Intent(this, JobRegisterActivity::class.java )
            startActivity(intent)
        }
    }
}

ViewBindingでViewにアクセスする(Fragment)

また、Fragmentでは以下のようにViewBindingを設定します。

RecordFragment.kt
class RecordFragment: Fragment(),
      RecordAfterCustomViewDelegate,
      RecordBeforeCustomViewDelegate,
      RecordNowOnCustomViewDelegate {

    // TIPS: bindingの変数宣言を行う
    private var _binding: FragmentRecordBinding? = null
    private val binding
            get() = _binding!!

// TIPS: onCreateViewではbindingの初期化を行います
// TIPS: 逆に言えばbindingの初期化以外の処理は行わず、bindingの初期化以外の処理を記述したい場合はonViewCreatedにします。
override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
    // TIPS: rootViewは不要になります
        _binding = FragmentHomeBinding.inflate(inflater, container, false)
        return binding.root
    }

// TIPS: Viewに実際にアクセスしたり、binding以外の処理をここで行います。つまり、bindingの初期化以外の処理をここに記述します。
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // Viewの処理
        binding.hogehoge.text = "hogehoge"
      // bindingの初期化以外の処理
        callHomeViewModel()
}

// TIPS: fragmentでは閉じられる(destory)される時にbindingをクリアにする処理が必要になります
override fun onDestroyView() {
     super.onDestroyView()
     _binding = null
}

終わりに

何か間違っているよ、という部分等あればコメントよろしくおねがいします!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Android Studioの設定

初めに

開発をしていく上で役に立ちそうなAndroid Studioの設定をまとめます。

環境

Android Studio 4.1.1
PC Windows 10

設定内容

フォントサイズ・字体

File > Settings > Editor > Font
image.png

未使用のimportの削除

File > Settings > Editor > General > Auto Import
1. Optimize imports on the fly(for current project)にチェックを入れる
image.png

ショートカットの設定・確認

File > Settings > Keymap
image.png
キーボード ショートカット

Logcatの色の変更

File > Settings > Editor > Android Logcat
1. Inherit values from: Console→Standard outputのチェックを外す
2. Foregroundの色を変更
image.png

おまけ

設定内容をエクスポートしてほかの環境でインポートするには以下の方法で可能なので、
ファイル共有できる環境であれば一度設定してしまえば使いまわすことが可能です。
AndroidStudioの設定をエクスポートしてインポートする方法

最後に

他にやっておいた方がいい設定あれば、追加していきたいので、
コメントいただければと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

10分でHMS Audio Engineを利用してアプリにカラオケ機能を実装

カラオケ機能(エコー機能)のアプリを作りました。

早速HMS Audio Engineを使って、シンプルにカラオケ機能(エコー機能)をアプリで作りました。

◆ Object作成、Object初期化、Callbackの登録

mHwAudioKit = new HwAudioKit(this, callBack);
mHwAudioKit.initialize();
// Create KaraokeFeature:
mHwAudioKaraokeFeatureKit = mHwAudioKit.createFeature(HwAudioKit.FeatureType. HWAUDIO_FEATURE_KARAOKE);
// Create a callback task.
IAudioKitCallback callBack = new IAudioKitCallback() {
@Override    
public void onResult(int i) {
        if (i == 0) {
             // HwAudioKit initialization is successful. Its APIs can be used, such as
             mHwAudioKit .getSupportedFeatures(); //Return the list set.
             mHwAudioKit .isFeatureSupported(FeatureType type); // Return true or false: true indicates supported, while false indicates the product does not support the query function.
        }
        if (i == 2) {
            // The Engine service is not installed and the kit is not supported
        }
        if (i == 1000) {
            // HwAudioKit initialization is successful. Its APIs can be used, such as:
            // Query whether Karaoke is supported: true indicates supported, while false indicates not.
            int isSupport = mHwAudioKaraokeFeatureKit.isKaraokeFeatureSupport();
            // Turn on or off the headset monitoring function. The value 0 indicates success, 1806 indicates that the product does not support the karaoke function, and 1805 indicates that no headset is inserted.
            boolean enableSuccess = mHwAudioKaraokeFeatureKit.enableKaraokeFeature(enable);
            // Set the sound effect and volume. The value 0 indicates success, 1806 indicates that the product does not support the karaoke function, 1805 indicates that no headset is inserted, and 1807 indicates parameter error. 
           int success = mHwAudioKaraokeFeatureKit.setParameter(HwAudioKaraokeFeatureKit.ParameName.CMD_SET_AUDIO_EFFECT_MODE_BASE, value);
           // Obtain the latency information.The value -1 indicates obtaining failure
           int latency = mHwAudioKaraokeFeatureKit.getKaraokeLatency();
        }
    }
}

◆ Object廃棄

mHwAudioKaraokeFeatureKit.destroy();
mHwAudioKit.destroy();

完成

audio.PNG

START RECORDING、STOP RECORDING、PLAY RECORDINGで自分の音声を録音して再生。
TURN ON KARAOKEで自分の音声をリアルタイムで聞くことができます(エコー機能)。
さらに、KTVモードやTHEATERモードなども変更可能です。

参考

HMS Audio Engineについて
https://developer.huawei.com/consumer/en/doc/development/Media-Guides/audio-introduction

開発ガイド
https://developer.huawei.com/consumer/en/doc/development/Media-Guides/audio-guide

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[ReactNative] ScreenView+Webview refreshControlについての備忘録(Androidのバグ)

最近小さめのReactNativeのプロジェクトにアサインされたワタクシでございます。
全くまだ分かってないんですけど、UI部分の改修案件が来たので対応しています。
その際に直面したバグ対応の備忘録を残します。

どのようなバグか

iosでは起きず、Androidのみのバグとなります。

render() {
  <ScrollView
     refreshControl={
       <RefreshControl
         refreshing={ this.state.refreshing }
         style={{ backgroundColor: "#FFFFFF" }}
         onRefresh={() => { this.onRefresh() }}
       />
     }
     contentContainerStyle={{flex: 1}}
     style={{ backgroundColor: "#FFFFFF" }}
   >
            <WebView
              style={{ flex: 1 }}
              onMessage={this.onMessage}
              onNavigationStateChange={this.onNavigationStateChange}
              onLoadStart={this.onLoadStart}
              onLoadEnd={this.onLoadEnd}
              ref={webView => {this.webView = webView}}
              source={{ uri: this.state.url.toString() }}
            />
    </ScrollView>
}

このようなソースがあります。
webviewを表示しており、このwebviewはスクロールが可能になっています。
スクロールをする際にiosでは起きませんが、Androidではスクロールで下までいってしまったあとに上に戻ろうとすると必ずrefreshControlが走り、上にスクロールが出来なくなるバグが起きてました。
アプリの上部に来た時だけ更新してくれればいいのに、常に上部をひっぱると更新されてしまいます。

原因

webview側は問題なくスクロールします。スクロールされているのはScrollViewではなくて、webviewであることがわかり、(ScrollViewにonScrollつけてもイベントが走らなかったため)ScrollViewのscrollYが更新されず0のままでした。
そうすると恐らく、scrollYが0であるとしてAndroidでは常にrefreshControlが走ってしまうのではないか、というところまでたどり着きました。

開発環境

  • react-native: 0.61.5
  • react: 16.9.5
  • react-native-webview: 8.0.3

解決方法

const INJECTED_JS = `
  window.onscroll = function() {
    window.ReactNativeWebView.postMessage(
      JSON.stringify({
        scrollTop: document.documentElement.scrollTop || document.body.scrollTop
      }),
    )
`

export default class WebViewPage extends React.Component {
  state = {
    scrollViewHeight: 0,
    isPullToRefreshEnabled: false,
  }

  onMessage = () => {
    const { data } = e.nativeEvent

    try {
      const { scrollTop } = JSON.parse(data)
      this.setState({ isPullToRefreshEnabled: scrollTop === 0 })
    } catch (err) {
      // ...
    }
  }

  render () {
    const { scrollViewHeight, isPullToRefreshEnabled } = this.state

    return (
      <View style={{flex:1}}>
        <ScrollView
          refreshControl={
            <RefreshControl
              refreshing={ this.state.refreshing }
              enabled={ isPullToRefreshEnabled }
              style={{ backgroundColor: "#FFFFFF" }}
              onRefresh={() => { this.onRefresh() }}
            />
          }
          onLayout={e => this.setState({ scrollViewHeight: e.nativeEvent.layout.height })}
          contentContainerStyle={{flex: 1}}
          style={{ backgroundColor: "#FFFFFF" }}
        >
          <WebView
            style={{ flex: 1 }}
            onMessage={this.onMessage}
            onNavigationStateChange={this.onNavigationStateChange}
            onLoadStart={this.onLoadStart}
            onLoadEnd={this.onLoadEnd}
            ref={webView => {this.webView = webView}}
            source={{ uri: this.state.url.toString() }}
            injectedJavaScript={INJECTED_JS}
          />
        </ScrollView>
      </View>
    )
  }
}

解説

  1. ScrollViewでスクロールイベント取れないならwebviewで取るしかないよなということで injectedJavaScript を導入。injectedJavaScriptはwebview側にjavascriptのコードを渡す属性で、ここでwebviewのスクロール位置を取得して、window.ReactNativeWebView.postMessage でReactNative側にその情報を渡してやります。この時、webview側にもonMessageメソッドを定義して、webview側のスクロール位置情報を受け取ります
  2. enabledでいつrefreshControlが走るか定義しておく(onMessage)
  3. ScrollViewコンポーネントのonLayoutを使って、viewが開いたときの高さを取得して保持しておきます。(初回だけ取得用)
  4. WebviewコンポーネントにinjectedJavaScriptを突っ込みます。

以上です。これで画面の一番上にスクロールした時にだけ上部を引っ張ればrefreshControlが走る設計になったかと思います。

参考文献

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む