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 파일 을 만 듭 니 다.그 중에서
<?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()
이 필요 없고 블랙 스크린 이 반 짝 이 는 현상 이 없다.총결산
이상 이 이 글 의 전체 내용 입 니 다.안 드 로 이 드 개발 자 여러분 께 도움 이 되 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Kotlin의 기초 - 2부지난 글에서는 Kotlin이 무엇인지, Kotlin의 특징, Kotlin에서 변수 및 데이터 유형을 선언하는 방법과 같은 Kotlin의 기본 개념에 대해 배웠습니다. 유형 변환은 데이터 변수의 한 유형을 다른 데이터...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.