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

android OS & iPadOS の記録。

GestureDetector【基礎】onLongPress()の誤動作。

2022-11-27 02:08:04 | Android studio 日記

GestureDetector.SimpleOnGestureListener を使ってタップイベントを処理している。
シングルタップ、ダブルタップ、スクロール、フリング、そしてロングプレス。

テストで普通やらないだろうという速度で全てのイベントを発生させる。
フリングで画像切り替え中にロングプレスが反応する。

なので、


        @Override
        public void onLongPress(MotionEvent event) {
            // 省略

            if ( event.getDownTime() < 1000 ) {
                return;
            }

            // 省略
        }


押されていた時間が1秒未満だったらスルーさせた。
相変わらず過度なフリングでロングプレス誤動作が起こる。

都合の良いメソッドはないのか調べたら
 event.setAction()
キャンセルとかもセットできるのかな?
 event.setAction(MotionEvent.ACTION_CANCEL);
なんでもやってみる。


        @Override
        public void onLongPress(MotionEvent event) {
            // 省略

            if ( event.getDownTime() < 1000 ) {
                event.setAction(MotionEvent.ACTION_CANCEL);
                return;
            }

            // 省略
        }

ロングプレス誤動作は出なくなった。今のところ…。
onLongPress()は return ture; じゃないからイベント消費も無いのかな。
MotionEvent.ACTION_CANCEL で一旦リセットされた?

まぁ、色々都合があるのだろう。
しがらみを断ち切る event.setAction(MotionEvent.ACTION_CANCEL);覚えておこう。

検索かけてもこの使い方は出てこない。
良いのか悪いのか分からん。

 


  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

emulator【基礎】Win11 から Pictures フォルダーにアクセス。今頃…。

2022-11-26 19:34:52 | Android studio 日記

エミュレーターのandroid OS にはファイルマネージャーも無くファイルの移動などできない。
assets フォルダ経由で Pictures フォルダへ画像ファイルをコピーしていたが手間がかかる。

少し前に気が付いたエミュレーター稼働時にエミュレーターの Pictures ディレクトリにWinPCからアクセスする方法を書き留める。

エミュレーターが問題なく動作している状態で
・エディタ右端の[ Device File Explorer ]タブをクリック。
・[ mnt ]フォルダ >[ sdcard ]フォルダ >[ Pictures ] を開く。
・フォルダ作成は、[ Pictures ]を右クリック > new > directory。名前を付けてOK。
・WinPC のフォルダから画像ファイルをドラッグ&ドロップでコピー。複数もOK。


何か楽な方法はあると思っていたけど、エミュレーターが動いていないと[ Device File Explorer ]で内部が表示されない。
ふっ…。
エミュレーターはデバイスマネージャー毎にファイル内容が違うみたいだから動いてないと確定できないのかもしれない。


  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

ProgressBar【基礎】グルグル ⑵ Fragment部分

2022-11-23 13:01:06 | Android studio 日記

とりあえずざっくり。後で無駄を削ぐ。
グルグル表示中は専用フラグを立ててUI操作が発生してもスルーさせる。念のために。

ワーカースレッド処理用のデータクラス作成。
ワーカースレッド内でtry/catchを使い例外発生を認知。
ワーカースレッドは停止するけどメインは引き釣られず動く事もあるのでエラー確認はきっちりと。

グルグル表示遅延スイッチONの場所は、一本の流れで分岐が無ければ直前でなくても良い。

MyMainModel と MainViewModel 見間違いそうで失敗した。いつかクラス名の変更をしよう。
あと、RecyclerViewのアクセスはメインスレッドでないと例外でるので一時移動。最後にメインに戻って設定もアリだけど、できるかどうか試した。


public class DirectoryFragment extends Fragment {
    private MainViewModel mViewModel;
    private RecyclerView mRecyclerView = null;

    private ImageView mButton = null;
    private View.OnClickListener mButtonClick;

    private MyAdaptrData aData = null;

    // 省略

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

        // 省略

        mViewModel = new ViewModelProvider((ViewModelStoreOwner) requireContext()).get(MainViewModel.class);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_directory, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        // 省略

        mButtonClick = v -> {
            if (mViewModel.getValueAllWaitFlag()) return;// UI操作をスルーさせる

            // 省略
        };

        mButton = view.findViewById(R.id.button);
        mButton.setOnClickListener(mButtonClick);

        // 省略

        mRecyclerView = view.findViewById(R.id.directory_recyclerview);
        mRecyclerView.setHasFixedSize(true);
        mRecyclerView.setVerticalScrollBarEnabled(false);
        mRecyclerView.setLayoutManager(
                new LinearLayoutManager( requireContext(), RecyclerView.VERTICAL, false ));

        File file;
        // 省略

        aData = new MyAdaptrData(
                mViewModel,
                mRecyclerView,
                // 省略
        );
        new MyTask_onViewCreated(aData).execute();
    }


    private static class MyAdaptrData {
        MainViewModel mViewModel = null;
        RecyclerView mRecyclerView = null;
        // 省略

        public MyAdaptrData(
            MainViewModel viewModel,
            RecyclerView rv,
            // 省略
        ) {
            mViewModel = viewModel;
            mRecyclerView = rv;
            // 省略
        }
        public MainViewModel getMainViewModel() { return mViewModel;}
        public RecyclerView getRecyclerView() { return mRecyclerView;}
        // 省略

        public void finish() {
            mViewModel = null;
            mRecyclerView = null;
            // 省略
        }
    }

    private static class MyTask_onViewCreated {
        ExecutorService executorService;
        MyAdaptrData aData_OVC;
        MyMainModel mMainModel;
        MyFileList_Directory fList;
        int pos = 0;

        public MyTask_onViewCreated(MyAdaptrData data) { // インスタンス時にデータを渡す場合
            super();
            aData_OVC = data;
            executorService = Executors.newSingleThreadExecutor();
            mMainModel = aData_OVC.getMainViewModel().getValueMyMainModel();
        }

        class Run_onViewCreated implements Runnable {//ワーカースレッド
            @Override
            public void run() {
                try {
                    fList = new MyFileList_Directory(aData_OVC.getFile());

                    // 省略 RecyclerView().scrollToPosition()設定用の位置算出部分
                    aData_OVC.getRecyclerView().post(new Runnable() {//メインスレッドのRecyclerViewにアクセス
                        @Override
                        public void run() { // ここだけメインスレッド
                            aData_OVC.getMainViewModel().setFileList_directory(fList);
                            aData_OVC.getRecyclerView().setAdapter(
                                    new MyDirectoryListAdapter( aData_OVC.getMainViewModel() )
                            );
                            aData_OVC.getRecyclerView().scrollToPosition( pos );
                        }
                    });

                } catch (Exception e) { /* 例外発生は起こりやすいのでキャッチする */;}

               new Handler(Looper.getMainLooper())
                       .post(MyTask_onViewCreated.this::onPostExecute); // メインスレッドに戻る
            }
        }

        void execute() { // execute(List<> list)とか、ここでデータを渡す事もOK。参照か、複製かを考える事
            onPreExecute();
            executorService.submit(new Run_onViewCreated());//ワーカースレッドへ移行
        }

        void onPreExecute() {//メインスレッド
            aData_OVC.getMainViewModel().setAllWaitFlag(true);//UI操作待機フラグON
            mMainModel.setProgressBarSW_ON();// ぐるぐる表示 遅延スイッチ
        }

        void onPostExecute() {//メインスレッド
            aData_OVC.getDirectoryName().setText(
                      aData_OVC.getMainViewModel().getValueFileList_directory().getTargetDirAbsolutePath());
            aData_OVC.getMainViewModel().setAllWaitFlag(false);//UI操作待機フラグOFF
            mMainModel.hideProgressCircle();// ぐるぐる非表示
        }
    }
    // 省略

}

 


  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

ProgressBar【基礎】グルグル ⑴ ON-OFFスイッチ部分

2022-11-23 12:55:05 | Android studio 日記

レイアウトは割愛。コードの工夫のみ。
グルグル表示の説明は表示のみが多く実際の稼働は思うように動作しない。初心者にはハードル高い。

グルグルを表示(ProgressBar.VISIBLE)して
ワーカースレッドで(処理)する。
処理後にメインスレッドに戻って
グルグルを非表示(ProgressBar.INVISIBLE)にする。

こんな回答で詳細はこちらへどうぞ。とリンク…。リンクをクリックする気は無い私^^;

いつも通り試行錯誤。
要領は、代替えAsyncTaskに割り当てる。
単純には動かない。

よくあるトラブルを潰す。
データの受け渡しを明確に記述して思い通りに手順が進んでいるか確認をしていく。
コード内容(綺麗?簡潔?)は気にしない。
動くかどうか。後でまとめれば良い。

という事で処理に必要なデータをまとめてクラスを作る。
場合によって、参照型か複製型でワーカースレッド処理へ渡す。
参照型は処理中にデータ元で変数の値の更新が発生すると問題が起こる場合がある。
そういう時は複製で渡す。


class test {
    ArrayList< String> buff;
    int index;
    
    public test( ArrayList< String> buf, int no ) {
        例/buff = buf; // 参照
        例/buff = new ArrayList<>(buf); // 複製

        index = no; // 複製

    }
    // 省略

    public void finish() { // 複製時は 念のため後処理。参照時clear()実行すると元データ消去されるので注意
        buff.clear();
    }
}


グルグルを表示のON-OFFだと短時間の時にグルグルフラッシュで気になる。
遅延実行とフラグを組み合わせて設定時間より早く処理完了なら表示はしない工夫。

public class MyMainModel {
    public MyMainModel(Context context) {
        mContext = context;
    }

    private Context mContext = null;
    public Context getContext() { return mContext;}

    // 省略

    public static boolean ON = true;
    public static boolean OFF = false;
    private boolean mProgressBarSW = false;

    public boolean getProgressBarSW() {
        return mProgressBarSW;
    }
    public void setProgressBarSW(boolean sw) {
        mProgressBarSW = sw;
    }
    public void setProgressBarSW_ON() {//スイッチON
        mProgressBarSW = ON;
        showProgressCircle();
    }

    private ProgressBar mProgressBar = null;
    public void setProgressCircle( ProgressBar pb ) {// レイアウト情報をセット
        mProgressBar = pb;
    }

    public void showProgressCircle() { // 0.1秒後でも処理が終わらずフラグがONなら表示する
        new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
            @Override
            public void run() {
                if ( getProgressBarSW() == ON )
                    mProgressBar.setVisibility(ProgressBar.VISIBLE);
            }
        },100);
    }

    public void hideProgressCircle() { // ワーカースレッド処理後に呼ぶ。非表示、スイッチOFFを実行
        mProgressBar.setVisibility(ProgressBar.GONE);
        setProgressBarSW(OFF);
    }


    public void finish() {
        mContext = null;
        mProgressBar = null;
        // 省略
    }

}

グルグルスイッチ小細工完了。

 


  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

RecyclerView【と】AsyncTask

2022-11-16 17:02:43 | Android studio 日記

ViewModel に recyclerView_id を管理させ、RecyclerView アダプタがアタッチ、デタッチされたとき recyclerView_id を変化させる。
アダプターが変化しなければ、recyclerView_id も変化しない。
ワーカースレッドで recyclerView_id を比較して更新の可否を決める。

意外とシンプルに判断できる。


public class MainViewModel extends ViewModel {

    private MutableLiveData< RecyclerView> recyclerView = null;
    public MutableLiveData< RecyclerView> getRecyclerView() {
        if ( recyclerView == null ) {
            recyclerView = new MutableLiveData< RecyclerView>(null);
            recyclerView_id = 0;
        }
        return recyclerView;
    }
    public RecyclerView getValueRecyclerView() {
        return getRecyclerView().getValue();
    }
    private long recyclerView_id;
    public void setRecyclerView(RecyclerView rv) {
        getRecyclerView().setValue(rv);
        recyclerView_id++;
    }
    public void clearRecyclerView() {

        if ( getRecyclerView() != null && getRecyclerView().getValue() != null ) {
            getRecyclerView().getValue().setAdapter(null);

            getRecyclerView().setValue(null);
            recyclerView_id++;
        }
    }
    public long getValueRecyclerViewID() {
        return recyclerView_id;
    }

    //省略
}

 


public class MyAdapter extends RecyclerView.Adapter {

    private final MainViewModel mViewModel;
    private RecyclerView myRecyclerView = null;
    private View.OnClickListener myListener;

    //省略

    public MyAdapter(MainViewModel viewModel, View.OnClickListener listener) {
        mViewModel = viewModel;
        myListener = listener;
        //省略
    }

    @Override
    public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
        myRecyclerView = recyclerView;
        mViewModel.setRecyclerView(myRecyclerView);//****
    }

    @Override
    public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
        myRecyclerView = null;
        mViewModel.setRecyclerView(null);//****
        super.onDetachedFromRecyclerView(recyclerView);
    }

    //省略

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        if (getItemCount() <= 0) return;

        Bitmap bitmap = loadImage(myImageAbsPaths.get(position));
        if (bitmap != null)
            holder.imageView.setImageBitmap(bitmap);
        else {
            holder.imageView.setImageBitmap(
                      BitmapFactory.decodeResource(mMainModel.getContext().getResources(), R.drawable.img_file));
            MyAsyncTask2 mAsyncTask = new MyAsyncTask2(mViewModel, myRecyclerView, position);
            mAsyncTask.execute(myImageAbsPaths.get(position));
        }

        holder.textView.setText(String.valueOf(position + 1));
        initStartPositionOffset(holder.linearLayout, position);
        holder.linearLayout.setId(holder.getAdapterPosition());
        holder.linearLayout.setOnClickListener(myListener);
    }

    //省略
}

 


public class MyAsyncTask2 {

    private MainViewModel mViewModel;
    private RecyclerView mRecyclerView;
    private final int vID;

    private long mRecyclerView_id = 0;


    //省略

    public MyAsyncTask2(MainViewModel data, RecyclerView rv, int id ) {
        super();

        mViewModel = data;
        mRecyclerView = rv ;
        vID = id;
        mRecyclerView_id = data.getValueRecyclerViewID();

        //省略
    }

    private class TaskRunnable implements Runnable {

        private String targetPath = "";
        public TaskRunnable(String param) { targetPath = param; }

        @Override
        public void run() {
            
            //省略

            new Handler(Looper.getMainLooper())
                    .post(() -> onPostExecute(result));
        }
    }

    public void execute(String param) {
        mViewModel.getValueExecutorService().submit(new TaskRunnable(param));// ワーカースレッド実行
    }

    void onPostExecute(int result) {

        if ( mRecyclerView == null || mRecyclerView_id != mViewModel.getValueRecyclerViewID() ) {
            finish();
            return;
        }

        switch ( result ) {
            case 0:
                if ( vID < mRecyclerView.getAdapter().getItemCount() ) {
                        mRecyclerView.getAdapter().notifyItemChanged(vID);
                }
                break;
            
            //省略
        }
        finish();
    }
    void finish() {
        mRecyclerView = null;
        mViewModel = null;
    }

    //省略
}

 


  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする