寒川アクアブログ

美容師しながらアプリ開発していて水草が趣味の私のブログです

shapeで定義した図形の色を動的に変える方法

xmlでshapeにより定義した図形に、動的にカラーフィルターをかける方法です

以下のようなshapeのリソースを、後述するリニアレイアウトのバックグラウンドに指定しているとします。

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <corners android:radius="5dp" />
    <stroke android:width="2dp"/>
</shape>


コードから、上のshapeの図形にカラーフィルターをかけていきます。

>|java|
LinearLayout linearLayout = (LinearLayout)dialog.findViewById(R.id.sample_linear_layout_id);

Drawable drawable = linearLayout.getBackground();

// Drawableを取得し


drawable.setColorFilter(Color.RED, PorterDuff.Mode.SRC_ATOP);

// カラーフィルターをかけます


linearLayout.setBackground(drawable);

// 再びセットし直します
|


drawable.setColorFilter(Color.RED, PorterDuff.Mode.SRC_ATOP);
引数が2つありますが、ひとつめに被せたい色、2つめに効果を指定します。
PorterDuff.Mode.SRC_ATOPを指定することで、不透明部分に色を乗せることができます。

独自のユーザーインターフェイスを作る際に重宝します!

Toolbarのメニューアイコンの色を変える方法

Toolbarの右側に並ぶメニューアイコンの色を変える方法です

アプリの外観を動的に変える機能を持ったアプリなどは、
メニューアイコンの色も変えると、統一感や、洗練された印象を与えることができます。

メニューアイコンは、以下の方法で、色や透明度を変えることができます。

Toolbar yourToolbar;    //    Toolbarの参照

Menu menu = yourToolbar.getMenu();
            
            //    Menuオブジェクトを取り出し、

        for (int i = 0;i < menu.size();i++) {

            MenuItem item = menu.getItem(i);

      //    さらに、各アイテムを取り出します

            Drawable drawable = item.getIcon();

      //    アイテムのDrawableを取り出し、

            drawable.setColorFilter(Color.Red, PorterDuff.Mode.SRC_ATOP);
            drawable.setAlpha(205);

      //    外観を調整

            item.setIcon(drawable);

      //    再びセットします

        }

getIconの際に、メニューアイテムにアイコンを設定していないとエラーになりますのでご注意ください。
nullかどうかのチェックをお願いします。

カラーフィルターをかけるメソッド、
setColorFilter(Color.Red, PorterDuff.Mode.SRC_ATOP)
例では赤を指定しています。
イメージビュー等のカラーフィルターと異なり、引数が2つあります。
1つめに色を指定し、2つめに
PorterDuff.Mode.SRC_ATOP
を指定することで、アイコンの不透明部分に色を乗せることができます。

setAlpha(205)
は、その名の通り透明度をセットします。
不透明の80%の透明度に指定しています。

フォントサイズを自動調整するテキストビュー

テキストビューに文字列を表示させる際、
文字列がテキストビューの縦・横幅にぴったり納まるように
フォントサイズを自動調整するテキストビューです。

こちらのサイトを参考にさせていただきました!
思った通りのレイアウトができ、本当に助かりました。ありがとうございます!
monakap.hatenablog.com

テキストビューを継承したクラスです
public class AutoResizeTextView extends TextView {

    private  final float MIN_TEXT_SIZE = 10f;

    public AutoResizeTextView(Context context) {
        super(context);
    }

    public AutoResizeTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }


    @Override
    protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
        super.onTextChanged(text, start, lengthBefore, lengthAfter);
        resize();
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        resize();
    }

    private void resize() {

        Paint paint = new Paint();

        int viewWidth = this.getWidth();
        int viewHeight = this.getHeight();

        //    適当に大きめの数値からスタート
        float textSize = 300f;

        paint.setTextSize(textSize);

        Paint.FontMetrics fm = paint.getFontMetrics();
        float textHeight = (float)(Math.abs(fm.top)) + (Math.abs(fm.descent));

        float textWidth = paint.measureText(this.getText().toString());
        while (viewWidth < textWidth | viewHeight < textHeight){
            if (MIN_TEXT_SIZE >= textSize){
                textSize = MIN_TEXT_SIZE;
                break;
            }

            textSize--;

            paint.setTextSize(textSize);

            fm = paint.getFontMetrics();
            textHeight = (float)(Math.abs(fm.top)) + (Math.abs(fm.descent));
            textWidth = paint.measureText(this.getText().toString());
        }
        setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
    }

}


前途のサイトに各処理の詳細が掲載されています。
非常にわかりやすいのでぜひご覧ください。


また、宣伝みたいになってしまいますが、
このテキストビューに、タップした時に色を暗くする機能
ボタンをタップした時に動的に色を変える方法 - kojiko-android’s blog
を持たせ、ボタンとして活用した電卓アプリを作りました。
全てのボタン、メインディスプレイ等を全部を、こちらのカスタムテキストビューを継承して使っています。
それぞれ、サイズ調整の際に、サイズを1づつデクリメントしているので、
やや重くなってしまいましたが、
端末の画面サイズが変わっても狙い通りのレイアウトになるので、
とても重宝しています。
よろしかったらお試しください!
play.google.com

今後の課題は、ボタンらしい影を追加すること。
大変そう・・・笑

計算結果に”E”が含まれる場合

doubleからStringに変換したときに、計算結果に”E”の文字が含まれて表示される場合の対処


一見、エラー(ERROR)の「E」では、と思うかもしれませんが、

これは桁数が大きくなっても正確な値を表すための表現で、

Eより左の数 × 10 の (Eより右の数 乗)を表しています。


とはいえ、一般のユーザーから見たら、意味のわからない結果が表示されているとクレームレビューになりかねません。

”E”を含まない表示方法に変換します
double sample_double = 123d;    //    任意の数字
DecimalFormat decimalFormat = new DecimalFormat("##########.##########");
 String str = decimalFormat.format( sample_double );

DecimalFormatクラスのコンストラクターのパラメーターに与えた形式にフォーマットされ、
”E”を含まない文字列を得ることができます。


電卓アプリなど、日常的な計算ではあまり目にしませんが、一般向けに開発するのであれば、
念のため出さない方が無難かもしれませんね。

ボタンをタップした時に動的に色を変える方法

たとえばカラーのカスタマイズが出来るアプリのように、
アプリ内から動的にボタンの色を変更した後、その色に基づいて
タップした時に色を変える方法です。

ボタンを”押した感”を演出します。

ColorTintListや、セレクター等を使わずに、
ビューの色を取得 → 明度に変更を加える
という処理を行っています。

private int temporaryColorInt;
private Button BUTTON_SAMPLE;

BUTTON_SAMPLE.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    switch (event.getAction()) {
                        case MotionEvent.ACTION_DOWN:

                            ColorDrawable colorDrawable = (ColorDrawable) v.getBackground();
                            temporaryColorInt = colorDrawable.getColor();
                            float[] hsv = new float[3];
                            Color.colorToHSV(colorDrawable.getColor(), hsv);
                            hsv[2] -= 0.2f;
                            v.setBackgroundColor(Color.HSVToColor(hsv));

                            break;
                        case MotionEvent.ACTION_UP:
                            v.setBackgroundColor(temporaryColorInt);
                            break;
                    }
                    return true;
                }
            });

temporaryColorIntは、カラーを一時的に格納するために定義した変数で、
ボタンを押す前のデフォルトの色を入れています。

バックグラウンドカラーを整数で取得

ColorDrawable colorDrawable = (ColorDrawable) v.getBackground();
int color = colorDrawable.getColor();

ボタンのBackgroundColorを取得します。
まわりくどいですが、このようにするしかなさそうです。

色相、彩度、明度、に分解後、変更を加え、適用する

float[] hsv = new float[3];
Color.colorToHSV(colorDrawable.getColor(), hsv);
hsv[2] -= 0.2f;
v.setBackgroundColor(Color.HSVToColor(hsv));

明るさだけを変更したいので、色の値をHSV表現に変換しています。
hsv[2] -= 0.2f;
明度を少しだけ暗くします。これはお好みです。
変更を加えたHSVをint値に戻し、ビューの背景色に再び設定します。

タップを離したときに、変数に入れておいたデフォルトの値をセットし、
押す前の状態に戻します。

FragmentDialogを使う

備忘録は、確認が完全でないため、不確かな情報が含まれる場合があります。

フラグメントダイアログを継承した、独自のダイアログの作成方法です

フラグメントダイアログは、newInstanceでインスタンス化する。
また、その際に任意のパラメーターを定義することができる。

静的メソッド、コンストラクターを定義
//    定数定義
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";


public static SampleDialog newInstance(int int1, int int2) {

        SampleDialog fragment = new SampleDialog();
        Bundle args = new Bundle();
        args.putInt(ARG_PARAM1, int1);
        args.putInt(ARG_PARAM2, int2);    //    同様にして増やすことが可能
        fragment.setArguments(args);

        return fragment;
    }

public SampleDialog() {
    //    空のコンストラクター
    }
ダイアログ側のオーバーライドメソッドを編集
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {

    Dialog dialog = new Dialog(getActivity());
    dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);    //    独自レイアウトにしたいので、タイトル非表示
    dialog.setContentView(R.layout.dialog_sample);    //    用意したレイアウトリソースxmlファイル

    if (getArguments() != null){
        int int1 = getArguments().getInt(ARG_PARAM1);
        int int2 = getArguments().getInt(ARG_PARAM2);            
    }

    return dialog;

    }

独自のダイアログをリターンするようにします。
インスタンス化の際に渡されたパラメーターを得ることができ、
ダイアログで扱うデータの初期化などを行えます。

呼び出すアクティビティ側のコード

public class MyActivity extends FragmentActivity{ ...

フラグメントダイアログを呼び出すときは、
アクティビティがフラグメントアクティビティを継承している必要があります。
(理由はわかりません)

SampleDialog dialog = SampleDialog.newInstance(123,456);
dialog.show(getSupportFragmentManager(), "Sample_dialog");

呼び出すときは簡単です。
show()の第2引数の文字列はタグで、任意の文字列でOKです。

NotificationCompat 内容を一部更新する

NotificationCompatを利用したノーティフィケーションの発行、部分的な更新の方法

通知が出るときのアニメーション無しに、通知の一部を更新する方法です。
また、スワイプや、”通知を全て消去”しても消えない通知を作る方法です。

(注) 端末を上から下にスワイプすると出てくる通知のリスト(あれの名前がわからない)上における、
通知の振舞いについてのエントリーです。


フィールドを用意する
private Notification notification;
private NotificationCompat.Builder builder;
private NotificationManagerCompat managerCompat;
private  int notifyID = 1;

あとでノーティフィケーションを使いまわしするような形でマネージャーのnotify(int,Notification)を実行するので、
フィールドを確保しておきます。
IDは、適当な値で大丈夫です。レイアウトxmlファイルのリソースIDなどを入れる作法もあるみたいですね。
同ID + 使いまわしNotificationで、通知の更新を行うことができます。


インスタンス化します
managerCompat = (NotificationManagerCompat.from(getApplicationContext()));
builder = new NotificationCompat.Builder(getApplicationContext());

インスタンス化をオンクリエイト内等で最初に行います。
notify()のたびに新しいインスタンスを作ると、2つめの通知が新たに出てしまいます。
更新のためには、同じインスタンスを使う必要があります。



使いまわして更新!!
private void sample_update(){
    builder.setContentIntent(sample_PendingIntent)    //    タップされた時に発行されるインテント
                .setSmallIcon(R.mipmap.icon_sample)    //    通知の左側のアイコン
                .setContentTitle(getString(R.string.text_sample))    //    通知のタイトル
                .setContentText("HELLO!")    //    通知のテキスト
                .setAutoCancel(true)    //    通知をタップしたときに、その通知を消すかどうか
                .setColor(Color.RED) ;   //    通知のアイコンを含むように円が描画される、その円の色(OS5.0以上)
                

    notification = builder.build();
    notification.flags = Notification.FLAG_NO_CLEAR;    //    通知をスワイプや、全消去しても消えない
    managerCompat.notify(notifyID, notification);  
}

ビルダーで各情報をセット、build()でひとまとまりの通知を製造、
マネージャーを介してその通知を飛ばす、という感じの流れです。

例では、builderのメソッド、set〇〇で、
各パラメーターを設定します。
一部だけ変えたい場合も、表示したいパラメーターはすべてセットする必要があります。

build()メソッドで、Notification型のインスタンスが戻り値として得られますので、
notify()メソッドの引数に与えます。
notify(notifyID,builder.build())でもいいですが、
消えないためのフラグを立てたので、いったん変数に入れています。