文 | Promise Sun
一宝冕、背景:
1. 2022上班第一天张遭,整理一下過去的工作,發(fā)現(xiàn)這方面的小知識點地梨,去年忘記記錄博客了菊卷,于是就有了這篇文章。分享給大家宝剖,希望對有需要的朋友有幫助洁闰。
2. 項目在開發(fā)調(diào)試過程中,后臺的接口域名一般會分生產(chǎn)環(huán)境万细、測試環(huán)境扑眉、自定義本地環(huán)境等等多個url地址環(huán)境,供開發(fā)人員使用,而且經(jīng)常會遇到頻繁切換url地址的情況腰素,就需要更改接口域名地址聘裁,然后AndroidStudio再重新編譯運行App,這樣就會非常麻煩弓千!
如果我們可以通過在app中直接切換環(huán)境衡便,不需要再重新運行打包,是不是就會方便很多呢洋访?
3. 在開發(fā)镣陕、打包app上線前,有時會需要手動改動很多配置變量姻政,比較麻煩呆抑,也很容易遺漏。
如果可以通過配置文件直接設(shè)置好汁展,就會避免很多問題理肺。
二、功能和方案:
1.實現(xiàn)主要功能:App一鍵切換url環(huán)境善镰、一鍵打包妹萨。
app應(yīng)用內(nèi)一鍵切換正式、測試環(huán)境炫欺,無需重新打包乎完。
包括一鍵打包,無需手動改動過多配置上線變量品洛。
2.實現(xiàn)方案:
主要通過配置本地文件的方式树姨,將所有涉及的相關(guān)變量寫在配置文件中,然后通過代碼實現(xiàn)相關(guān)功能桥状。
3.實現(xiàn)功能項目下載地址:
下載本文Demo請點擊此處
三帽揪、解決方案步驟一:基礎(chǔ)配置
1. 新建configs目錄
首先在app目錄下,新建configs文件夾辅斟,在configs下新建auto和release兩個文件目錄转晰。
auto:此文件夾中存放 開發(fā)版本使用的配置文件。
release:此文件夾中存放 線上版本使用的配置文件士飒。
(注:這里的文件夾名稱是可以自定義的查邢,只要開發(fā)的代碼中也做相應(yīng)更改就沒問題。)
下圖僅供參考:
2. 配置文件設(shè)置
1)auto目錄:
每個文件代表一種url環(huán)境的變量配置酵幕,環(huán)境可以相互切換扰藕。**
設(shè)置了5個配置文件(包括config.properties、configDev.properties芳撒、configPre.properties邓深、configCustom.properties未桥、configProduct.properties),大家可以根據(jù)自己的需要進行設(shè)置芥备,若不需要這么多開發(fā)環(huán)境冬耿,可以自行刪除或者增加相關(guān)配置文件。
(注:以下文件中的屬性配置僅供參考门躯,大家也可以根據(jù)實際需要進行設(shè)置。)
config.properties文件:
#app 運行環(huán)境設(shè)置
#環(huán)境名
name=develop
#項目環(huán)境 url酷师,此處只是示例讶凉,需要替換成你自己的域名地址
api.base.url=http://www.reibang.com/u/d346ccc6f7a4
#是否為線上
isProduct=false
#是否顯示Log日志
isShowLog=true
#是否顯示JSON格式
isJSON=true
configDev.properties文件:
# dev環(huán)境
#環(huán)境名
name=develop
#項目環(huán)境 url,此處只是示例山孔,需要替換成你自己的
api.base.url=https://blog.csdn.net/sun_promise/dev/
#是否為線上
isProduct=false
#是否顯示Log日志
isShowLog=true
#是否顯示JSON格式
isJSON=true
configPre.properties文件:
# pre環(huán)境
#環(huán)境名
name=pre
#項目環(huán)境 url懂讯,此處只是示例,需要替換成你自己的
api.base.url=https://blog.csdn.net/sun_promise/pre/
#是否為線上
isProduct=false
#是否顯示Log日志
isShowLog=true
#是否顯示JSON格式
isJSON=true
configCustom.properties文件:
# 自定義測試環(huán)境
#環(huán)境名
name=custom
#項目環(huán)境 url台颠,此處只是示例褐望,需要替換成你自己的域名地址
api.base.url=http://192.168.xx.xx:11008/
#是否為線上
isProduct=false
#是否顯示Log日志
isShowLog=true
#是否顯示JSON格式
isJSON=true
configProduct.properties文件:
# 正式環(huán)境
#環(huán)境名
name=product
#項目環(huán)境 url,此處只是示例串前,需要替換成你自己的
api.base.url=https://blog.csdn.net/sun_promise
#是否為線上
isProduct=true
#是否顯示Log日志
isShowLog=true
#是否顯示JSON格式
isJSON=true
2)release目錄:
只有一個文件config.properties瘫里,代表線上產(chǎn)品版本,不能切換環(huán)境荡碾,此文件的配置是給線上真正的用戶使用的谨读。
config.properties文件:
# 線上環(huán)境
#環(huán)境名
name=product
#項目環(huán)境 url,此處只是示例坛吁,需要替換成你自己的
api.base.url=https://blog.csdn.net/sun_promise
#若以下配置不使用劳殖,可以刪除不設(shè)置
#是否為線上
isProduct=true
#是否顯示Log日志
isShowLog=false
#是否顯示JSON格式
isJSON=false
3. 配置build.gradle(app下的Module)
apply plugin: 'com.android.application'
android {
compileSdk 31
defaultConfig {
applicationId "com.sun.urlenvconfig"
minSdk 21
targetSdk 31
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
// 多渠道打包,AS3.0之后:原因就是使用了productFlavors分包拨脉,
// 解決方法就是在build.gradle中的defaultConfig中添加 一個flavorDimensions "1"就可以了哆姻,后面的1一般是跟你的versionCode相同
// defaultConfig.versionCode
flavorDimensions "\"${defaultConfig.versionCode}\""
//記錄下利用buildConfigField為項目進行動態(tài)配置(對應(yīng)BuildConfig.class)
// eg: ----- debug:打印日志,在內(nèi)網(wǎng)測試.----- release:關(guān)閉日志玫膀,外網(wǎng)矛缨,簽名等
// 已經(jīng)通過配置文件設(shè)置了,此處可以不設(shè)置了
// buildConfigField("boolean", "IS_PRODUCT", "\"${IS_PRODUCT}\"")
// buildConfigField("boolean", "IS_JSON", "true")
}
buildTypes {
debug {
minifyEnabled false
zipAlignEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
// signingConfig signingConfigs.release
}
release {
minifyEnabled true
zipAlignEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
// signingConfig signingConfigs.release
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
sourceSets {
//開發(fā)版本使用的配置文件
auto {
assets.srcDirs = ['assets', 'configs/auto']
}
// 線上版本使用的配置文件
product {
assets.srcDirs = ['assets', 'configs/release']
}
}
//多渠道打包
productFlavors {
auto {
//可以設(shè)置app不同環(huán)境的名字 貨主測試版
manifestPlaceholders = [app_name: "環(huán)境配置測試版"]
}
// 線上產(chǎn)品版本
product {
manifestPlaceholders = [app_name: "環(huán)境配置正式版"]
}
}
// 打包時選擇帖旨,這些都是可以自己在config.properties文件中自己配置劳景,然后組合打包的。
// autoDebug 指定默認(rèn)測試環(huán)境碉就,有 log,可切換
// autoRelease 指定默認(rèn)測試環(huán)境盟广,無 log,可切換
// productDebug 環(huán)境,無 log,不可切換
// productRelease 環(huán)境瓮钥,無 log,不可切換
applicationVariants.all { variant ->
variant.outputs.all { output ->
def buildTypeName = variant.buildType.name
def versionName = defaultConfig.versionName
def versionCode = defaultConfig.versionCode
// 多渠道打包的時候筋量,后臺不支持中文
outputFileName = "envconfig-v${versionName}-${versionCode}-${buildTypeName}-${buildTime()}.apk"
}
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
/*butterknife*/
api 'com.jakewharton:butterknife:10.2.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.0'
//MMKV 組件
implementation 'com.tencent:mmkv-static:1.2.7'
}
//設(shè)置默認(rèn)環(huán)境:不寫參數(shù)或者環(huán)境名錯誤烹吵,則默認(rèn)develop環(huán)境
setDefaultEnv()
def setDefaultEnv() {
def envName = envConfig()
def envConfigDir = "${rootDir}/app/configs/auto/"
//def envConfigDir = "${rootDir}/app/configs/release/"
def renameFile = "config.properties"
println("打包接口環(huán)境:${envName}")
task configCopy(type: Copy) {
copy {
delete "${envConfigDir}/${renameFile}"
from(envConfigDir)
into(envConfigDir)
include(envName)
rename(envName, renameFile)
}
}
}
//這里可以更改AndroidStudio的默認(rèn)運行環(huán)境: 更改envName這里對應(yīng)的值即可。
String envConfig() {
def envName = "develop" //默認(rèn)運行環(huán)境設(shè)置:pre桨武、custom肋拔、product、develop
if (hasProperty("env")) {
envName = getPropmerty("env")
}
println("配置環(huán)境為:${envName}")
def envFileName = 'configDev'
if (envName == "develop") {
envFileName = 'configDev'
} else if (envName == "pre") {
envFileName = 'configPre'
} else if (envName == "custom") {
envFileName = 'configCustom'
} else if (envName == "product") {
envFileName = 'configProduct'
}
return envFileName + ".properties"
}
static def buildTime() {
def date = new Date()
def formattedDate = date.format('yyyyMMdd_HHmmss')
return formattedDate
}
4. 配置渠道包的app名
更改清單文件的設(shè)置android:label="${app_name}"呀酸,
<application
android:name=".MyApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="${app_name}"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.UrlEnvironmentConfig">
四凉蜂、解決方案步驟二:開發(fā)代碼完善功能
1. 涉及功能代碼目錄,僅供參考性誉。
2. 初始化環(huán)境配置
MyApplication類
/**
* @Author Promise Sun
*/
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
//SP框架要在PropertyUtils工具類之前進行初始化窿吩,如果你的項目中已有其他的SP工具類,可以直接使用
MMKV.initialize(this);
SpUtil.getInstance();
//Url相關(guān)
PropertyUtils.init(this);
//設(shè)置打印開關(guān)
// LogUtil.setIsLog(PropertyUtils.isShowLog());
}
}
3. 加載配置文件工具類 PropertyUtils
/**
* @Author Promise Sun
*/
public class PropertyUtils {
private static Properties mProps = new Properties();
private static boolean mHasLoadProps = false;
private static final Object mLock = new Object();
private static final String TAG = "PropertyUtils";
public PropertyUtils() { }
/**
* 在AppApplication中初始化
*/
public static void init(Context context) {
if (!mHasLoadProps) {
synchronized (mLock) {
if (!mHasLoadProps) {
try {
//獲取環(huán)境類型
ConfigManager.EnvironmentType environmentType = ConfigManager.getDefault().getAppEnv();
//Log.e("xyh", "init: " + environmentType.configType + ".properties");
InputStream is = context.getAssets().open(environmentType.configType + ".properties");
mProps.load(is);
mHasLoadProps = true;
Log.e(TAG, "load config.properties successfully!");
} catch (IOException var4) {
Log.e(TAG, "load config.properties error!", var4);
}
}
}
}
}
public static String getApiBaseUrl() {
if (mProps == null) {
throw new IllegalArgumentException("must call #UtilsManager.init(context) in application");
} else {
return mProps.getProperty(PropertyKey.BASE_URL, "");
}
}
public static boolean isProduct() {
return mProps.getProperty(PropertyKey.IS_PRODUCT, "false").equals("true");
}
public static boolean isJSON() {
return mProps.getProperty(PropertyKey.IS_JSON, "false").equals("true");
}
public static boolean isShowLog() {
return mProps.getProperty(PropertyKey.IS_SHOW_LOG, "false").equals("true");
}
public static String getEnvironmentName() {
return mProps.getProperty(PropertyKey.NAME_ENV, "");
}
public static ConfigManager.EnvironmentType environmentMap() {
String envName = getEnvironmentName();
switch (envName) {
case "config":
return ConfigManager.EnvironmentType.DEV;
case "pre":
return ConfigManager.EnvironmentType.PRE;
case "custom":
return ConfigManager.EnvironmentType.CUSTOM;
case "product":
return ConfigManager.EnvironmentType.PRODUCT;
default:
return ConfigManager.EnvironmentType.DEFAULT;
}
}
}
4. 設(shè)置環(huán)境配置管理類ConfigManager
/**
* @Author Promise Sun
* 環(huán)境配置管理類
*/
public class ConfigManager {
//當(dāng)前環(huán)境
private EnvironmentType mCurrentEnvType;
private static final String APP_ENV = "appEnv";
private ConfigManager() {
}
public static ConfigManager getDefault() {
return HOLDER.INSTANCE;
}
private static class HOLDER {
static ConfigManager INSTANCE = new ConfigManager();
}
/***
* 保存環(huán)境:指在切換環(huán)境時調(diào)用一次
*/
public void saveAppEnv(EnvironmentType type) {
SpUtil.setString(APP_ENV, type.configType);
}
/***
* 獲取環(huán)境類型
*/
public EnvironmentType getAppEnv() {
if (mCurrentEnvType == null) {
// Log.e("sun:", "FLAVOR: " + BuildConfig.FLAVOR);
String env;
if (GlobalConstant.AUTO.equals(BuildConfig.FLAVOR)) {
env = SpUtil.getString(APP_ENV, EnvironmentType.DEFAULT.configType);
if (TextUtils.isEmpty(env)) {
env = EnvironmentType.DEFAULT.configType;
}
} else {
env = EnvironmentType.DEFAULT.configType;
}
mCurrentEnvType = EnvironmentType.map(env);
}
return mCurrentEnvType;
}
//環(huán)境類型
public enum EnvironmentType {
// 默認(rèn)環(huán)境dev config:環(huán)境配置文件名
DEFAULT("config"),
// develop環(huán)境
DEV("configDev"),
// 自定義測試環(huán)境
CUSTOM("configCustom"),
// 預(yù)發(fā)布環(huán)境
PRE("configPre"),
// 線上環(huán)境
PRODUCT("configProduct");
String configType;
EnvironmentType(String configType) {
this.configType = configType;
}
public static EnvironmentType map(String configType) {
if (TextUtils.equals(EnvironmentType.DEV.configType, configType)) {
return EnvironmentType.DEV;
} else if (TextUtils.equals(EnvironmentType.PRE.configType, configType)) {
return EnvironmentType.PRE;
} else if (TextUtils.equals(EnvironmentType.CUSTOM.configType, configType)) {
return EnvironmentType.CUSTOM;
} else if (TextUtils.equals(EnvironmentType.PRODUCT.configType, configType)) {
return EnvironmentType.PRODUCT;
} else {
return EnvironmentType.DEFAULT;
}
}
}
}
5. PropertyKey :配置文件相關(guān)屬性設(shè)置
@StringDef({PropertyKey.BASE_URL,PropertyKey.IS_PRODUCT
,PropertyKey.IS_SHOW_LOG,PropertyKey.IS_JSON
, PropertyKey.NAME_ENV})
@Retention(RetentionPolicy.SOURCE)
public @interface PropertyKey {
String NAME_ENV = "name";
String BASE_URL = "api.base.url";
String IS_PRODUCT = "isProduct";
String IS_JSON = "isJSON";
String IS_SHOW_LOG = "isShowLog";
}
6. 常量類GlobalConstant
/**
* 常量池
* @Author Promise Sun
*/
public class GlobalConstant {
public static final String AUTO="auto";
}
7. SharedPreferences工具類
(注:SP工具類可以使用你自己項目中已有的错览,下面這段可以忽略)
public class SpUtil {
private static SpUtil mInstance;
private static MMKV mv;
private SpUtil() {
mv = MMKV.defaultMMKV();
}
/**
* 初始化MMKV,只需要初始化一次纫雁,建議在Application中初始化
*/
public static SpUtil getInstance() {
if (mInstance == null) {
synchronized (SpUtil.class) {
if (mInstance == null) {
mInstance = new SpUtil();
}
}
}
return mInstance;
}
/**
* 保存數(shù)據(jù)的方法,我們需要拿到保存數(shù)據(jù)的具體類型倾哺,然后根據(jù)類型調(diào)用不同的保存方法
*
* @param key
* @param object
*/
public static void setFloat(String key, Object object) {
mv.encode(key, (Float) object);
}
public static void setString(String key, Object object) {
mv.encode(key, (String) object);
}
public static void setInt(String key, Object object) {
mv.encode(key, (Integer) object);
}
public static void setDouble(String key, Object object) {
mv.encode(key, (Double) object);
}
public static void setLong(String key, Object object) {
mv.encode(key, (Long) object);
}
public static void setBoolean(String key, Object object) {
mv.encode(key, (Boolean) object);
}
public static void setStringSet(String key, Set<String> sets) {
mv.encode(key, sets);
}
public static void setParcelable(String key, Parcelable obj) {
mv.encode(key, obj);
}
/**
* 得到保存數(shù)據(jù)的方法轧邪,我們根據(jù)默認(rèn)值得到保存的數(shù)據(jù)的具體類型,然后調(diào)用相對于的方法獲取值
*/
public static Integer getInt(String key) {
return mv.decodeInt(key, 0);
}
public static Double getDouble(String key) {
return mv.decodeDouble(key, 0.00);
}
public static Long getLong(String key) {
return mv.decodeLong(key, 0L);
}
public static Boolean getBoolean(String key) {
return mv.decodeBool(key, false);
}
public static Float getFloat(String key) {
return mv.decodeFloat(key, 0F);
}
public static String getString(String key) {
return mv.decodeString(key, "");
}
public static String getString(String key, String defaultValue) {
return mv.decodeString(key, defaultValue);
}
public static Set<String> getStringSet(String key) {
return mv.decodeStringSet(key, Collections.<String>emptySet());
}
public static Parcelable getParcelable(String key) {
return mv.decodeParcelable(key, null);
}
/**
* 移除某個key對
*
* @param key
*/
public static void removeByKey(String key) {
mv.removeValueForKey(key);
}
/**
* 清除所有key
*/
public static void removeAll() {
mv.clearAll();
}
/**
* 是否包含某個key
*/
public static boolean containsKey(String key) {
return mv.containsKey(key);
}
}
五羞海、解決方案步驟三:頁面切換環(huán)境功能實現(xiàn)
1. 功能實現(xiàn)類ChangeUrlEnvActivity
(注:功能已實現(xiàn)忌愚,只是UI有點丑,大家可以自行開發(fā)設(shè)置)
/**
* @Author Promise Sun
*/
public class ChangeUrlEnvActivity extends AppCompatActivity {
@BindView(R.id.toolbar_left)
RelativeLayout ShowBack;
@BindView(R.id.tvTitle)
TextView tvTitle;
@BindView(R.id.tv_env)
TextView tv_env;
@BindView(R.id.tv_Env_Show)
TextView tv_Env_Show;
@BindView(R.id.group)
RadioGroup mRadioGroup;
@BindView(R.id.rb_test)
RadioButton rb_test;
@BindView(R.id.et_base_url)
EditText et_base_url;
@BindView(R.id.ll_set_env)
LinearLayout ll_set_env;
@BindView(R.id.btn_ok)
Button btn_ok;
private Unbinder unbinder;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_url_env_change);
unbinder =ButterKnife.bind(this);
initView();
}
@Override
protected void onDestroy() {
super.onDestroy();
unbinder.unbind();
}
@OnClick({R.id.toolbar_left,R.id.btn_ok})
public void onViewClicked(View view) {
switch (view.getId()) {
case R.id.toolbar_left:
finish();
break;
default:
break;
}
}
protected void initView() {
ShowBack.setVisibility(View.VISIBLE);
tvTitle.setTextColor(getResources().getColor(R.color.black));
tvTitle.setText("URL 環(huán)境");
tv_env.setText("當(dāng)前測試環(huán)境:"+ PropertyUtils.getEnvironmentName());
tv_Env_Show.setText( "url :" + PropertyUtils.getApiBaseUrl());
ConfigManager.EnvironmentType environmentType = PropertyUtils.environmentMap();
switch (environmentType) {
case DEV:
mRadioGroup.check(R.id.rb_dev);
break;
case CUSTOM:
mRadioGroup.check(R.id.rb_test);
break;
case PRE:
mRadioGroup.check(R.id.rb_pre);
break;
case PRODUCT:
mRadioGroup.check(R.id.rb_product);
break;
default:
mRadioGroup.check(R.id.rb_dev);
break;
}
//點擊切換環(huán)境
mRadioGroup.setOnCheckedChangeListener((group, checkedId) -> {
switch (checkedId) {
case R.id.rb_dev:
if (ConfigManager.getDefault().getAppEnv() != ConfigManager.EnvironmentType.DEV) {
ConfigManager.getDefault().saveAppEnv(ConfigManager .EnvironmentType.DEV);
}
setRestart();
break;
case R.id.rb_pre:
if (ConfigManager.getDefault().getAppEnv() != ConfigManager.EnvironmentType.PRE) {
ConfigManager.getDefault().saveAppEnv(ConfigManager.EnvironmentType.PRE);
}
setRestart();
break;
case R.id.rb_product:
if (ConfigManager.getDefault().getAppEnv() != ConfigManager.EnvironmentType.PRODUCT) {
ConfigManager.getDefault().saveAppEnv(ConfigManager.EnvironmentType.PRODUCT);
}
setRestart();
break;
case R.id.rb_test:
if (ConfigManager.getDefault().getAppEnv() != ConfigManager.EnvironmentType.CUSTOM) {
ConfigManager.getDefault().saveAppEnv(ConfigManager.EnvironmentType.CUSTOM);
}
setRestart();
break;
}
});
}
private void setRestart() {
Toast.makeText(this, "1s后關(guān)閉App却邓,重啟生效", Toast.LENGTH_SHORT).show();
//退出app要進行退出登錄和去除數(shù)據(jù)相關(guān)
// system.exit(0)菜循、finish、android.os.Process.killProcess(android.os.Process.myPid())區(qū)別:
//可以殺死當(dāng)前應(yīng)用活動的進程申尤,這一操作將會把所有該進程內(nèi)的資源(包括線程全部清理掉)癌幕。
//當(dāng)然,由于ActivityManager時刻監(jiān)聽著進程昧穿,一旦發(fā)現(xiàn)進程被非正常Kill勺远,它將會試圖去重啟這個進程。這就是為什么时鸵,有時候當(dāng)我們試圖這樣去結(jié)束掉應(yīng)用時胶逢,發(fā)現(xiàn)它又自動重新啟動的原因。
//1. System.exit(0) 表示是正常退出饰潜。
//2. System.exit(1) 表示是非正常退出初坠,通常這種退出方式應(yīng)該放在catch塊中。
//3. Process.killProcess 或 System.exit(0)當(dāng)前進程確實也被 kill 掉了彭雾,但 app 會重新啟動碟刺。
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Process.killProcess(Process.myPid());
}
}, 1000);
}
}
2. 布局文件 activity_url_env_change.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/include_toolbar" />
<TextView
android:id="@+id/tv_env"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:text="當(dāng)前測試環(huán)境:"
android:textSize="18sp"
android:textColor="#FFFFFF"
android:textStyle="bold" />
<TextView
android:id="@+id/tv_Env_Show"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:textSize="18sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:text="切換環(huán)境,請選擇:"
android:textSize="18sp"
android:textStyle="bold" />
<RadioGroup
android:id="@+id/group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="25dp"
android:gravity="center">
<RadioButton
android:id="@+id/rb_dev"
android:layout_width="170dp"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:padding="16dp"
android:text="dev 環(huán)境"
android:textStyle="bold"/>
<RadioButton
android:id="@+id/rb_pre"
android:layout_width="170dp"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:padding="16dp"
android:text="pre 環(huán)境"
android:textStyle="bold"/>
<RadioButton
android:id="@+id/rb_product"
android:layout_width="170dp"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:padding="16dp"
android:text="prod 線上環(huán)境"
android:textStyle="bold"/>
<RadioButton
android:id="@+id/rb_test"
android:layout_width="170dp"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:padding="16dp"
android:text="自定義測試環(huán)境"
android:textStyle="bold"/>
</RadioGroup>
<LinearLayout
android:id="@+id/ll_set_env"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:visibility="gone"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:text="手動設(shè)置測試環(huán)境:"
android:textSize="18sp"
android:textStyle="bold" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="@color/white"
android:orientation="horizontal">
<TextView
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="15dp"
android:layout_marginRight="10dp"
android:gravity="center_vertical"
android:text="URL :"
android:textSize="16sp" />
<EditText
android:id="@+id/et_base_url"
android:layout_width="match_parent"
android:layout_height="55dp"
android:layout_gravity="center_vertical"
android:layout_marginRight="10dp"
android:layout_toLeftOf="@+id/iv_user_del"
android:layout_toRightOf="@+id/iv_icon_username"
android:background="#ffffffff"
android:hint="請輸入測試環(huán)境url"
android:maxLines="2"
android:padding="10dp"
android:singleLine="true"
android:textColor="#0f30b9"
android:textColorHint="#999999"
android:textSize="16sp" />
</LinearLayout>
<Button
android:id="@+id/btn_ok"
android:layout_width="150dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="100dp"
android:background="#0f30b9"
android:text="o k"
android:textColor="@color/white"
android:textSize="18sp" />
</LinearLayout>
</LinearLayout>
版權(quán)聲明:本文為博主原創(chuàng)文章薯酝,轉(zhuǎn)載請點贊此文并注明出處半沽,謝謝爽柒!