lidroid(Android服务器TomCat实现软件的版本检测)

算下来,TomCat服务器已经写了很长一段时间了,一直说拿他来搞点事 情,也一直没做,今天刚好有空,交流群还有人请教,就寻思着把一些相关性的原理和基础操作写下来,虽然我网络这一块还是不怎...

算下来,TomCat服务器已经写了很长一段时间了,一直说拿他来搞点事 情,也一直没做,今天刚好有空,交流群还有人请教,就寻思着把一些相关性的原理和基础操作写下来,虽然我网络这一块还是不怎么扎实,嘿嘿,还记得我们怎么搭建的服务器吗?

地址:[Android服务器——TomCat服务器的搭建](http://blog.csdn.net/qq_26787115/article/details/50575915)

我们新建一个项目TomCatVersion

这边先来说一下原理,我们做的小例子也是十分的简单,一个首页,我们用Handler实现,然后同步检测当前版本号和系统的版本号对比,如果有升级则弹出提示框提示升级,点击确定开始下载apk,同时显示进度,等下载完成之后启动新下载的APK进行安装,如果点击取消,进入主页,如果没有升级,则直接进入主页,思路应该很清晰吧!那好,我们开始!

一,**工作

1.搭建TomCat服务器

我们没有服务器,所以使用TomCat服务器模拟,不会搭建的请看[Android服务器——TomCat服务器的搭建](http://blog.csdn.net/qq_26787115/article/details/50575915)

2.新建一个IndexActivity类,并且在AndroidManifest.xml里注册并且设置为主入口

<activity android:name="com.lgl.tomcatversion.IndexActivity" >

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

3.**一张首页的图片

可有可无,这里作为演示就去网上下载了一张

4.版本更新接口

也就是服务器的地址,我们这里也就直接自己写一段简单的json了

{

"versionName": "2.0",

"versionCode": 1,

"content": "修复多项bug!",

"url": "http://localhost:192.168.1.101/lgl/TomCatVersion.apk"

}

我们把他放在服务器里面】

Android服务器TomCat实现软件的版本检测,升级,以及下载进度

乱码请无视,浏览器的锅

5.网络权限

<uses-permission android:name="android.permission.INTERNET"/>

二.layout_index.xml

布局就没什么内容了,一个进度,一个文本

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:andro

android:layout_width="match_parent"

android:layout_height="match_parent"

android:background="@drawable/index"

android:gravity="center_horizontal">

<TextView

android:gravity="center"

android:layout_alignParentBottom="true"

android:

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_marginBottom="20dp"

android:textColor="@android:color/white"

android:textSize="20sp" />

<ProgressBar

android:layout_centerInParent="true"

android:layout_alignParentBottom="true"

android:

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_marginBottom="60dp" />

</RelativeLayout>

三.逻辑**

1.获取版本号

首先你的首页的文本上需要获取当前应用的版本号吧

/**

* 获取APP版本号

*

* @return

*/

private String getAppVersion() {

try {

//PackageManager管理器

PackageManager pm = getPackageManager();

//获取相关信息

packageInfo = pm.getPackageInfo(getPackageName(), 0);

//版本名称

String name = packageInfo.versionName;

//版本号

int version = packageInfo.versionCode;

Log.i("版本信息", "版本名称:"+name+"版本号"+version);

return name;

} catch (NameNotFoundException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

//如果出现异常抛出null

return null;

}

我们打印出来的Log

Android服务器TomCat实现软件的版本检测,升级,以及下载进度

我们需要的就是这个name(版本名称)

2.解析JSON

这段json还是十分的简单的,我们直接就用原生的方式解析了,本来想用Volley的,但是演示的话,希望各位自己根据需求使用

/**

* 解析JSON

*/

private void getJSON() {

// 子线程访问,耗时操作

new Thread() {

public void run() {

try {

// JSON地址

HttpURLConnection conn = (HttpURLConnection) new URL(

//模拟器一般有一个预留IP:10.0.2.2

"http://192.168.1.103:8080/lgl/update.json")

.openConnection();

//请求方式GRT

conn.setRequestMethod("GET");

//连接超时

conn.setConnectTimeout(5000);

//响应超时

conn.setReadTimeout(3000);

//连接

conn.connect();

//获取请求码

int responseCode = conn.getResponseCode();

//等于200说明请求成功

if(responseCode == 200){

//拿到他的输入流

InputStream in = conn.getInputStream();

String stream = Utils.toStream(in);

Log.i("JSON", stream);

}

} catch (MalformedURLException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}.start();

}

这里我们写了一个Utils来转换流

package com.lgl.tomcatversion;

import java.io.ByteArrayOutputStream;

import java.io.IOException;

import java.io.InputStream;

/**

* 流转换Stream的工具栏

*

* @author LGL

*

*/

public class Utils {

// 对外发放

public static String toStream(InputStream in) throws IOException {

ByteArrayOutputStream out = new ByteArrayOutputStream();

// 长度

int length = 0;

byte[] buffer = new byte[1024];

// -1代表读完了

while ((length = in.read(buffer)) != -1) {

out.write(buffer, 0, length);

}

// 读完关闭

in.close();

out.close();

// 我们把返回的数据转换成String

return out.toString();

}

}

这样我们就可以把Json打印出来了

Android服务器TomCat实现软件的版本检测,升级,以及下载进度

既然获取到了,那我们就开始解析JSON吧

在Log后面继续写代码

// 解析

JSONObject jsonObject = new JSONObject(stream);

// 获取版本名

String versionName = jsonObject.getString("versionName");

// 获取版本号

int versionCode = jsonObject.getInt("versionCode");

// 获取更新内容

String content = jsonObject.getString("content");

// 获取下载地址

String url = jsonObject.getString("url");

这样我们就解析完成了

3.版本比较以及提示更新

这里我们可以根据name或者code的比较来判断是否有更新,有更新的话,弹出提示框,点确定再更新,我这里就比较Code了,先写以恶搞获取code的方法

/**

* 获取versionCode

*/

private int getCode() {

// PackageManager管理器

PackageManager pm = getPackageManager();

// 获取相关信息

try {

packageInfo = pm.getPackageInfo(getPackageName(), 0);

// 版本号

int version = packageInfo.versionCode;

return version;

} catch (NameNotFoundException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

return 0;

}

>然后我们接着刚才解析完JSON的地方比对,我们先把服务器的JSON数据改了

{

"versionName": "2.0",

"versionCode": 2,

"content": "修复多项bug!",

"url": "http://192.168.1.103:8080/lgl/TomCatVersion.apk"

}

>这样就会提示更新了,我们检修

// 版本判断

if (versionCode > getCode()) {

// 提示更新

msg.what = UPDATE_YES;

} else {

// 不更新,跳转到主页

msg.what = UPDATE_NO;

}

>子线程中我们是不能弹框的,所以我们用Handler,当我们发送UPDATE_YES的时候就弹框,也就是执行我们弹框的方法

/**

* 升级弹框

*/

private void showUpdateDialog() {

updateDialog = new CustomDialog(this, 0, 0, R.layout.dialog_update,

R.style.Theme_dialog, Gravity.CENTER, 0);

// 更新内容

dialog_update_content = (TextView) updateDialog

.findViewById(R.id.dialog_update_content);

dialog_update_content.setText(content);

// 确定更新

dialog_confrim = (TextView) updateDialog

.findViewById(R.id.dialog_confrim);

dialog_confrim.setOnClickListener(this);

// 取消更新

dialog_cancel = (TextView) updateDialog

.findViewById(R.id.dialog_cancel);

dialog_cancel.setOnClickListener(this);

updateDialog.show();

}

>这里我用了一个自定义的Dialog'

CustomDialog

package com.lgl.tomcatversion;

import android.app.Dialog;

import android.content.Context;

import android.view.Gravity;

import android.view.Window;

import android.view.WindowManager;

public class CustomDialog extends Dialog {

public CustomDialog(Context context, int layout, int style) {

this(context, WindowManager.LayoutParams.MATCH_PARENT,

WindowManager.LayoutParams.WRAP_CONTENT, layout, style,

Gravity.CENTER);

}

public CustomDialog(Context context, int width, int height, int layout,

int style, int gravity, int anim) {

super(context, style);

setContentView(layout);

// set window params

Window window = getWindow();

WindowManager.LayoutParams params = window.getAttributes();

// set width,height by density and gravity

// float density = getDensity(context);

params.width = WindowManager.LayoutParams.MATCH_PARENT;

params.height = WindowManager.LayoutParams.WRAP_CONTENT;

params.gravity = gravity;

window.setAttributes(params);

window.setWindowAnimations(anim);

}

public CustomDialog(Context context, int width, int height, int layout,

int style, int gravity) {

this(context, width, height, layout, style, gravity,

R.style.pop_anim_style);

}

}

他需要用到一些资源

dialog_update.xml

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:andro

android:

android:layout_width="match_parent"

android:layout_height="150dip"

android:layout_marginEnd="20dp"

android:layout_marginStart="20dp"

android:background="@drawable/dialog_bg"

android:orientation="vertical" >

<LinearLayout

android:

android:layout_width="match_parent"

android:layout_height="45dp"

android:layout_alignParentBottom="true"

android:layout_gravity="bottom"

android:orientation="horizontal" >

<TextView

android:

android:layout_width="0dp"

android:layout_height="match_parent"

android:layout_weight="1"

android:gravity="center"

android:singleLine="true"

android:text="暂不更新"

android:textSize="18sp" />

<View

android:layout_width="0.2dp"

android:layout_height="match_parent"

android:background="#******" />

<TextView

android:

android:layout_width="0dp"

android:layout_height="match_parent"

android:layout_weight="1"

android:gravity="center"

android:singleLine="true"

android:text="立即下载"

android:textSize="18sp" />

</LinearLayout>

<View

android:

android:layout_width="match_parent"

android:layout_height="0.2dp"

android:layout_above="@id/dialog_notifly_bottom"

android:background="#******" />

<TextView

android:

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_above="@+id/dialog_notifly_line"

android:layout_below="@+id/TextView01"

android:layout_marginBottom="10dip"

android:layout_marginLeft="10dip"

android:layout_marginRight="10dip"

android:gravity="center"

android:singleLine="true"

android:textSize="18sp" />

<TextView

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_alignParentLeft="true"

android:layout_alignParentTop="true"

android:layout_marginTop="20dp"

android:gravity="center"

android:singleLine="true"

android:text="检测到有新版本,是否下载?"

android:textSize="18sp" />

</RelativeLayout>

styles.xml

<style name="pop_anim_style">

<item name="android:windowEnterAnimation">@anim/pop_in</item>

<item name="android:windowExitAnimation">@anim/pop_out</item>

</style>

<style name="Theme_dialog" parent="@android:style/Theme.Dialog">

<item name="android:windowBackground">@android:color/transparent</item>

<item name="android:windowNoTitle">true</item>

</style>

以及用到的动画

pop_in.xml

<?xml version="1.0" encoding="utf-8"?>

<set xmlns:andro >

<translate

android:duration="@android:integer/config_shortAnimTime"

android:fromXDelta="0"

android:fromYDelta="100%"

android:toXDelta="0"

android:toYDelta="0" />

</set>

pop_out.xml

<?xml version="1.0" encoding="utf-8"?>

<set xmlns:andro >

<translate

android:duration="@android:integer/config_shortAnimTime"

android:fromXDelta="0"

android:fromYDelta="0"

android:toXDelta="0"

android:toYDelta="100%" />

</set>

接着我们再执行一下

Android服务器TomCat实现软件的版本检测,升级,以及下载进度

我们现在再来处理一下没有更新的逻辑,没有更新的话直接跳主页面,我们写一个方法

/**

* 跳转主页面

*/

private void goHome() {

startActivity(new Intent(this, MainActivity.class));

finish();

}

但是这样还是有个问题,他没有更新一下子就跳过去了,所以我们在发消息的时候先让线程睡一会儿

try {

//停留三秒钟

Thread.sleep(3000);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

// 全部走完发消息

handler.sendMessage(msg);

但是这里又出现了一个新的问题,毕竟是网络问题,他是耗时的,这样的话,万一等太久了用户体验也上不去啊,所以我们这里要做一个优化

我们在开始网络请求的时间记录一个时间

// 开始访问网络的时间

long startTime = System.currentTimeMillis();

然后再网络请求结束的时候去计算时间并且计算一共用了多少时间

// 网络访问结束的时间

long endTime = System.currentTimeMillis();

// 计算网络用了多少时间

long time = endTime - startTime;

try {

if (time < 3000) {

// 停留三秒钟

Thread.sleep(3000 - time);

}

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

4.下载更新以及下载进度

涉及到下载,这里你可以使用很多的开源框架,我这里使用的是xutils

地址:https://git**.com/wyouflf/xUtils

我们下载之后拷贝在libs里就可以用了,用起来也很简单,使用之前先加个权限

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

然后开始下载

/**

* 下载更新

*/

private void downloadAPK() {

tv_pro.setVisibility(View.VISIBLE);

// 判断是否有SD卡

if (Environment.getExternalStorageState().equals(

Environment.MEDIA_MOUNTED)) {

// 获取手机根目录

String path = Environment.getExternalStorageDirectory()

.getAbsolutePath() + "/TomCatVersion.apk";

HttpUtils httpUtils = new HttpUtils();

/**

* 1.网络地址 2.存放地址 3.回调

*/

httpUtils.download(url, path, new RequestCallBack<File>() {

// 下载进度

@Override

public void onLoading(long total, long current,

boolean isUploading) {

// TODO Auto-generated method stub

super.onLoading(total, current, isUploading);

// 显示进度

tv_pro.setText(100 * current / total + "%");

}

// 成功

@Override

public void onSuccess(ResponseInfo<File> responseInfo) {

}

// 失败

@Override

public void onFailure(HttpException error, String msg) {

Log.i("error", msg);

}

});

} else {

Toast.makeText(getApplicationContext(), "未找到SD卡", Toast.LENGTH_LONG)

.show();

}

}

这里的代码逻辑也是十分的简单的,多吧,我们来看一下效果‘

Android服务器TomCat实现软件的版本检测,升级,以及下载进度

更新的包体积有点小,所以一下子就百分之百了,我们去SD卡更目录看一下

Android服务器TomCat实现软件的版本检测,升级,以及下载进度

确定是下载完成了,但是下完完之后啥也没发生啊,这就要我们再次优化了

下载完之后软起动

下载完之后自动进入安装界面,这才是真正的体验,我们在onSuccess()方法中

// 跳转系统安装页面

Intent i = new Intent();

i.setAction(Intent.ACTION_VIEW);

i.addCategory(Intent.CATEGORY_DEFAULT);

i.setDataAndType(Uri.fromFile(new File(path)),"application/vnd.android.package-archive");

startActivity(i);

我们来看看效果

Android服务器TomCat实现软件的版本检测,升级,以及下载进度

大致的一个逻辑思路就是这样了完整代码

IndexActivity

package com.lgl.tomcatversion;

import java.io.File;

import java.io.IOException;

import java.io.InputStream;

import java.net.HttpURLConnection;

import java.net.MalformedURLException;

import java.net.URL;

import org.json.JSONException;

import org.json.JSONObject;

import android.app.Activity;

import android.content.DialogInterface;

import android.content.DialogInterface.OnCancelListener;

import android.content.Intent;

import android.content.pm.PackageInfo;

import android.content.pm.PackageManager;

import android.content.pm.PackageManager.NameNotFoundException;

import android.net.Uri;

import android.os.Bundle;

import android.os.Environment;

import android.os.Handler;

import android.os.Message;

import android.util.Log;

import android.view.Gravity;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.TextView;

import android.widget.Toast;

import com.lidroid.xutils.HttpUtils;

import com.lidroid.xutils.exception.HttpException;

import com.lidroid.xutils.http.ResponseInfo;

import com.lidroid.xutils.http.callback.RequestCallBack;

/**

* 首页

*

* @author LGL

*

*/

public class IndexActivity extends Activity implements OnClickListener {

// 更新

private static final int UPDATE_YES = 1;

// 不更新

private static final int UPDATE_NO = 2;

// URL错误

private static final int URL_ERROR = 3;

// 没有网络

private static final int IO_ERROR = 4;

// 数据异常

private static final int JSON_ERROR = 5;

private TextView tv_version;

private PackageInfo packageInfo;

private JSONObject jsonObject;

private String versionName;

private int versionCode;

private String content;

private String url;

private TextView tv_pro;

// 升级提示框

private CustomDialog updateDialog;

private TextView dialog_update_content;

private TextView dialog_confrim;

private TextView dialog_cancel;

private Handler handler = new Handler() {

public void handleMessage(android.os.Message msg) {

switch (msg.what) {

case UPDATE_YES:

showUpdateDialog();

break;

case UPDATE_NO:

goHome();

break;

case URL_ERROR:

Toast.makeText(getApplicationContext(), "地址错误",

Toast.LENGTH_LONG).show();

goHome();

break;

case IO_ERROR:

Toast.makeText(getApplicationContext(), "请检查网络",

Toast.LENGTH_LONG).show();

goHome();

break;

case JSON_ERROR:

Toast.makeText(getApplicationContext(), "Json解析错误",

Toast.LENGTH_LONG).show();

goHome();

break;

// 就算你报任何错,爸比是爱你的,依然让你进主页面

}

}

};

private String path;

@Override

protected void onCreate(Bundle savedInstanceState) {

// TODO Auto-generated method stub

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_index);

initView();

getJSON();

}

/**

* 初始化控件

*

* @author LGL

*/

private void initView() {

tv_pro = (TextView) findViewById(R.id.tv_pro);

tv_version = (TextView) findViewById(R.id.tv_version);

// 设置版本号

tv_version.setText("版本号:" + getAppVersion());

}

/**

* 获取APP版本号

*

* @return

*/

private String getAppVersion() {

try {

// PackageManager管理器

PackageManager pm = getPackageManager();

// 获取相关信息

packageInfo = pm.getPackageInfo(getPackageName(), 0);

// 版本名称

String name = packageInfo.versionName;

// 版本号

int version = packageInfo.versionCode;

Log.i("版本信息", "版本名称:" + name + "版本号" + version);

return name;

} catch (NameNotFoundException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

// 如果出现异常抛出

return "无法获取";

}

/**

* 解析JSON

*/

private void getJSON() {

// 子线程访问,耗时操作

new Thread() {

public void run() {

Message msg = Message.obtain();

// 开始访问网络的时间

long startTime = System.currentTimeMillis();

try {

// JSON地址

HttpURLConnection conn = (HttpURLConnection) new URL(

// 模拟器一般有一个预留IP:10.0.2.2

"http://192.168.1.103:8080/lgl/update.json")

.openConnection();

// 请求方式GRT

conn.setRequestMethod("GET");

// 连接超时

conn.setConnectTimeout(5000);

// 响应超时

conn.setReadTimeout(3000);

// 连接

conn.connect();

// 获取请求码

int responseCode = conn.getResponseCode();

// 等于200说明请求成功

if (responseCode == 200) {

// 拿到他的输入流

InputStream in = conn.getInputStream();

String stream = Utils.toStream(in);

Log.i("JSON", stream);

jsonObject = new JSONObject(stream);

versionName = jsonObject.getString("versionName");

versionCode = jsonObject.getInt("versionCode");

content = jsonObject.getString("content");

url = jsonObject.getString("url");

// 版本判断

if (versionCode > getCode()) {

// 提示更新

msg.what = UPDATE_YES;

} else {

// 不更新,跳转到主页

msg.what = UPDATE_NO;

}

}

} catch (MalformedURLException e) {

// URL错误

e.printStackTrace();

msg.what = URL_ERROR;

} catch (IOException e) {

// 没有网络

e.printStackTrace();

msg.what = IO_ERROR;

} catch (JSONException e) {

// 数据错误

e.printStackTrace();

msg.what = JSON_ERROR;

} finally {

// 网络访问结束的时间

long endTime = System.currentTimeMillis();

// 计算网络用了多少时间

long time = endTime - startTime;

try {

if (time < 3000) {

// 停留三秒钟

Thread.sleep(3000 - time);

}

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

// 全部走完发消息

handler.sendMessage(msg);

}

}

}.start();

}

/**

* 获取versionCode

*/

private int getCode() {

// PackageManager管理器

PackageManager pm = getPackageManager();

// 获取相关信息

try {

packageInfo = pm.getPackageInfo(getPackageName(), 0);

// 版本号

int version = packageInfo.versionCode;

return version;

} catch (NameNotFoundException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

return 0;

}

/**

* 升级弹框

*/

private void showUpdateDialog() {

updateDialog = new CustomDialog(this, 0, 0, R.layout.dialog_update,

R.style.Theme_dialog, Gravity.CENTER, 0);

//如果他点击其他地方,不安装,我们就直接去

updateDialog.setOnCancelListener(new OnCancelListener() {

@Override

public void onCancel(DialogInterface dialog) {

goHome();

}

});

// 更新内容

dialog_update_content = (TextView) updateDialog

.findViewById(R.id.dialog_update_content);

dialog_update_content.setText(content);

// 确定更新

dialog_confrim = (TextView) updateDialog

.findViewById(R.id.dialog_confrim);

dialog_confrim.setOnClickListener(this);

// 取消更新

dialog_cancel = (TextView) updateDialog

.findViewById(R.id.dialog_cancel);

dialog_cancel.setOnClickListener(this);

updateDialog.show();

}

/**

* 点击事件

*/

@Override

public void onClick(View v) {

switch (v.getId()) {

case R.id.dialog_confrim:

updateDialog.di**iss();

downloadAPK();

break;

case R.id.dialog_cancel:

// 跳主页面

goHome();

break;

}

}

/**

* 跳转主页面

*/

private void goHome() {

startActivity(new Intent(this, MainActivity.class));

finish();

}

/**

* 下载更新

*/

private void downloadAPK() {

tv_pro.setVisibility(View.VISIBLE);

// 判断是否有SD卡

if (Environment.getExternalStorageState().equals(

Environment.MEDIA_MOUNTED)) {

path = Environment.getExternalStorageDirectory().getAbsolutePath()

+ "/TomCatVersion.apk";

HttpUtils httpUtils = new HttpUtils();

/**

* 1.网络地址 2.存放地址 3.回调

*/

httpUtils.download(url, path, new RequestCallBack<File>() {

// 下载进度

@Override

public void onLoading(long total, long current,

boolean isUploading) {

// TODO Auto-generated method stub

super.onLoading(total, current, isUploading);

// 显示进度

tv_pro.setText(100 * current / total + "%");

}

// 成功

@Override

public void onSuccess(ResponseInfo<File> responseInfo) {

// 跳转系统安装页面

Intent i = new Intent();

i.setAction(Intent.ACTION_VIEW);

i.addCategory(Intent.CATEGORY_DEFAULT);

i.setDataAndType(Uri.fromFile(new File(path)),

"application/vnd.android.package-archive");

startActivity(i);

}

// 失败

@Override

public void onFailure(HttpException error, String msg) {

Log.i("error", msg);

}

});

} else {

Toast.makeText(getApplicationContext(), "未找到SD卡", Toast.LENGTH_LONG)

.show();

}

}

}

layout_index.xml

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:andro

android:layout_width="match_parent"

android:layout_height="match_parent"

android:background="@drawable/index"

android:gravity="center_horizontal" >

<TextView

android:

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_alignParentBottom="true"

android:layout_marginBottom="20dp"

android:gravity="center"

android:textColor="@android:color/white"

android:textSize="20sp" />

<ProgressBar

android:

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_alignParentBottom="true"

android:layout_centerInParent="true"

android:layout_marginBottom="60dp" />

<TextView

android:

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_alignParentTop="true"

android:layout_marginTop="207dp"

android:gravity="center"

android:text="欢迎"

android:textColor="#fff"

android:textSize="50sp" />

<TextView

android:textColor="#220000"

android:visibility="invisible"

android:

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_above="@+id/progressBar"

android:layout_centerHorizontal="true"

android:layout_marginBottom="95dp"

android:textAppearance="?android:attr/textAppearanceLarge" />

</RelativeLayout>

Demo下载地址:http://download.csdn.net/detail/qq_26787115/9463814

  • 发表于 2023-02-08 09:02
  • 阅读 ( 223 )
  • 分类:教育培训

0 条评论

请先 登录 后评论
环球游戏
环球游戏

471 篇文章

感兴趣的文章

相关问题