goo blog サービス終了のお知らせ 

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

android OS & iPadOS の記録。

Fragment [基礎] MyMessageクラス。リスナー組み込み。

2022-10-08 01:07:10 | Android studio 日記

フラグメント内で使うメッセージボード。
メッセージボードのボタンをタップするとフラグメントに通知を送る。

Bundle args で授受。

public class MyMessage {

    public interface ClickListener { // リスナーのインターフェイスを作成
        void onClick(Bundle args); // バンドルで渡す
    }

    private ClickListener mClickListener;
    public void setClickListener(final ClickListener listener) {
        mClickListener = listener;
    }

    private View.OnClickListener mMessageClick = new View.OnClickListener() {
        @Override
        public void onClick(View v) { // メッセージボードのボタンがタップされたら通知する。
            offMessageBoard();

            Bundle args = new Bundle(); 
            args.putInt("ID", mMessageId);   // 呼び出し元のID
            args.putInt("param", mMessageParam);   // パラメータ数
            args.putInt("V_ID", v.getId());   // タップしたView の ID

            mMessageId = -1;

            mClickListener.onClick( args );
        }
    };

    private ConstraintLayout mLayout;

    private TextView mMessageTextView = null;
    private TextView mAcceptedTextView = null;
    private TextView mCancelTextView = null;
    private TextView mDoneTextView = null;

    private int mMessageId = 0;
    private int mMessageParam = 0;

    @SuppressLint({"NonConstantResourceId", "ResourceType"})
    public MyMessage(Activity activity) {
        mLayout = activity.findViewById(R.id.message_layout);
        mMessageTextView = ( TextView ) activity.findViewById(R.id.message_text);

        mAcceptedTextView = ( TextView ) activity.findViewById(R.id.key_accepted);
        mAcceptedTextView.setOnClickListener(mMessageClick);
        mCancelTextView = ( TextView ) activity.findViewById(R.id.key_cancel);
        mCancelTextView.setOnClickListener(mMessageClick);
        mDoneTextView = ( TextView ) activity.findViewById(R.id.key_done);
        mDoneTextView.setOnClickListener(mMessageClick);

    }

    @SuppressLint("SetTextI18n")
    public void setMessage(int id, String mes ) {
        mMessageId = id; // 呼び出し元のID
        mMessageParam = 0; // パラメータ数
        mMessageTextView.setText(mes);
        mAcceptedTextView.setVisibility( View.GONE );
        mCancelTextView.setVisibility( View.GONE );
        mDoneTextView.setText("DONE");
        mDoneTextView.setVisibility( View.VISIBLE );

        mLayout.setVisibility(View.VISIBLE);
    }

    public void setMessage( int id,  String mes, String param1 ) {
        mMessageId = id;
        mMessageParam = 1;
        mMessageTextView.setText(mes);
        mAcceptedTextView.setText(param1);
        mAcceptedTextView.setVisibility( View.VISIBLE );
        mCancelTextView.setVisibility( View.GONE );
        mDoneTextView.setVisibility( View.GONE );

        mLayout.setVisibility(View.VISIBLE);
    }

    public void setMessage( int id,  String mes, String param1, String param2 ) {
        mMessageId = id;
        mMessageParam = 2;
        mMessageTextView.setText(mes);
        mAcceptedTextView.setText(param1);
        mAcceptedTextView.setVisibility( View.VISIBLE );
        mCancelTextView.setText(param2);
        mCancelTextView.setVisibility( View.VISIBLE );
        mDoneTextView.setVisibility( View.GONE );

        mLayout.setVisibility(View.VISIBLE);
    }

    public void setMessage( int id,  String mes, String param1, String param2, String param3 ) {
        mMessageId = id;
        mMessageParam = 3;
        mMessageTextView.setText(mes);
        mAcceptedTextView.setText(param1);
        mAcceptedTextView.setVisibility( View.VISIBLE );
        mCancelTextView.setText(param2);
        mCancelTextView.setVisibility( View.VISIBLE );
        mDoneTextView.setText(param3);
        mDoneTextView.setVisibility( View.VISIBLE );

        mLayout.setVisibility(View.VISIBLE);
    }
    public boolean isMessageBoard() { return ( mLayout.getVisibility() == View.VISIBLE ); }
    private void offMessageBoard() { mLayout.setVisibility( View.GONE ); }

    public void finish() {
        mMessageTextView = null;
        mAcceptedTextView = null;
        mCancelTextView = null;
        mDoneTextView = null;
        mMessageClick = null;
        mLayout = null;
    }
}

public class MyFragment extends Fragment {

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

        mMessage = new MyMessage( requireActivity() );

        mMessage.setClickListener(new MyMessage.ClickListener() {
            @Override
            public void onClick(Bundle result) {//メッセージボードのボタンがタップされると呼ばれる

                if (result.getInt("ID") == 1) {// メッセージボードを開いた処理のID
                    edit(result.getInt("V_ID"));//ボタンのViewID
                }
            }
        });
        // 省略
    }

    private void edit(int id) {

        switch (id) {
            case R.id.key_done:
                break;
            case R.id.key_accepted:
                break;
            case R.id.key_cancel:
                break;
            default:
        }
    }
}

昔にコールバック記事を見たような覚えから簡単とたかを括った。
見つからない分からない…。

そして、下のサイトに辿り着きました。
何となく自分に合わせて組み込み。
動く…。

 

【nyan のアプリ開発】様 [Android] カスタム Listener を interface を使って実装してみる

 


Fragment [基礎] MyMessageクラス

2022-10-07 16:58:47 | Android studio 日記

アラートダイアログをカスタマイズするのが面倒なので…。
メッセージボードを各フラグメントに組み込めるようにまとめる。
ただ、気に掛けるルールが少し残った。残念。

ルール1、MyMessageクラスとmymessage.xmlレイアウト。そして、myfragment.xmlレイアウトにインクルード指定。
ルール2、メッセージが開いているとき他のタップ処理を自前でスルーさせる。

 

MyMessage.java と mymessage.xmlをペアで利用。
フラグメントのmyfragment.xmlにインクルード表記したらメッセージボードが利用できる。


【MyMessage.java】

public class MyMessage {
    private ConstraintLayout mLayout;
    private View.OnClickListener mMessageClick = null;

    private TextView mMessageTextView = null;
    private TextView mAcceptedTextView = null;
    private TextView mCancelTextView = null;
    private TextView mDoneTextView = null;

    private int mMessageId = 0;

    @SuppressLint({"NonConstantResourceId", "ResourceType"})
    public MyMessage(Activity activity) {
        mLayout = activity.findViewById(R.id.message_layout);
        mMessageTextView = ( TextView ) activity.findViewById(R.id.message_text);

        mMessageClick  = v -> {

            offMessageBoard();
            switch (v.getId()) {
                case R.id.key_done:
                    break;
                case R.id.key_accepted:
                    messageAccepted();
                    break;
                case R.id.key_cancel:
                    messageCancel();
                    break;
                default:

            }
        };

        mAcceptedTextView = ( TextView ) activity.findViewById(R.id.key_accepted);
        mAcceptedTextView.setOnClickListener(mMessageClick);
        mCancelTextView = ( TextView ) activity.findViewById(R.id.key_cancel);
        mCancelTextView.setOnClickListener(mMessageClick);
        mDoneTextView = ( TextView ) activity.findViewById(R.id.key_done);
        mDoneTextView.setOnClickListener(mMessageClick);
    }

    private void messageCancel() {
        // 処理
        mMessageId = 0;
    }

    private void messageAccepted() {
        // mMessageIdの値で分岐処理
        mMessageId = 0;
    }

    public void setMessage( String mes ) {
        mMessageId = 0;
        mMessageTextView.setText(mes);
        mAcceptedTextView.setVisibility( View.GONE );
        mCancelTextView.setVisibility( View.GONE );
        mDoneTextView.setVisibility( View.VISIBLE );

        mLayout.setVisibility(View.VISIBLE);
    }

    public void setMessage( String mes, String param1, String param2 ) {
        mMessageId = 1;
        mMessageTextView.setText(mes);
        mAcceptedTextView.setText(param1);
        mCancelTextView.setText(param2);

        mAcceptedTextView.setVisibility( View.VISIBLE );
        mCancelTextView.setVisibility( View.VISIBLE );
        mDoneTextView.setVisibility( View.GONE );

        mLayout.setVisibility(View.VISIBLE);
    }

    public boolean isMessageBoard() { return ( mLayout.getVisibility() == View.VISIBLE ); }
    private void offMessageBoard() { mLayout.setVisibility( View.GONE ); }

    public void finish() {

        mMessageTextView = null;
        mAcceptedTextView = null;
        mCancelTextView = null;
        mDoneTextView = null;
        mMessageClick = null;
        mLayout = null;
    }
}

 

あ、コールバックさせてない。


MyMessageクラスの使い方

2022-10-07 16:52:48 | Android studio 日記

mMessage.isMessageBoard() 開いていると true が戻るので他はタップをスルーさせる。


【MyFragment.java】

public class MyFragment extends Fragment {

    private View.OnClickListener mClick = null;
    private View.OnLongClickListener mLongClick = null;
    private View.OnClickListener mReturnClick = null;

    private MyMessage mMessage;
    // 省略

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

        // 省略

        mMessage = new MyMessage( requireActivity() );


        mClick = v -> {
            if ( mMessage.isMessageBoard() ) return;

            // 処理
        };
        mLongClick = v -> {
            if ( mMessage.isMessageBoard() ) return;

            // 処理
        };

        mReturnClick = v -> {
            if ( mMessage.isMessageBoard() ) return;

            // 処理
        };
        //省略

    }

    // 省略

    @Override
    public void onDestroy() {
        super.onDestroy();

        mMessage.finish();
        mMessage = null;

        mClick = null;
        mLongClick = null;
        mReturnClick = null;
    }
}

 

【myfragment.xml】 【activity_main.xml】複数のFragment で利用表示させるため。

一番下に一文を追加書き込み。

< include layout="@layout/mymessage"/>

 

【mymessage.xml】レイアウトデザイン。

< 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:id="@+id/message_layout"
    android:background="@drawable/message_frame"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:visibility="gone"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent">

    < TextView
        android:id="@+id/message_text"
        android:layout_width="400dp"
        android:layout_height="wrap_content"
        android:maxLines="6"
        android:layout_marginLeft="50dp"
        android:layout_marginRight="50dp"
        android:layout_marginTop="50dp"
        android:textSize="@dimen/message_size"
        android:text="@string/message_area"
        android:textColor="@color/black"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    < LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:padding="@dimen/message_margin"
        app:layout_constraintStart_toStartOf="@id/message_text"
        app:layout_constraintEnd_toEndOf="@id/message_text"
        app:layout_constraintTop_toBottomOf="@id/message_text" >

        < TextView
            android:id="@+id/key_done"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingRight="@dimen/message_padding"
            android:paddingLeft="@dimen/message_padding"
            android:background="@drawable/message_button_frame"
            android:textSize="@dimen/message_size"
            android:textColor="@color/black"
            android:text="@string/done" />

        < TextView
            android:id="@+id/key_accepted"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="@dimen/message_margin"
            android:paddingRight="@dimen/message_padding"
            android:paddingLeft="@dimen/message_padding"
            android:background="@drawable/message_button_frame"
            android:textSize="@dimen/message_size"
            android:textColor="@color/black"
            android:text="@string/accepted"
            tools:ignore="RtlHardcoded" />

        < TextView
            android:id="@+id/key_cancel"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="@dimen/message_margin"
            android:background="@drawable/message_button_frame"
            android:textSize="@dimen/message_size"
            android:textColor="@color/black"
            android:paddingRight="@dimen/message_padding"
            android:paddingLeft="@dimen/message_padding"
            android:text="@string/cancel"
            tools:ignore="RtlHardcoded" />

     </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

 


【message_frame.xml】

< shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <!-- 背景色 -->
    < solid android:color="#E689FBF2" />

    <!-- 角丸 -->
    < corners android:radius="15dp" />

</shape>

【message_button_frame.xml】

< shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <!-- 背景色 -->
    < solid android:color="#36C0FF" />

    <!-- 角丸 -->
    < corners android:radius="15dp" />

</shape>


Fragment+ViewModel [基礎] 都合の良いように手を加える。

2022-10-01 17:06:41 | Android studio 日記

Contextをクラス内で使いたい。
しかし、クラスをインスタンス化する場所でContextを取得できない事がある。

ので、MainActivityでインスタンス化して参照をViewModelで管理。フラグメントで共有使用。
各フラグメントでその都度渡せば良いんだけど。問題が起きたらそうしよう。

 

public class MainActivity extends AppCompatActivity {

    private MainViewModel mViewModel = null;

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

        // 一部省略

        mViewModel = new ViewModelProvider(this ).get(MainViewModel.class);
        mViewModel.getMyMainModel().setValue( new MyMainModel(this) );

    }
    // 一部省略

}


【MainViewModel.java】インスタンスはMainActivityで作成。参照のみ管理させる。

public class MainViewModel extends ViewModel {

    private MutableLiveData< MyMainModel> mData =  new MutableLiveData< MyMainModel>(null);

    public MutableLiveData< MyMainModel> getMyMainModel() { return mData;}
    public MyMainModel getValueMyMainModel() { return mData.getValue();}
}


【MyMainModel.java】

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

    public int getScreenWidth() {
        WindowManager wManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);

        // 省略
        Point realSize = new Point();
        wManager.getDefaultDisplay().getSize(realSize);
        return realSize.x;
    }
    // 省略
}


【MainFragment.java】

public class MainFragment extends Fragment implements View.OnTouchListener {
    private MainViewModel mViewModel;

    private GestureDetectorCompat mDetector;
    class MyGestureListener extends SimpleOnGestureListener {
        //省略
    }
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        return mDetector.onTouchEvent(event);
    }
    public static MainFragment newInstance(String path) {
        MainFragment mainFragment = new MainFragment();
        Bundle args = new Bundle();
        args.putString("path", path);
        mainFragment.setArguments(args);
        return mainFragment;
    }
    @Override
    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        String path = "";
        Bundle args = getArguments();
        if(args != null )
            path = args.getString("path");

        mViewModel = new ViewModelProvider((ViewModelStoreOwner) requireContext()).get(MainViewModel.class);
        mDetector = new GestureDetectorCompat(requireContext(), new MyGestureListener());

 

        //省略
    }
    //省略
}


色々試してみて「できる」「できない」を確認する。
あ~面倒くさ。

 


Fragment [基礎] 画像表示。覚え書き。

2022-10-01 14:30:25 | Android studio 日記

・Fragment Mainにスクリーンに合わせて画像を拡大縮小させる。
・Subに本来の大きさの画像を表示させる。小さい場合は中央に大きい場合はスクロールさせる。

画像表示アプリで前回利用の画像を記憶して、アプリ再起動で再表示させる。

1:ActivityからMainへ画像のフルパスを渡す。
2:フルパスが存在しファイルか判断。表示不可ならPicturesディレクトリ表示モードへ移行(今回は作らない)。
3:画像をスクリーンサイズに加工(メモリ節約必須)、表示。
4:ダブルタップされたらフルパスをSubに渡してフラグメントを置き換える。
5:スクリーンサイズと画像サイズ。縦横は別々に考慮。
 スクリーンより小さい場合は中央に合わせる。大きい場合は左・上に合わせる。
6:Mainにはスクロール以外のジェスチャー組み込み。Subはスクロールとダブルタップのみ。

 

準備:RecyclerViewのアダプター用リストも考える。

・ターゲットフルパスから親ディレクトリを取得。
・fileListで画像ファイルをリストアップ。
・リストを昇順並べ替え。
・ターゲットディレクトリとファイル名リスト。または、ファイルフルパスリスト。

ターゲットディレクトリとファイル名リストはメモリ消費が少ない。
ファイル名フルパスのリストはメモリ消費が多い。が微妙に処理速度稼げる。
今となってはRecyclerViewのアダプターの引数に何でも指定できると分かったから自由度高い。

MyFileListクラス
データ
・targetDirectory
・fileName[]
メソッド
・fileName[]昇順並べ替え。
・fileName[]のgetName()、getAbsPath()。
・int findName()

ついでに

MyFileListForDirectoryクラス
データ
・targetDirectory
・dirName[]
・fileName[]
メソッド
・dirName[]、fileName[]昇順並べ替え。
・dirName[]、fileName[]のgetName()、getAbsPath()。
・dirName[]+fileName[] > name[]とtype[]に変換。
・name[]とtype[]のget()


Mainでファイル名とその他を用意したら画像を表示。
間にfileName[]からファイル名を取り出すクラス。

MyImageFileクラス
・fileName[]を引数にする
・targetNameからindexNoを求める
・next()prev()
・1枚or2枚表示
・ランド、ポート、1枚、2枚判断
・出力、int[2]か、string[2]

出力が1つか2つで画像合成判断。
1つの時にランドかポートで回転判断。

出力からスクリーンサイズに合わせてBitmap作成。
ImageSwitcherにセットして表示。アニメーションも組んどくか。


ダブルタップの位置から表示画像のパスを求める。
(1画像、2画像)
Subへフルパスを渡して置き換える。

 


シングルタップ、フリング、ロングタップ処理は後で追加。

 


フラグメントの操作内容でクラス機能をまとめる。
癒着しないように構造を分かりやすくする。系統化etc.