Androidアプリの「白黒」の1行のコードソリューションはありますか?

序文

清明節は中国の伝統的な祭りです。昨年のこの日、多くのウェブサイトやアプリが白黒になって深い哀悼の意を表した。

もちろん、ここではテクノロジーについてのみ説明します。これは、白黒のAndroidアプリの実現の探求です。

本日は、紅陽大直の実施計画をご紹介します。

元の住所:Hongyang

この方法をマスターし、墓掃除の日に白黒に変更し、建国記念日に大きな赤に変更します...プロダクトマネージャーとして引き継ぎ、党のリーダーシップに厳密に従い、中国の夢を実現します!

テキスト

同じ日に、Hongyangはwanandroid.comで白黒効果も開始しました。

あなたはたくさんのアプリを作るかもしれません、そしてウェブ側のサイト全体がこの効果を達成することができます。それは一文だけを必要とします:

html {filter:progid:DXImageTransform.Microsoft.BasicImage(grayscale=1);-webkit-filter: grayscale(100%);}

htmlにcssスタイルを追加するだけで、ページ全体にグレースケール効果を追加するものとして理解できます。

それだけです、それは本当に便利です。

アプリを振り返ると、誰もが開発するのが面倒だと感じていました。一般的な考え方は次のとおりです。

  • ピーリング

  • サーバーから送信された画像を表示するには、グレースケール処理が別途必要です。

ワークロードはまだ非常に大きいようです。

後で、このようにWeb側でページ全体にグレースケール効果を追加できるので、アプリでもそれを実行できるかどうかを考えていました。

では、アプリページにグレースケール効果を追加するにはどうすればよいでしょうか。

通常、私たちのアプリページは実際にはCanvasによって描画されますよね?

Canvasに対応するAPIもグレースケールをサポートしている必要があります

では、コントロールを描画するときに、描画する前にグレースケール効果を設定できますか?

不思議なことが発見されたようです。

灰色のImageViewを実行する試み

次に、まずImageViewを使用してグレースケール効果の実現可能性を検証します。

GrayImageViewというカスタムImageViewを作成します

レイアウトファイルは次のようになります。

<LinearLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".TestActivity">

    <ImageView
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:src="@mipmap/logo">

    </ImageView>

    <com.imooc.imooc_wechat_app.view.GrayImageView
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:src="@mipmap/logo" />

</LinearLayout>

非常に単純で、比較のためにImageViewを配置します。

GrayImageViewのコードを見てください:

public class GrayImageView extends AppCompatImageView {
    private Paint mPaint = new Paint();

    public GrayImageView(Context context, AttributeSet attrs) {
        super(context, attrs);

        ColorMatrix cm = new ColorMatrix();
        cm.setSaturation(0);
        mPaint.setColorFilter(new ColorMatrixColorFilter(cm));
    }

    @Override
    public void draw(Canvas canvas) {
        canvas.saveLayer(null, mPaint, Canvas.ALL_SAVE_FLAG);
        super.draw(canvas);
        canvas.restore();
    }

}

コードを分析する前に、レンダリングを見てみましょう。

完璧です。wanandroidアイコンを灰色にすることに成功しました。

コードを見てください。コードは非常に単純です。drawメソッドを上書きします。このメソッドでは、canvasに対して特別な処理を行います。

どんな特別扱い?実際には、グレースケール効果が設定されています。

アプリでは、4 * 5マトリックスであるカラー処理にカラーマトリックスを使用することがよくあります。原理は次のとおりです。

[ a, b, c, d, e,
    f, g, h, i, j,
    k, l, m, n, o,
    p, q, r, s, t ] 

特定の色[R、G、B、A]に適用すると、最終的な色の計算は次のようになります。

R’ = a*R + b*G + c*B + d*A + e;
G’ = f*R + g*G + h*B + i*A + j;
B’ = k*R + l*G + m*B + n*A + o;
A’ = p*R + q*G + r*B + s*A + t;

不快に見えますか?はい、私も不快に感じます。代数を見るのは面倒です。

誰もが不快なので、Androidはより親密で、ColorMartrixクラスを提供します。このクラスは多くのAPIを提供します。特別な操作がない限り、APIを直接呼び出して、必要な効果のほとんどを取得できます。自分で計算するためのマトリックス。

グレースケールのような効果の場合、彩度APIを操作できます。

setSaturation(float sat)

0を渡すだけです。ソースコードを見ることができます。最下層は特定の行列を渡して操作を実行します。

わかりました、わかりました、上記を忘れてください。キャンバスグレーで描画したものを作成できるAPIがあることを覚えておいてください。

すでにImageViewグレースケールを作成しましたが、TextViewを使用できますか?ボタンは大丈夫ですか?

推論を引き出してみてください

TextViewとButtonを試してみましょう。

コードはまったく同じです。実際、GrayTextViewなどの異なる実装クラスです。

public class GrayTextView extends AppCompatTextView {
    private Paint mPaint = new Paint();

    public GrayTextView(Context context, AttributeSet attrs) {
        super(context, attrs);

        ColorMatrix cm = new ColorMatrix();
        cm.setSaturation(0);
        mPaint.setColorFilter(new ColorMatrixColorFilter(cm));
    }

    @Override
    public void draw(Canvas canvas) {
        canvas.saveLayer(null, mPaint, Canvas.ALL_SAVE_FLAG);
        super.draw(canvas);
        canvas.restore();
    }
}

違いはありません。GrayButtonは投稿されません。レイアウトファイルを見てみましょう。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".TestActivity">

    <ImageView
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:src="@mipmap/logo">

    </ImageView>

    <com.imooc.imooc_wechat_app.view.GrayImageView
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:src="@mipmap/logo" />

    <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="鸿洋真帅"
    android:textColor="@android:color/holo_red_light"
    android:textSize="30dp" />


    <com.imooc.imooc_wechat_app.view.GrayTextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="鸿洋真帅"
        android:textColor="@android:color/holo_red_light"
        android:textSize="30dp" />


    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="鸿洋真帅"
        android:textColor="@android:color/holo_red_light"
        android:textSize="30dp" />


    <com.imooc.imooc_wechat_app.view.GrayButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="鸿洋真帅"
        android:textColor="@android:color/holo_red_light"
        android:textSize="30dp" />

</LinearLayout>

対応するレンダリング:

TextViewとButtonも赤のフォントを灰色に正常に変更したことがわかります。

この瞬間、いきなりそうなると思いますか?

実際、関連するさまざまなビューをこの種のカスタムビューに置き換え、appcompatを使用してスキンを変更するだけで済みます。サーバーが関与する必要はなく、クライアントがそれを行うことができます。

それは...ですか?すべてのビューをカスタムビューに置き換える必要がありますか?

これは高コストのように聞こえます。

考えてみてください、もっと簡単なものはありますか?

見上げる

今のレイアウトファイルはとてもシンプルですが、今すぐレイアウトファイルを見てみてください。質問をしたいと思います。

楽観的になります。

  1. 上記のxmlで、ImageViewの親ビューは誰ですか?

  2. TextViewの親ビューは誰ですか?

  3. ボタンの親ビューは誰ですか?

少し助けがありますか?

1つずつカスタマイズする必要がありますか?

親ビューはLinearLayoutであるため、GrayLinearLayoutを作成するだけで、内部ビューが灰色になります。結局、Canvasオブジェクトが渡されます。

やってみよう:

GrayLinearLayout:

public class GrayLinearLayout extends LinearLayout {
    private Paint mPaint = new Paint();

    public GrayLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);

        ColorMatrix cm = new ColorMatrix();
        cm.setSaturation(0);
        mPaint.setColorFilter(new ColorMatrixColorFilter(cm));
    }

    @Override
    public void draw(Canvas canvas) {
        canvas.saveLayer(null, mPaint, Canvas.ALL_SAVE_FLAG);
        super.draw(canvas);
        canvas.restore();
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        canvas.saveLayer(null, mPaint, Canvas.ALL_SAVE_FLAG);
        super.dispatchDraw(canvas);
        canvas.restore();
    }

}

コードは非常に単純ですが、詳細があることに注意してください。dispatchDrawも上書きすることに注意してください。なぜですか?自分のためだと思います。

xmlを置き換えます:

<?xml version="1.0" encoding="utf-8"?>
<com.imooc.imooc_wechat_app.view.GrayLinearLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".TestActivity">

    <ImageView
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:src="@mipmap/logo" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="鸿洋真帅"
        android:textColor="@android:color/holo_red_light"
        android:textSize="30dp" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="鸿洋真帅"
        android:textColor="@android:color/holo_red_light"
        android:textSize="30dp" />

</com.imooc.imooc_wechat_app.view.GrayLinearLayout>

青いロゴImageView、赤いフォントTextView、Buttonを配置し、効果を確認します。

完璧!

また少し厄介ではないですか!

設定したアクティビティのルートレイアウトを変更する限り!

アクティビティのルートレイアウトは、LinearLayout、FrameLayout、RelativeLayout、ConstraintLayoutなどです。

鶏肉を変えてください...これはいつ変更する必要がありますか、そして今との違いは何ですか。

何かアイデアはありますか?明確な見方はありませんか?

もう一度考えてみて。

セットしたアクティビティのルートレイアウトはどこに配置されますか?

android.id.content

このコンテンツビューにありますか?

このコンテンツビューは現在FrameLayoutです!

次に、このandroid.id.contentに対応するFrameLayoutを生成し、GrayFrameLayoutに置き換えるだけです。

それを変更する方法は?

appcompatとは何ですか?LayoutFactoryに移動しますか?

確かに可能ですが、LayoutFactoryを設定するには、appcompat関連のロジックも考慮する必要があります。

プロセスを変更する必要のないソリューションはありますか?

LayoutInflaterの詳細

本当にそうです。

AppCompatActivityはonCreateViewメソッドをオーバーライドできます。このメソッドは、LayoutFactoryがビューを構築しているときに実際にコールバックされ、通常はその内部mPrivateFactoryに対応します。

彼の優先順位は、Factory、Factory2、関連コードよりも低くなっています。

if (mFactory2 != null) {
    view = mFactory2.onCreateView(parent, name, context, attrs);
} else if (mFactory != null) {
    view = mFactory.onCreateView(name, context, attrs);
} else {
    view = null;
}

if (view == null && mPrivateFactory != null) {
    view = mPrivateFactory.onCreateView(parent, name, context, attrs);
}

if (view == null) {
    final Object lastContext = mConstructorArgs[0];
    mConstructorArgs[0] = context;
    try {
        if (-1 == name.indexOf('.')) {
            view = onCreateView(parent, name, attrs);
        } else {
            view = createView(name, null, attrs);
        }
    } finally {
        mConstructorArgs[0] = lastContext;
    }
}   

ただし、現在FrameLayoutの場合、appcompatには特別な処理はありません。つまり、onCreateViewコールバックでFrameLayoutオブジェクトを作成できます。

非常に簡単で、ActivityのonCreateViewメソッドを上書きするだけです。

public class TestActivity extends AppCompatActivity {

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

    @Override
    public View onCreateView(String name, Context context, AttributeSet attrs) {
        return super.onCreateView(name, context, attrs);
    }
}

このメソッドでは、コンテンツビューに対応するFrameLayoutをGrayFrameLayoutに置き換えます。

@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
    if("FrameLayout".equals(name)){
        int count = attrs.getAttributeCount();
        for (int i = 0; i < count; i++) {
            String attributeName = attrs.getAttributeName(i);
            String attributeValue = attrs.getAttributeValue(i);
            if (attributeName.equals("id")) {
                int id = Integer.parseInt(attributeValue.substring(1));
                String idVal = getResources().getResourceName(id);
                if ("android:id/content".equals(idVal)) {
                    GrayFrameLayout grayFrameLayout = new GrayFrameLayout(context, attrs);
                    return grayFrameLayout;
                }
            }
        }
    }
    return super.onCreateView(name, context, attrs);
}

コードは理解できるはずです。IDがandroid:id / contentであることがわかり、GrayFrameLayoutに置き換えました。

最後に、GrayFrameLayoutを見てください。

public class GrayFrameLayout extends FrameLayout {
    private Paint mPaint = new Paint();

    public GrayFrameLayout(Context context, AttributeSet attrs) {
        super(context, attrs);

        ColorMatrix cm = new ColorMatrix();
        cm.setSaturation(0);
        mPaint.setColorFilter(new ColorMatrixColorFilter(cm));
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        canvas.saveLayer(null, mPaint, Canvas.ALL_SAVE_FLAG);
        super.dispatchDraw(canvas);
        canvas.restore();
    }


    @Override
    public void draw(Canvas canvas) {
        canvas.saveLayer(null, mPaint, Canvas.ALL_SAVE_FLAG);
        super.draw(canvas);
        canvas.restore();
    }

}

さて、それを実行して効果を確認します。

効果は大丈夫です。

次に、onCreateViewのコードをBaseActivityに配置します。

何、BaseActivityはありませんか?

…自分で行ってください。

確認するアプリを探す

今といえば、そこからの活動はありません。

それを検証するために、より複雑なプロジェクトを見つけましょう。

githubにアクセスしてwanandroidJavaオープンソースプロジェクトを見つけました。

選択済み:https://github.com/jenly1314/WanAndroid

インポート後、BaseActivityに追加したコードを追加するだけです。

ランニング効果チャート:

はい、ウェブビューのテキストと画像は白黒です。

そのようなアプリは完全に白黒です。

待ってください。ステータスバーが変更されていないことがわかりました。ステータスバーにAPIがありますか?BaseActivityのコード行を呼び出して処理します。

ここをクリックして返信してください:「記事は本当によく書かれています」、あなたは白黒のapkを手に入れてそれを自分で体験することができます。

本当に大丈夫ですか?

実際、実行されなかったのは残念です。

それから私は自分自身にいくつかの質問を吹きました。

1.アクティビティウィンドウの背景が設定されている場合はどうなりますか?

コンテンツビューを扱っているので、ウィンドウの下にある必要があり、ウィンドウの背景を覆っていてはなりません。

何をすべきか?

慌てる必要はありません。

生成したGrayFrameLayoutで背景を設定することもできますか?

if ("android:id/content".equals(idVal)) {
    GrayFrameLayout grayFrameLayout = new GrayFrameLayout(context, attrs);
    grayFrameLayout.setBackgroundDrawable(getWindow().getDecorView().getBackground());
    return grayFrameLayout;
}

テーマにwindowBackgroundを設定する場合は、テーマからドローアブルを抽出する必要があります。参照コードは次のとおりです。

TypedValue a = new TypedValue();
getTheme().resolveAttribute(android.R.attr.windowBackground, a, true);
if (a.type >= TypedValue.TYPE_FIRST_COLOR_INT && a.type <= TypedValue.TYPE_LAST_COLOR_INT) {
    // windowBackground is a color
    int color = a.data;
} else {
    // windowBackground is not a color, probably a drawable
    Drawable c = getResources().getDrawable(a.resourceId);
}

2. Dialogはサポートしていますか?

このソリューションは、デフォルトでダイアログの白黒をすでにサポートしています。なぜですか?ダイアログ関連のソースコードを自分で見て、ダイアログ内のビュー構造がどのように見えるかを確認してください。

さらに、Webビュー内の画像テキストもサポートされています。

3. android.R.id.contentが将来FrameLayoutでない場合はどうなりますか?

それは確かに可能です。

おそらく、PhoneWindowの内部ビューを次のようにすることもできます。

decorView
    GrayFrameLayout
        android.R.id.content
            activity rootView

または、次のようになります。

decorView
    android.R.id.content
        GrayFrameLayout
            activity rootView

OK。

さて、私はそれを終えるつもりです、それは1行のコードで実現できますか?いいえ、30行のコードがあるようですが、それは十分に単純です。

コードは3分間書かれ、記事は午後中ずっと書かれていました。

やっと

この記事は、コードを投稿するのに約30行のコードしかかからず、それで終わりなので、白黒を実現する方法について単に話しているだけではありません。実際、この記事には1Wを超える文字が含まれているので、十分な知識を得ることができれば幸いです。

Androidの学習は長い道のりです。私たちが学ばなければならないのは、表面的なテクノロジーだけでなく、最下層も理解し、次の原則を理解することです。この方法でのみ、競争力を向上させることができます。

千マイルの旅は一歩から始まります。あなたと私が互いに励まし合うことを願っています。

今回編集したAndroid向けの最も重要で人気のある学習方向資料をGitHubに配置しました。また、さまざまな方向への自己学習プログラミングルート、インタビューの質問/対面、および一連の技術記事もあります。 。

リソースは継続的に更新されており、誰もが一緒に学び、議論することを歓迎します。

おすすめ

転載: blog.csdn.net/weixin_49559515/article/details/112853151