Android 는 야간 모드 에 대한 깊 은 이 해 를 실현 합 니 다.

20328 단어 android야간패턴
이 글 은 일간/야간 모드 전환 을 실현 하 는 세 가지 방안 을 제시 하 였 으 며,세 가지 방안 을 종합 하면 문장의 편폭 이 너무 길 어 질 수 있 으 니 인내심 을 가지 고 읽 어 주 십시오.
    1.setTheme 의 방법 으로Activity 테 마 를 다시 설정 합 니 다.
    2.설정Android Support Library 중의UiMode 은 일간/야간 모드 전환 을 지원 합 니 다.
    3.자원 id 맵 을 통 해 사용자 정의ThemeChangeListener 인 터 페 이 스 를 되 돌려 낮/밤 모드 전환 을 처리 합 니 다.
1.setTheme 방법 사용
먼저setTheme 방법 을 사용 하여 일간/야간 모드 전환 을 실현 하 는 방안 을 살 펴 보 자.이러한 방안 의 방향 은 매우 간단 하 다.바로 사용자 가 야간 모드 를 선택 할 때 Activity 는 야간 모드 의 주제 로 설정 한 다음 에Activity 호출recreate()방법 으로 다시 만 들 면 된다.
그럼 시작 하 세 요.colors.xml 에서 두 개의 색 을 정의 하고 각각 낮 과 밤의 주제 색 을 표시 합 니 다.

<?xml version="1.0" encoding="utf-8"?>
<resources>
 <color name="colorPrimary">#3F51B5</color>
 <color name="colorPrimaryDark">#303F9F</color>
 <color name="colorAccent">#FF4081</color>

 <color name="nightColorPrimary">#3b3b3b</color>
 <color name="nightColorPrimaryDark">#383838</color>
 <color name="nightColorAccent">#a72b55</color>
</resources>
그 다음 에 styles.xml 에서 두 그룹의 테 마 를 정의 합 니 다.즉,일간 테마 와 야간 테마 입 니 다.

<resources>

 <!-- Base application theme. -->
 <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
  <!-- Customize your theme here. -->
  <item name="colorPrimary">@color/colorPrimary</item>
  <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
  <item name="colorAccent">@color/colorAccent</item>
  <item name="android:textColor">@android:color/black</item>
  <item name="mainBackground">@android:color/white</item>
 </style>

 <style name="NightAppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
  <!-- Customize your theme here. -->
  <item name="colorPrimary">@color/nightColorPrimary</item>
  <item name="colorPrimaryDark">@color/nightColorPrimaryDark</item>
  <item name="colorAccent">@color/nightColorAccent</item>
  <item name="android:textColor">@android:color/white</item>
  <item name="mainBackground">@color/nightColorPrimaryDark</item>
 </style>

</resources>
테마 에 있 는mainBackground 속성 은 배경 색 을 나타 내 는 사용자 정의 속성 입 니 다.

<?xml version="1.0" encoding="utf-8"?>
<resources>
 <attr name="mainBackground" format="color|reference"></attr>
</resources>
다음은 레이아웃 activitymain.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:background="?attr/mainBackground"
 android:paddingBottom="@dimen/activity_vertical_margin"
 android:paddingLeft="@dimen/activity_horizontal_margin"
 android:paddingRight="@dimen/activity_horizontal_margin"
 android:paddingTop="@dimen/activity_vertical_margin"
 tools:context="com.yuqirong.themedemo.MainActivity">

 <Button
  android:id="@+id/btn_theme"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:text="   /    " />

 <TextView
  android:id="@+id/tv"
  android:layout_below="@id/btn_theme"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:gravity="center_horizontal"
  android:text="  setTheme()   " />

</RelativeLayout>
android:background 속성 에서 사용 합 니 다."?attr/main Background 는RelativeLayout 의 배경 색 이 주제 에서 미리 정 의 된mainBackground 속성의 값 을 참조 한 다 는 것 을 나타 낸다.이렇게 해서 일간/야간 모드 전환 의 색 을 바 꾸 었 다.
마지막 으로MainActivity 의 코드 입 니 다.

public class MainActivity extends AppCompatActivity {

 //        
 private int theme = R.style.AppTheme;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
 //          
  if(savedInstanceState != null){
   theme = savedInstanceState.getInt("theme");
   setTheme(theme);
  }
  setContentView(R.layout.activity_main);

  Button btn_theme = (Button) findViewById(R.id.btn_theme);
  btn_theme.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
    theme = (theme == R.style.AppTheme) ? R.style.NightAppTheme : R.style.AppTheme;
    MainActivity.this.recreate();
   }
  });
 }

 @Override
 protected void onSaveInstanceState(Bundle outState) {
  super.onSaveInstanceState(outState);
  outState.putInt("theme", theme);
 }

 @Override
 protected void onRestoreInstanceState(Bundle savedInstanceState) {
  super.onRestoreInstanceState(savedInstanceState);
  theme = savedInstanceState.getInt("theme");
 }
}
MainActivity 에서 몇 가지 주의해 야 할 점 이 있 습 니 다.
     1.호출recreate() 방법 후 Activity 의 생명주기 호출onSaveInstanceState(Bundle outState)으로 관련 데 이 터 를 백업 하고,이후 에 도 호출onRestoreInstanceState(Bundle savedInstanceState)하여 관련 데 이 터 를 복원 하기 때문에 우 리 는theme 의 값 을 Activity 가 다시 만 든 후에 사용 할 수 있 도록 저장 합 니 다.
     2.우 리 는onCreate(Bundle savedInstanceState) 방법 에서theme 값 을 복원 한 후에setTheme() 방법 은 반드시setContentView() 방법 전에 호출 해 야 한다.그렇지 않 으 면 효 과 를 볼 수 없다.
     3.recreate()방법 은 API 11 에 추가 되 기 때문에 Android 2.X 에서 사용 하면 이상 을 던 집 니 다.
위의 코드 를 붙 인 후에 우 리 는 이 방안 이 실현 한 효과 도 를 살 펴 보 자.

2.Android Support Library 의 UiMode 사용 방법
UiMode 를 사용 하 는 방법 도 간단 합 니 다.colors.xml 를 낮/밤 두 가지 로 정의 해 야 합 니 다.이후 패턴 에 따라 다른 colors.xml 을 선택 합 니 다.Activity 호출recreate() 이후 일/야간 모드 전환 기능 이 구현 됐다.
이렇게 많아다음은 values/colors.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
 <color name="colorPrimary">#3F51B5</color>
 <color name="colorPrimaryDark">#303F9F</color>
 <color name="colorAccent">#FF4081</color>
 <color name="textColor">#FF000000</color>
 <color name="backgroundColor">#FFFFFF</color>
</resources>
values/colors.xml 을 제외 하고 야간 모드 의 색상 을 설정 하기 위해 values-night/colors.xml 파일 을 만 듭 니 다.그 중에서의 name 은 values/colors.xml 와 대응 해 야 합 니 다.

<?xml version="1.0" encoding="utf-8"?>
<resources>
 <color name="colorPrimary">#3b3b3b</color>
 <color name="colorPrimaryDark">#383838</color>
 <color name="colorAccent">#a72b55</color>
 <color name="textColor">#FFFFFF</color>
 <color name="backgroundColor">#3b3b3b</color>
</resources>
styles.xml 에서 colors.xml 에서 정 의 된 색상 을 참조 하 십시오:

<resources>

 <!-- Base application theme. -->
 <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
  <!-- Customize your theme here. -->
  <item name="colorPrimary">@color/colorPrimary</item>
  <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
  <item name="colorAccent">@color/colorAccent</item>
  <item name="android:textColor">@color/textColor</item>
  <item name="mainBackground">@color/backgroundColor</item>
 </style>

</resources>
activity_main.xml 레이아웃 의 내용 은 위setTheme() 방법 과 차이 가 많 지 않 습 니 다.여 기 는 붙 이지 않 습 니 다.이후 의 일 은 간단 해 졌 습 니 다.MyApplication 에서 기본 모드 를 선택 하 십시오.

public class MyApplication extends Application {

 @Override
 public void onCreate() {
  super.onCreate();
  //          
  AppCompatDelegate.setDefaultNightMode(
    AppCompatDelegate.MODE_NIGHT_NO);
 }

}
주의해 야 할 것 은 이곳 의 Mode 는 네 가지 유형 을 선택 할 수 있다 는 것 이다.
    1、MODE_NIGHT_NO:밝 은 색(light)테 마 를 사용 하고 야간 모드 를 사용 하지 않 습 니 다.
    2、MODE_NIGHT_YES:어두 운 색(dark)테 마 를 사용 하고 야간 모드 를 사용 합 니 다.
    3、MODE_NIGHT_AUTO:현재 시간 에 따라 밝 은 색(light)/어두 운 색(dark)테 마 를 자동 으로 전환 합 니 다.
    4、MODE_NIGHT_FOLLOW_SYSTEM(기본 설정):시스템 에 따라 MODE 로 설정 합 니 다.NIGHT_NO
사용자 가 버튼 을 누 르 면 날짜/밤 을 전환 할 때 해당 모드 를 다시 설정 합 니 다.

public class MainActivity extends AppCompatActivity {

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

  Button btn_theme = (Button) findViewById(R.id.btn_theme);
  btn_theme.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
    int currentNightMode = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
    getDelegate().setLocalNightMode(currentNightMode == Configuration.UI_MODE_NIGHT_NO
      ? AppCompatDelegate.MODE_NIGHT_YES : AppCompatDelegate.MODE_NIGHT_NO);
    //       recreate      
    recreate();
   }
  });
 }

}
UiMode 방안 이 실현 한 효과 도 를 살 펴 보 자.

앞의 두 가지 방법 으로 볼 때 배치 가 비교적 간단 하고 마지막 실현 효과 도 대체적으로 같다.그러나 단점 은 호출recreate()이 필요 하 다 는 것 이다.Activity 를 다시 만 들 려 면 일부 상태의 저장 이 필요 합 니 다.이것 은 약간의 난이 도 를 증가 시 켰 다.그래서 세 번 째 해결 방법 을 함께 살 펴 보 자.
자원 id 맵,리 셋 인터페이스
세 번 째 방법 은 설 정 된 주제 에 따라 자원 id 의 맵 을 동적 으로 가 져 온 다음 에 인터페이스 로 UI 에 관련 속성 값 을 설정 하 는 것 이다.우 리 는 여기 서 먼저 야간 모드 의 자원 이름 에 접 두 사 를 붙 여야 한다 고 규정 합 니 다.""night",예 를 들 어 일간 모드 의 배경 색 을 color 라 고 명명 합 니 다.background,이에 대응 하 는 야간 모드 의 배경 자원 을 color 라 고 명명 해 야 합 니 다.background_night 。자,다음은 우리 데모 에 필요 한 colors.xml 입 니 다.

<?xml version="1.0" encoding="utf-8"?>
<resources>
 
 <color name="colorPrimary">#3F51B5</color>
 <color name="colorPrimary_night">#3b3b3b</color>
 <color name="colorPrimaryDark">#303F9F</color>
 <color name="colorPrimaryDark_night">#383838</color>
 <color name="colorAccent">#FF4081</color>
 <color name="colorAccent_night">#a72b55</color>
 <color name="textColor">#FF000000</color>
 <color name="textColor_night">#FFFFFF</color>
 <color name="backgroundColor">#FFFFFF</color>
 <color name="backgroundColor_night">#3b3b3b</color>
 
</resources>
모든 color 가 대응 하 는 것 을 볼 수 있 습 니 다."night'와 일치 합 니 다.
이 걸 보면 왜 대응 하 는""를 설정 하 느 냐 는 질문 이 있 을 거 예요.night” ?도대체 어떤 방식 으로 일/야간 모드 를 설정 합 니까?다음은 ThemeManager 가 답 해 드 리 겠 습 니 다.

public class ThemeManager {

 //        
 private static ThemeMode mThemeMode = ThemeMode.DAY;
 //        
 private static List<OnThemeChangeListener> mThemeChangeListenerList = new LinkedList<>();
 //        ,key :     ,  <key:    , value:int >
 private static HashMap<String, HashMap<String, Integer>> sCachedNightResrouces = new HashMap<>();
 //          ,          :R.color.activity_bg,          :R.color.activity_bg_night
 private static final String RESOURCE_SUFFIX = "_night";

 /**
  *     ,           
  */
 public enum ThemeMode {
  DAY, NIGHT
 }

 /**
  *       
  *
  * @param themeMode
  */
 public static void setThemeMode(ThemeMode themeMode) {
  if (mThemeMode != themeMode) {
   mThemeMode = themeMode;
   if (mThemeChangeListenerList.size() > 0) {
    for (OnThemeChangeListener listener : mThemeChangeListenerList) {
     listener.onThemeChanged();
    }
   }
  }
 }

 /**
  *           resId       resId,  :        resId
  *
  * @param dayResId      resId
  * @return      resId,      ,   dayResId;        nightResId
  */
 public static int getCurrentThemeRes(Context context, int dayResId) {
  if (getThemeMode() == ThemeMode.DAY) {
   return dayResId;
  }
  //    
  String entryName = context.getResources().getResourceEntryName(dayResId);
  //     
  String typeName = context.getResources().getResourceTypeName(dayResId);
  HashMap<String, Integer> cachedRes = sCachedNightResrouces.get(typeName);
  //        ,        id
  if (cachedRes == null) {
   cachedRes = new HashMap<>();
  }
  Integer resId = cachedRes.get(entryName + RESOURCE_SUFFIX);
  if (resId != null && resId != 0) {
   return resId;
  } else {
   //            id     
   try {
    //      ,    ,      int 
    int nightResId = context.getResources().getIdentifier(entryName + RESOURCE_SUFFIX, typeName, context.getPackageName());
    //      
    cachedRes.put(entryName + RESOURCE_SUFFIX, nightResId);
    sCachedNightResrouces.put(typeName, cachedRes);
    return nightResId;
   } catch (Resources.NotFoundException e) {
    e.printStackTrace();
   }
  }
  return 0;
 }

 /**
  *   ThemeChangeListener
  *
  * @param listener
  */
 public static void registerThemeChangeListener(OnThemeChangeListener listener) {
  if (!mThemeChangeListenerList.contains(listener)) {
   mThemeChangeListenerList.add(listener);
  }
 }

 /**
  *    ThemeChangeListener
  *
  * @param listener
  */
 public static void unregisterThemeChangeListener(OnThemeChangeListener listener) {
  if (mThemeChangeListenerList.contains(listener)) {
   mThemeChangeListenerList.remove(listener);
  }
 }

 /**
  *       
  *
  * @return
  */
 public static ThemeMode getThemeMode() {
  return mThemeMode;
 }

 /**
  *          
  */
 public interface OnThemeChangeListener {
  /**
   *        
   */
  void onThemeChanged();
 }
}
위의 ThemeManager 코드 는 기본적으로 주석 이 있어 서 알 아 보 는 것 은 어렵 지 않 습 니 다.그 중에서 가장 핵심 적 인 것 은getCurrentThemeRes 방법 이다.여기 서getCurrentThemeRes 의 논 리 를 설명 하 다.매개 변수 에 있 는 dayResId 는 일간 모드 의 자원 id 입 니 다.현재 테마 가 일간 모드 라면 dayResId 로 돌아 갑 니 다.반대로 현재 테마 가 야간 모드 라면 데 이 리 스 Id 에 따라 자원 이름과 자원 유형 을 얻 습 니 다.예 를 들 어 현재 하나의 자원 이R.color.colorPrimary이면 자원 이름 은 colorPrimary 이 고 자원 유형 은 color 이다.그리고 자원 형식 과 자원 이름 에 따라 캐 시 를 가 져 옵 니 다.캐 시 가 없 으 면 자원 을 동적 으로 가 져 와 야 합 니 다.여기 사용법 은...

context.getResources().getIdentifier(String name, String defType, String defPackage)
name 매개 변 수 는 자원 이름 이지 만 주의해 야 할 것 은 자원 이름 에 접 두 사 를 붙 여야 한 다 는 것 입 니 다.""night",즉 위 에서 colors.xml 에서 정 의 된 이름 입 니 다.
defType 매개 변 수 는 자원 의 유형 입 니 다.예 를 들 어 color,drawable 등;
defPackage 는 자원 파일 의 패키지 이름,즉 현재 앱 의 패키지 이름 입 니 다.
위의 이 방법 이 있 으 면R.color.colorPrimary자원 을 통 해 해당 하 는R.color.colorPrimary_night 자원 을 찾 을 수 있다.마지막 으로 찾 은 야간 모드 자원 을 캐 시 에 추가 해 야 합 니 다.그러면 나중에 캐 시 에서 직접 읽 고 자원 id 를 동적 으로 찾 지 않 아 도 됩 니 다.
ThemeManager 에 남 은 코드 는 모두 비교적 간단 할 것 이 니 모두 가 알 아 볼 수 있 을 것 이 라 고 믿 습 니 다.
이제 MainActivity 의 코드 를 살 펴 보 겠 습 니 다.

public class MainActivity extends AppCompatActivity implements ThemeManager.OnThemeChangeListener {

 private TextView tv;
 private Button btn_theme;
 private RelativeLayout relativeLayout;
 private ActionBar supportActionBar;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  ThemeManager.registerThemeChangeListener(this);
  supportActionBar = getSupportActionBar();
  btn_theme = (Button) findViewById(R.id.btn_theme);
  relativeLayout = (RelativeLayout) findViewById(R.id.relativeLayout);
  tv = (TextView) findViewById(R.id.tv);
  btn_theme.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
    ThemeManager.setThemeMode(ThemeManager.getThemeMode() == ThemeManager.ThemeMode.DAY
      ? ThemeManager.ThemeMode.NIGHT : ThemeManager.ThemeMode.DAY);
   }
  });
 }

 public void initTheme() {
  tv.setTextColor(getResources().getColor(ThemeManager.getCurrentThemeRes(MainActivity.this, R.color.textColor)));
  btn_theme.setTextColor(getResources().getColor(ThemeManager.getCurrentThemeRes(MainActivity.this, R.color.textColor)));
  relativeLayout.setBackgroundColor(getResources().getColor(ThemeManager.getCurrentThemeRes(MainActivity.this, R.color.backgroundColor)));
  //        
  if(supportActionBar != null){
   supportActionBar.setBackgroundDrawable(new ColorDrawable(getResources().getColor(ThemeManager.getCurrentThemeRes(MainActivity.this, R.color.colorPrimary))));
  }
  //        
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
   Window window = getWindow();
   window.setStatusBarColor(getResources().getColor(ThemeManager.getCurrentThemeRes(MainActivity.this, R.color.colorPrimary)));
  }
 }

 @Override
 public void onThemeChanged() {
  initTheme();
 }

 @Override
 protected void onDestroy() {
  super.onDestroy();
  ThemeManager.unregisterThemeChangeListener(this);
 }

}
MainActivity 에서OnThemeChangeListener 인 터 페 이 스 를 실현 하면 주제 가 바 뀔 때 리 셋 방법 을 실행 할 수 있다.그리고 initTheme() 에서 UI 와 관련 된 색상 속성 값 을 다시 설정 합 니 다.그리고onDestroy() 에서 Theme Change Listener 를 제거 하 는 것 도 잊 지 마 세 요.
마지막 으로 세 번 째 방법의 효 과 를 살 펴 보 자.

앞의 두 가지 방법의 효과 와 별 차이 가 없다 고 말 하 는 사람 이 있 을 지 모 르 지만 자세히 보면 앞의 두 가지 방법 이 모드 를 전환 하 는 순간 짧 은 블랙 스크린 현상 이 존재 하 는 반면 세 번 째 방법 은 없다.앞의 두 가지 방법 을 모두 호출 해 야 하기 때문이다recreate() .세 번 째 방법 은 Activity 를 다시 만 들 필요 가 없고 리 셋 방법 으로 이 루어 집 니 다.
세 가지 방법 비교
여기까지 왔 으 니 틀 에 맞 게 정리 해 야 할 때 가 된 것 같다.그럼 위 에서 준 세 가지 방법 에 따라 간단하게 비교 해 보 세 요.setTheme 방법:여러 가지 테 마 를 설정 할 수 있어 서 쉽게 시작 할 수 있 습 니 다.일/야간 모드 외 에 도 다양한 주제 가 있 을 수 있다.그러나 호출recreate()이 필요 하 며 전환 순간 에 검 은 화면 이 반 짝 이 는 현상 이 나타 날 수 있 습 니 다.UiMode 방법:장점 은 바로 Android Support Library 에서 지원 되 고 간단 한 규범 이다.그러나 호출recreate() 이 필요 하고 블랙 스크린 이 반 짝 이 는 현상 이 존재 한다.
동적 으로 자원 id,리 셋 인터페이스 가 져 오기:이 방법 은 앞의 두 가지 방법 보다 복잡 하 며,리 셋 방법 에 서 는 각 UI 와 관련 된 속성 값 을 설정 해 야 합 니 다.그러나 호출recreate() 이 필요 없고 블랙 스크린 이 반 짝 이 는 현상 이 없다.
총결산
이상 이 이 글 의 전체 내용 입 니 다.안 드 로 이 드 개발 자 여러분 께 도움 이 되 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기