タブレット用プログラムの書き止め

android OS & iPadOS の記録。

【kotlin】ViewModel と LiveData の利用

2024-02-09 23:08:39 | Android studio 日記

ViewModel と LiveData を組み合わせて MainActivity で画像切り替えができるか試す。

ところがデベロッパーの説明の「 by viewModels() 」が利用できない。インポートもできない。
探しても情報が出てこない。

諦めかけた時に


サイト 【穀風】kokufu.blogspot.com
    ”by ViewModels() を使って ViewModel を取得する方法”


を見つけました。

ありがとうございました。
APIレベルの関係で少し調整しましたが期待通りの動作が行えました。

そのコードは以下の通りです。

【MainViewModel.kt】
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel

class MainViewModel: ViewModel() {

    val screenType: MutableLiveData<Boolean> by lazy {
        MutableLiveData<Boolean>()
    }

    override fun onCleared() {
        super.onCleared()
    }
}

【MainActivity.kt】
・・・
import androidx.activity.viewModels

class MainActivity : AppCompatActivity() {
    private val viewModel:MainViewModel by viewModels()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        if (savedInstanceState==null) { //初回
            viewModel.screenType.value = true //フラグをセット
            setImage() //画像Aをセット
        }

        val dispTypeObserver = Observer<Boolean> {type ->
            //省略。画像BをImageViewにセットする
        }
        //viewModel.screenTypeが更新されたら実行されるコードをセットする
        viewModel.screenType.observe(this, dispTypeObserver )

        //true から false に更新させる
        viewModel.screenType.value = false

    }
    fun setImage(){
        // 省略。Picturesディレクトリから画像ファイルを読み込みImageViewにセットしている
    }
}


穀風さんの設定をしないと
  by viewModels()
これがエラーでビルドできない。

エミュ実行で画像Bが表示された。
Observerを切れば画像Aで止まる。

フラグを更新させると画像を変化させられる事が実証できた。
デバイスが回転した時に条件によって画像を変化できる。

縦長なら一枚表示、横長なら左右並びで二枚表示という感じ。


取り合えず、一歩。

 


kotlinによる画像ビュワー構想

2024-02-09 12:56:12 | Android studio 日記

ViewModel(その他も?)が曲者でAPIレベル制限があるようで低いとデベロッパー説明が役に立たない。インポートができなくて使えない。

古いアンドロイド機種を使いたいので工夫する。
ついでに色々と試してみる。

今回は、アクティビティに画像を表示して、サムネイル表示、ディレクトリ移動等はフラグメントで処理する。

前は速度的なところを重要視したけど、機能的にまとめた構造体を意識する。
管理部分から処理、作業部分に要求を出す。結果を管理部分に戻す。
一般的な内容で作る。


ViewModelでは、画像のファイルパスをディレクトリでリスト管理をする。
表示用のターゲットファイルのパスを取得するメソッドを用意する。
LiveDataでターゲットディレクトリが更新されたらディレクトリ内のファイルパスを全て取得、名前順に並び替える。
また、その他のモードフラグなども管理する。

ViewModelから画像表示、メッセージ操作、プログレスサークル操作ができるか試す。
MainActivityにViewModelを配置し、activity_main.xmlにUIを設置、LiveDataの更新でUIを操作するメソッドを作る。
フラグメントで利用するのはファイルパスのリストだけ。


メモリリークが起こらないように作れれば良いかな。


前途多難

2024-02-09 01:05:13 | Android studio 日記

何となく脳内変換と検索で行ける。
TextViewをボタンの代わりにしてタッチイベント処理。
XMLは省略。テストだから。

【MainActivity.kt】
class MainActivity : AppCompatActivity() {

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

        val mTipsLayout = findViewById(R.id.tips_button)
        mTipsLayout.setOnTouchListener { v: View?, event: MotionEvent ->
            val action = event.actionMasked
            when (action) {
                MotionEvent.ACTION_DOWN -> {
                    if (savedInstanceState == null) {
                        supportFragmentManager.beginTransaction()
                            .replace(R.id.fragment_layout, TipsFragment.newInstance(" tips ",""))
                            .commitNow()
                    }
                    return@setOnTouchListener true
                }
                else -> return@setOnTouchListener true
            }
        }

        val mMainLayout = findViewById(R.id.start_button)
        mMainLayout.setOnTouchListener { v: View?, event: MotionEvent ->
            val action = event.actionMasked
            when (action) {
                MotionEvent.ACTION_DOWN -> {
                    if (savedInstanceState == null) {
                        supportFragmentManager.beginTransaction()
                            .replace(R.id.fragment_layout, MainFragment.newInstance(" Main "))
                            .commitNow()
                    }
                    return@setOnTouchListener true
                }
                else -> return@setOnTouchListener true
            }
        }
    }
}


MainFragmentとTipsFragmentは戻るとMainActivityに。
Fragmentを削除しているのに画面に文字が残る。
意味分からないのとMainFragment始動なので強制的にMainFragmentへ。

viewModelは別クラスのUIを参照させるとメモリリークするので工夫が必要。
色々機能を一つのクラスにまとめたり削除したり、拡張も考えつつ構造構築かな。

【TipsFragment.kt】

private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"

class TipsFragment : Fragment() {
    private var param1: String? = null
    private var param2: String? = null

    companion object {
        @JvmStatic
        fun newInstance(param1: String, param2: String) =
            TipsFragment().apply {
                arguments = Bundle().apply {
                    putString(ARG_PARAM1, param1)
                    putString(ARG_PARAM2, param2)
                }
            }
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let {
            param1 = it.getString(ARG_PARAM1)
            param2 = it.getString(ARG_PARAM2)
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_tips, container, false)
    }

    override fun onViewCreated(
        view: View,
        savedInstanceState: Bundle?
    ) {
        super.onViewCreated(view, savedInstanceState);

        val textView = view.findViewById(R.id.message_tips_fragment)
        textView.setText(param1)

        textView.setOnTouchListener { v: View?, event: MotionEvent ->
            val action = event.actionMasked
            when (action) {
                MotionEvent.ACTION_DOWN -> {
                    parentFragmentManager.beginTransaction()
                        .replace(R.id.fragment_layout, MainFragment.newInstance(" main "))
                        .commitNow()

                    return@setOnTouchListener true
                }
                else -> return@setOnTouchListener true
            }
        }
    }
}

【MainFragment.kt】
private const val ARG_KEY = "PATH"
class MainFragment : Fragment() {

    private var full_path:String = ""

    companion object {
        fun newInstance(path:String) =
            MainFragment().apply {
                arguments = Bundle().apply {
                    putString(ARG_KEY,path)
                }
            }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let {
            full_path = it.getString(ARG_KEY,"MainFragment")
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        return inflater.inflate(R.layout.fragment_main, container, false)
    }

    override fun onViewCreated(
        view: View,
        savedInstanceState: Bundle?
    ) {
        super.onViewCreated(view, savedInstanceState);

       // 省略。tipsとほぼ同じ。
    }
}

前日は問題なくビルドできてたのに
起動してビルドしたら警告...、バージョンがなんたら。

決めたAndroidOSでアプリ作成をしたいのに次の日にそのAPIじゃ無理。対象を上げろみたいな...。
昨日はビルドしてエミュ動いてたじゃん。

気まぐれキャラか!