我为我的大学做了一个Android项目,我们希望它的外观会根据用户的位置而改变。
例如,如果用户位于海域附近,则以海盗为主题,有木制按钮,海洋背景等。 如果用户在山区附近,那可能是以维京为主题。你明白了。
现在我的问题是:实现这样的事情的最佳方式是什么?
提前感谢您的答案:)
答案 0 :(得分:0)
最好的方法是使用自定义活动主题。使用样式和(自定义)主题属性,您可以将所有可自定义样式放置在主题中。虽然它有点麻烦,但您可以在运行时更改主题。创建视图时应用主题,因此您必须确保重新创建活动。如果您手动处理该更改,则可以提供更好的体验。
您的布局通常如下所示:
典型布局示例
<LinearLayout
android:id="@+id/layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@drawable/pirate_background">
<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/pirate_greeting"
/>
<TextView
android:id="@+id/greeting"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingStart="@dimen/activity_horizontal_margin"
android:paddingEnd="@dimen/activity_horizontal_margin"
android:text="@string/pirate_greeting"
/>
</LinearLayout>
现在,如果名为pirate_ *的所有属性都应该是可更改的,那么您希望将它们放在主题中,以便更改主题会自动更改这些元素。最基本的方法是为要自定义的每个属性定义主题属性。这看起来像这样:
使用自定义主题属性的布局
<LinearLayout
android:id="@+id/layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="?attr/my_layout_background">
<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="?attr/my_image_src"
/>
<TextView
android:id="@+id/greeting"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingStart="@dimen/activity_horizontal_margin"
android:paddingEnd="@dimen/activity_horizontal_margin"
android:text="?attr/my_greeting_text"
/>
</LinearLayout>
那个问号?表示您指的是主题属性。您可以在资源文件中定义这些属性,通常命名为&#39; attrs.xml&#39;。
<强>值/ attrs.xml 强>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="my_layout_background" format="reference" />
<attr name="my_image_src" format="reference" />
<attr name="my_greeting_text" format="string" />
</resources>
这些属性仅在您的主题为其定义了值时才有效。所以无论你有什么主题,请确保它包含上述属性的定义。
<强>值/的themes.xml 强>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="MyAppTheme" parent="[your base theme]">
<item name="my_layout_background">@drawable/default_greeting</item>
<item name="my_image_src">@drawable/default_greeting</item>
<item name="my_greeting_text">@string/default_greeting</item>
</style>
<style name="MyAppTheme.Pirates">
<item name="my_layout_background">@drawable/pirate_background</item>
<item name="my_image_src">@drawable/pirate_greeting</item>
<item name="my_greeting_text">@string/pirate_greeting</item>
</style>
<style name="MyAppTheme.China">
<item name="my_layout_background">@drawable/china_background</item>
<item name="my_image_src">@drawable/nihao</item>
<item name="my_greeting_text">@string/nihao</item>
</style>
</resources>
为简洁起见,我将假设您知道如何设置主题(如果您不了解,请了解here)。提示:在Android Studio的布局编辑器中,您可以指定一个主题,允许您以不同的主题预览布局。稍微玩一下吧。
到目前为止,您已经完成了简单的部分。我们现在要在运行时更改配置,而这部分并不那么容易。但如果按照我的指示,它就不会太难。对于典型活动,在setContentView(...)
方法中创建(或夸大)视图。无论当时的主题是什么,都将决定观点的样子。因此,如果您想以编程方式更改主题,则应确保在调用setContentView(...)
之前执行此操作。
设置自定义主题的活动
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme(R.style.MyAppTheme_Pirates);
setContentView(R.layout.activity_main);
// ...
}
大多数情况下,您可能希望在创建活动时更改主题。您可以使用recreate()
方法,这将破坏当前活动并立即创建一个新活动。当然,如果您销毁活动,您将丢失它所拥有的任何数据(例如您要更改的主题)。有两种方法可以解决这个问题:
onSaveInstanceState()
,并将主题存储在那里。如果您这样做,则可以在创建新活动时恢复此信息。您选择哪种方式取决于您。哪个更合适取决于您选择如何构建代码。无论你选择什么,最后你的活动都会有这样的代码:
重新创建自己以应用新主题的活动
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme(ThemeStorage.getTheme());
setContentView(R.layout.activity_main);
}
public void applyNewTheme(@StyleRes int themeResId) {
ThemeStorage.setTheme(themeResId);
recreate();
}
现在您需要做的就是拥有一些代码来确定要应用的主题。这可以是侦听位置更新的后台服务。如果确定应用程序应更改为新主题,则只需在部分或全部正在运行的活动上调用applyNewTheme
即可。您可以使用ActivityLifecycleCallbacks
获取所有正在运行的活动的列表。您在Application类中注册这些回调。这里提供了一些简单的代码。
public class MyApplication extends Application {
private ActivityCollector mActivityCollector = new ActivityCollector();
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(mActivityCollector.getCallbacks());
}
public ActivityCollector getActivityCollector() {
return mActivityCollector;
}
}
/**
* Uses {@link android.app.Application.ActivityLifecycleCallbacks} to
* maintain a list of created activities.
*/
public class ActivityCollector {
private List<Activity> mCreatedActivities = new ArrayList<>();
private ActivityLifecycleCallbacks mLifecycleCallbacks = new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
mCreatedActivities.add(activity);
}
@Override
public void onActivityDestroyed(Activity activity) {
mCreatedActivities.remove(activity);
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
};
public List<Activity> getCreatedActivities() {
return mCreatedActivities;
}
public ActivityLifecycleCallbacks getCallbacks() {
return mLifecycleCallbacks;
}
}
因此,将ActivityCollector
传递给需要将主题应用于您的活动的代码,并且您已完成所有设置。