Android切替テーマ(二)

6723 ワード

Android切替テーマ(二)
背景
Androidの切り替えテーマについて書いた文章--Androidの切り替えテーマと肌の切り替えの実現について、setTheme()を使ってテーマを切り替える方法を紹介しましたが、この関数を使うと現在のActivityを再起動しなければ有効にならないという欠点があります.では、setTheme()を使用して現在のActivityを再起動しないことを保証する方法はありますか?私はこれがあることを教えて、以下は私がこの方法を詳しく紹介します(githubの上の1つのオープンソースプロジェクトを参考にして紹介します!文末はこのプロジェクトの住所を提供します).
===
げんり
よく知られているように、setTheme()以降に現在のActivityを再起動する必要があるのは、現在のViewTreeを再レンダリングするためです.だから今私たちのやり方は私たちが自分で彼にレンダリングすればいいのではないでしょうか.それなら、現在のActivityを再起動する必要はありません!コードを見てみましょう.
===
コード#コード#
コードの実装の核心は、ユーザーがsetTheme()を呼び出した後、現在のThemeを取得し、その後、私たちが使用している属性を取得し、その後、対応するコントロールに設定することでokになります.
最初のステップ
まずインタフェースを定義します.
public interface ColorUiInterface {
    public View getView();
    public void setTheme(Resources.Theme themeId);
}

これにより、すべてのコントロールを書き換え、インタフェースを継承し、対応する関数を実現することができ、setTheme()以降、各コントロールにあるsetTheme()メソッドを直接呼び出すことができます.
ステップ2
カスタムビューを実現します
public class ColorTextView extends TextView implements ColorUiInterface {
    private int attr_drawable = -1;
    private int attr_textAppearance = -1;
    private int attr_textColor = -1;
    public ColorTextView(Context context) {
        super(context);
    }

    public ColorTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.attr_drawable = ViewAttributeUtil.getBackgroundAttibute(attrs);
        this.attr_textColor = ViewAttributeUtil.getTextColorAttribute(attrs);
    }

    public ColorTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.attr_drawable = ViewAttributeUtil.getBackgroundAttibute(attrs);
        this.attr_textColor = ViewAttributeUtil.getTextColorAttribute(attrs);
    }

    @Override
    public View getView() {
        return this;
    }

    @Override
    public void setTheme(Resources.Theme themeId) {
        if (attr_drawable != -1) {
            ViewAttributeUtil.applyBackgroundDrawable(this, themeId, attr_drawable);
        }
        if (attr_textColor != -1) {
            ViewAttributeUtil.applyTextColor(this, themeId, attr_textColor);
        }
    }
}

以上のコードから、私はまずいくつかのよく使われる肌を変える要素を手に入れました.例えば、背景色、フォントの色、他のものがあれば、これはユーザーによってカスタマイズされます.もちろん読者も他のカスタムviewを実現することができます.
ステップ3ViewAttributeUtilというクラスの具体的な実現を見てみましょう.
public class ViewAttributeUtil {

  public static int getAttributeValue(AttributeSet attr, int paramInt) {
    int value = -1;
    int count = attr.getAttributeCount();
    for(int i = 0; i 

このクラスは比較的簡単で、対応するthemeidに基づいて対応するthemeidの値を得ることです!さて、これまで、ActivityのsetTheme()メソッドを呼び出した後、対応するviewに再設定すればいいのではないでしょうか.
ステップ4
ダイレクトコード
public class ColorUiUtil {
/**
 *       
 *
 * @param rootView
 */
public static void changeTheme(View rootView, Resources.Theme theme) {
    if (rootView instanceof ColorUiInterface) {
        ((ColorUiInterface) rootView).setTheme(theme);
        if (rootView instanceof ViewGroup) {
            int count = ((ViewGroup) rootView).getChildCount();
            for (int i = 0; i < count; i++) {
                changeTheme(((ViewGroup) rootView).getChildAt(i), theme);
            }
        }
        if (rootView instanceof AbsListView) {
            try {
                Field localField = AbsListView.class.getDeclaredField("mRecycler");
                localField.setAccessible(true);
                Method localMethod = Class.forName("android.widget.AbsListView$RecycleBin").getDeclaredMethod("clear", new Class[0]);
                localMethod.setAccessible(true);
                localMethod.invoke(localField.get(rootView), new Object[0]);
            } catch (NoSuchFieldException e1) {
                e1.printStackTrace();
            } catch (ClassNotFoundException e2) {
                e2.printStackTrace();
            } catch (NoSuchMethodException e3) {
                e3.printStackTrace();
            } catch (IllegalAccessException e4) {
                e4.printStackTrace();
            } catch (InvocationTargetException e5) {
                e5.printStackTrace();
            }
        }
    } else {
        if (rootView instanceof ViewGroup) {
            int count = ((ViewGroup) rootView).getChildCount();
            for (int i = 0; i < count; i++) {
                changeTheme(((ViewGroup) rootView).getChildAt(i), theme);
            }
        }
        if (rootView instanceof AbsListView) {
            try {
                Field localField = AbsListView.class.getDeclaredField("mRecycler");
                localField.setAccessible(true);
                Method localMethod = Class.forName("android.widget.AbsListView$RecycleBin").getDeclaredMethod("clear", new Class[0]);
                localMethod.setAccessible(true);
                localMethod.invoke(localField.get(rootView), new Object[0]);
            } catch (NoSuchFieldException e1) {
                e1.printStackTrace();
            } catch (ClassNotFoundException e2) {
                e2.printStackTrace();
            } catch (NoSuchMethodException e3) {
                e3.printStackTrace();
            } catch (IllegalAccessException e4) {
                e4.printStackTrace();
            } catch (InvocationTargetException e5) {
                e5.printStackTrace();
            }
        }
    }
}
}

コードは簡単で、再帰を使って、現在のViewTreeから私たちが最初に定義したインタフェースを継承したものを見つけて、それからsetTheme()の方法を呼び出せばいいのです!!
最後の一歩
では、Activityでどのように呼び出すかを見てみましょう.
   setTheme(R.style.theme_1);
   ColorUiUtil.changeTheme(rootView, getTheme());   
    

簡単じゃないですか.
まとめ
この方法は私はとても良い感じがして、唯一の欠点を言います!再帰を使いました!ちょっと性能を消耗します!しかし、これは無視できると思います!!
***このオープンソースプロジェクトのアドレスを指定します.MultipleTheme***