文档格式(1.标题格式:标题2;2.字体格式:arial;3.字体大小:正文-中)
1. 代码中为textView设置drawableleft
a. 在布局文件中设置:
<span style="font-size:18px;">android:drawableLeft="@drawable/icon"</span>
b.代码中设置
<span style="font-size:18px;">Drawable drawable= getResources().getDrawable(R.drawable.drawable);
/// 这一步必须要做,否则不会显示.
drawable.setBounds(0, 0, drawable.getMinimumWidth(), drawable.getMinimumHeight());
myTextview.setCompoundDrawables(drawable,null,null,null);</span>
c.代码中使用textView的函数
<span style="font-size:18px;">public void setCompoundDrawablesWithIntrinsicBounds (Drawable left,
Drawable top, Drawable right, Drawable bottom)</span>
2. 布局文件设置textView超过行数后滚动显示
设置你的TextView的属性:
<span style="font-size:18px;">android:maxLines = "AN_INTEGER"
android:scrollbars = "vertical"</span>
然后在你的代码中用:
yourTextView.setMovementMethod(new ScrollingMovementMethod())
它可以自由的滚动了。
3. 更新模块的实现
我们看到很多Android应用都具有自动更新功能,用户一键就可以完成软件的升级更新。得益于Android系统的软件包管理和安装机制,这一功能实现起来相当简单,下面我们就来实践一下。首先给出界面效果:
1. 准备知识
在AndroidManifest.xml里定义了每个Android apk的版本标识:
<span style="font-size:18px;"><manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.myapp"
android:versionCode="1"
android:versionName="1.0.0"></span>
其中,android:versionCode和android:versionName两个字段分别表示版本代码,版本名称。versionCode是整型数字,versionName是字符串。由于version是给用户看的,不太容易比较大小,升级检查时,可以以检查versionCode为主,方便比较出版本的前后大小。
那么,在应用中如何读取AndroidManifest.xml中的versionCode和versionName呢?可以使用PackageManager的API,参考以下代码:
<span style="font-size:18px;"> public static int getVerCode(Context context) {
int verCode = -1;
try {
verCode = context.getPackageManager()
.getPackageInfo("com.myapp", 0).versionCode;
} catch (NameNotFoundException e) {
Log.e(TAG, e.getMessage());
}
return verCode;
}
public static String getVerName(Context context) {
String verName = "";
try {
verName = context.getPackageManager()
.getPackageInfo("com.myapp", 0).versionName;
} catch (NameNotFoundException e) {
Log.e(TAG, e.getMessage());
}
return verName;
}</span>
或者在AndroidManifest中将android:versionName="1.2.0"写成android:versionName="@string/app_versionName",然后在values/strings.xml中添加对应字符串,这样实现之后,就可以使用如下代码获得版本名称:
<span style="font-size:18px;">public static String getVerName(Context context) {
String verName = context.getResources()
.getText(R.string.app_versionName).toString();
return verName;
}</span>
同理,apk的应用名称可以这样获得:
<span style="font-size:18px;">public static String getAppName(Context context) {
String verName = context.getResources().getText(R.string.app_name)
.toString();
return verName;
}</span>
2. 版本检查
在服务端放置最新版本的apk文件,如:http://localhost/myapp/myapp.apk
同时,在服务端放置对应此apk的版本信息调用接口或者文件,如:http://localhost/myapp/ver.json
ver.json中的内容为:
<span style="font-size:18px;">{
"appname": "jtapp12",
"apkname": "jtapp-12-updateapksamples.apk",
"verName": 1.0.1,
"verCode": 2
}</span>
然后,在手机客户端上进行版本读取和检查:
<span style="font-size:18px;">private boolean getServerVer() {
try {
String verjson = NetworkTool.getContent(Config.UPDATE_SERVER
+ Config.UPDATE_VERJSON);
JSONArray array = new JSONArray(verjson);
if (array.length() > 0) {
JSONObject obj = array.getJSONObject(0);
try {
newVerCode = Integer.parseInt(obj.getString("verCode"));
newVerName = obj.getString("verName");
} catch (Exception e) {
newVerCode = -1;
newVerName = "";
return false;
}
}
} catch (Exception e) {
Log.e(TAG, e.getMessage());
return false;
}
return true;
}</span>
比较服务器和客户端的版本,并进行更新操作。
<span style="font-size:18px;">if (getServerVerCode()) {
int vercode = Config.getVerCode(this); // 用到前面第一节写的方法
if (newVerCode > vercode) {
doNewVersionUpdate(); // 更新新版本
} else {
notNewVersionShow(); // 提示当前为最新版本
}
}</span>
详细方法:
<span style="font-size:18px;">private void notNewVersionShow() {
int verCode = Config.getVerCode(this);
String verName = Config.getVerName(this);
StringBuffer sb = new StringBuffer();
sb.append("当前版本:");
sb.append(verName);
sb.append(" Code:");
sb.append(verCode);
sb.append(",/n已是最新版,无需更新!");
Dialog dialog = new AlertDialog.Builder(Update.this).setTitle("软件更新")
.setMessage(sb.toString())// 设置内容
.setPositiveButton("确定",// 设置确定按钮
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
finish();
}
}).create();// 创建
// 显示对话框
dialog.show();
}
private void doNewVersionUpdate() {
int verCode = Config.getVerCode(this);
String verName = Config.getVerName(this);
StringBuffer sb = new StringBuffer();
sb.append("当前版本:");
sb.append(verName);
sb.append(" Code:");
sb.append(verCode);
sb.append(", 发现新版本:");
sb.append(newVerName);
sb.append(" Code:");
sb.append(newVerCode);
sb.append(", 是否更新?");
Dialog dialog = new AlertDialog.Builder(Update.this)
.setTitle("软件更新")
.setMessage(sb.toString())
// 设置内容
.setPositiveButton("更新",// 设置确定按钮
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
pBar = new ProgressDialog(Update.this);
pBar.setTitle("正在下载");
pBar.setMessage("请稍候...");
pBar.setProgressStyle(ProgressDialog.STYLE_SPINNER);
downFile(Config.UPDATE_SERVER
+ Config.UPDATE_APKNAME);
}
})
.setNegativeButton("暂不更新",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int whichButton) {
// 点击"取消"按钮之后退出程序
finish();
}
}).create();// 创建
// 显示对话框
dialog.show();
}</span>
4. 下载模块
注,本部分参考了前人的相关实现
<span style="font-size:18px;">public void download() {
mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mBuilder = new NotificationCompat.Builder(this);
String appName = getString(getApplicationInfo().labelRes);
int iconId = getApplicationInfo().icon;
mBuilder.setContentTitle(appName).setSmallIcon(iconId);
String urlStr = intent.getStringExtra(APK_DOWNLOAD_URL);
InputStream in = null;
FileOutputStream out = null;
try {
URL url = new URL(urlStr);
HttpURLConnection urlConnection = (HttpURLConnection) url
.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setDoOutput(false);
urlConnection.setConnectTimeout(10 * 1000);
urlConnection.setReadTimeout(10 * 1000);
urlConnection.setRequestProperty("Connection", "Keep-Alive");
urlConnection.setRequestProperty("Charset", "UTF-8");
urlConnection
.setRequestProperty("Accept-Encoding", "gzip, deflate");
urlConnection.connect();
long bytetotal = urlConnection.getContentLength();
long bytesum = 0;
int byteread = 0;
in = urlConnection.getInputStream();
File dir = StorageUtils.getCacheDirectory(this);
String apkName = urlStr.substring(urlStr.lastIndexOf("/") + 1,
urlStr.length());
File apkFile = new File(dir, apkName);
out = new FileOutputStream(apkFile);
byte[] buffer = new byte[BUFFER_SIZE];
int oldProgress = 0;
while ((byteread = in.read(buffer)) != -1) {
bytesum += byteread;
out.write(buffer, 0, byteread);
int progress = (int) (bytesum * 100L / bytetotal);
// 如果进度与之�?进度相等,则�?更新,如果更新太频�?,�?�则会�?�?界�?��?�顿
if (progress != oldProgress) {
updateProgress(progress);
// updateProgressByBroadCast(progress,apkFile.getAbsolutePath());
}
oldProgress = progress;
}
// 下载完�?
mBuilder.setContentText("下载完成").setProgress(0, 0, false);
Intent installAPKIntent = new Intent(Intent.ACTION_VIEW);
// 如果没有设置SDCard写�?��?,或者没有sdcard,apk文件�?存在内存中,�?��?授予�?��?�?能安�?
String[] command = { "chmod", "777", apkFile.toString() };
ProcessBuilder builder = new ProcessBuilder(command);
builder.start();
installAPKIntent.setDataAndType(Uri.fromFile(apkFile),
"application/vnd.android.package-archive");
// installAPKIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// installAPKIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// installAPKIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
installAPKIntent, PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(pendingIntent);
Notification noti = mBuilder.build();
noti.flags = android.app.Notification.FLAG_AUTO_CANCEL;
mNotifyManager.notify(0, noti);
Intent installBroadCast = new Intent(
InstallApkReceiver.ACTION_INSTALL_APK);
installBroadCast.putExtra(InstallApkReceiver.EXTRA_APKPATH,
apkFile.getAbsolutePath());
sendBroadcast(installBroadCast);
} catch (Exception e) {
Log.e(TAG, "download apk file error", e);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}</span>
4.登陆时记住密码和自动登陆
首次登陆时,如果用户勾选中记住密码和自动登陆,则使用sharedpreference保存当前用户输入的信息,在保存密码时使用md5加密方式进行加密,以保证用户信息不被泄露,下次登陆时从sharedpreference中获取之前的账户信息。项目工程地址()
<span style="font-size:18px;">//判断记住密码多选框的状态
SharedPreferences sp = this.getSharedPreferences("userInfo", Context.MODE_WORLD_READABLE);
if(sp.getBoolean("ISCHECK", false))
{
//设置默认是记录密码状态
rem_pw.setChecked(true);
userName.setText(sp.getString("USER_NAME", ""));
password.setText(sp.getString("PASSWORD", ""));
//判断自动登陆多选框状态
if(sp.getBoolean("AUTO_ISCHECK", false))
{
//设置默认是自动登录状态
auto_login.setChecked(true);
//do loginTask
doLoginTask();
//跳转界面
Intent intent = new Intent(LoginActivity.this,LogoActivity.class);
LoginActivity.this.startActivity(intent);
}
}
// 登录监听事件 现在默认为用户名为:liu 密码:123
btn_login.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
userNameValue = userName.getText().toString();
passwordValue = password.getText().toString();
if(userNameValue.equals("liu")&&passwordValue.equals("123"))
{
Toast.makeText(LoginActivity.this,"登录成功", Toast.LENGTH_SHORT).show();
//登录成功和记住密码框为选中状态才保存用户信息
if(rem_pw.isChecked())
{
//记住用户名、密码、
Editor editor = sp.edit();
editor.putString("USER_NAME", userNameValue);
editor.putString("PASSWORD",passwordValue);
editor.commit();
}
//跳转界面
Intent intent = new Intent(LoginActivity.this,LogoActivity.class);
LoginActivity.this.startActivity(intent);
//finish();
}else{
Toast.makeText(LoginActivity.this,"用户名或密码错误,请重新登录", Toast.LENGTH_LONG).show();
}
}
});
//监听记住密码多选框按钮事件
rem_pw.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) {
if (rem_pw.isChecked()) {
System.out.println("记住密码已选中");
sp.edit().putBoolean("ISCHECK", true).commit();
}else {
System.out.println("记住密码没有选中");
sp.edit().putBoolean("ISCHECK", false).commit();
}
}
});
//监听自动登录多选框事件
auto_login.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) {
if (auto_login.isChecked()) {
System.out.println("自动登录已选中");
sp.edit().putBoolean("AUTO_ISCHECK", true).commit();
} else {
System.out.println("自动登录没有选中");
sp.edit().putBoolean("AUTO_ISCHECK", false).commit();
}
}
});</span>
5.使用开源框架universal-imageLoader
知识储备
使用该第三方加载图片的框架需要的步骤:
1.下载universal-imageLoader的jar包或源码,放到libs目录或引入到需要使用的项目中。
2.在manifest.xml文件中设置需要的权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET"/>
3.在应用中配置ImageConfiguration参数。
imageLoader = ImageLoader.getInstance();
imageLoader.init(configuration);
4.使用,由于我们将其再次简单的封装成了一个方便使用的util类,在使用时我们调用如下代码即可:
ImageLoaderUtil.getInstance(ActMediaShow.this).displayImg(
imageUrl, mediaTypeImageView,ImageLoaderUtil.getOptions(),null);
SimpleImageLoadingListener imgLoadListener = new SimpleImageLoadingListener() {
@Override
public void onLoadingStarted(String imageUri, View view) {
spinner.setVisibility(View.VISIBLE);
}
@Override
public void onLoadingFailed(String imageUri, View view,
FailReason failReason) {
String message = null;
switch (failReason.getType()) {
case IO_ERROR:
message = "Input/Output error";
break;
case DECODING_ERROR:
message = "Image can't be decoded";
break;
case NETWORK_DENIED:
message = "Downloads are denied";
break;
case OUT_OF_MEMORY:
message = "Out Of Memory error";
break;
case UNKNOWN:
message = "Unknown error";
break;
}
Toast.makeText(ActMediaShow.this, message,
Toast.LENGTH_SHORT).show();
spinner.setVisibility(View.GONE);
}
@Override
public void onLoadingComplete(String imageUri,
View view, Bitmap loadedImage) {
spinner.setVisibility(View.GONE);
}
};
ImageLoaderUtil.getInstance(ActMediaShow.this).displayImg(
medias.get(position).getImgTypeUri(), mediaTypeImageView,ImageLoaderUtil.getOptions(),null);
下面是我简单封装的一个调用的util
package com.zondy.jinfeng.util;
import java.io.File;
import android.content.Context;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;
import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiskCache;
import com.nostra13.universalimageloader.cache.disc.naming.Md5FileNameGenerator;
import com.nostra13.universalimageloader.cache.memory.impl.UsingFreqLimitedMemoryCache;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
import com.nostra13.universalimageloader.core.assist.ImageScaleType;
import com.nostra13.universalimageloader.core.assist.QueueProcessingType;
import com.nostra13.universalimageloader.core.download.BaseImageDownloader;
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
import com.nostra13.universalimageloader.utils.StorageUtils;
import com.zondy.jinfeng.R;
public class ImageLoaderUtil {
ImageLoader imageLoader;
File cacheDir;// 缓存文件路径
ImageLoaderConfiguration configuration;
private static ImageLoaderUtil instance = null;
private ImageLoaderUtil(Context context) {
cacheDir = StorageUtils.getOwnCacheDirectory(context,
"FreeMeso/imageCache");
configuration = new ImageLoaderConfiguration.Builder(context)
.memoryCacheExtraOptions(1000, 1000)
// max width, max height,即保存的每个缓存文件的最大长宽
.threadPoolSize(3)
// 线程池内加载的数量
.threadPriority(Thread.NORM_PRIORITY - 2)
// 下载图片的线程优先级
.denyCacheImageMultipleSizesInMemory()
.diskCacheFileNameGenerator(new Md5FileNameGenerator())
// 将保存的时候的URI名称用MD5 加密
.memoryCache(new UsingFreqLimitedMemoryCache(2 * 1024 * 1024))
// You can pass your own memory cache
// implementation/你可以通过自己的内存缓存实现
.memoryCacheSize(2 * 1024 * 1024)
// 内存缓存的最大值
.diskCacheSize(50 * 1024 * 1024)
// 50 Mb sd卡(本地)缓存的最大值
.tasksProcessingOrder(QueueProcessingType.LIFO)
// 由原先的discCache -> diskCache
.diskCache(new UnlimitedDiskCache(cacheDir))
// 自定义缓存路径
.imageDownloader(
new BaseImageDownloader(context, 5 * 1000, 30 * 1000))
.writeDebugLogs() // Remove for release app
.build();
// Initialize ImageLoader with configuration.
imageLoader = ImageLoader.getInstance();
imageLoader.init(configuration);
}
/**
* 单例获取ImageLoader实例
*
* @param context
* @return
*/
public static ImageLoaderUtil getInstance(Context context) {
if (instance == null) {
instance = new ImageLoaderUtil(context);
}
return instance;
}
/**
* displayImg
*
* @param uri
* @param imageView
* @param options
* @param imageLoaderListener
*/
public void displayImg(String uri, ImageView imageView,
DisplayImageOptions options,
ImageLoadingListener imageLoaderListener) {
if (options != null && imageLoaderListener != null) {
imageLoader.displayImage(uri, imageView, options,
imageLoaderListener);
} else if (options != null) {
imageLoader.displayImage(uri, imageView, options, null);
} else if (imageLoaderListener != null) {
imageLoader.displayImage(uri, imageView, null, imageLoaderListener);
} else {
imageLoader.displayImage(uri, imageView, null, null);
}
}
/**
* image display options
*
* @return
*/
public static DisplayImageOptions getOptions() {
DisplayImageOptions options = new DisplayImageOptions.Builder()
.showImageOnLoading(R.drawable.ic_empty_img) // 设置图片下载期间显示的图片
.showImageForEmptyUri(R.drawable.ic_empty_img) // 设置图片Uri为空或是错误的时候显示的图片
.showImageOnFail(R.drawable.ic_empty_img) // 设置图片加载或解码过程中发生错误显示的图片
.imageScaleType(ImageScaleType.EXACTLY)//设置 图片的缩放方式 缩放类型
.cacheInMemory(true) // 设置下载的图片是否缓存在内存中
.cacheOnDisk(true) // 设置下载的图片是否缓存在SD卡中
// .displayer(new RoundedBitmapDisplayer(5)) // 设置成圆角图片,在有的手机里面会报错,所以先注释掉
.build();
return options;
}
public void clearImageCache() {
imageLoader.clearMemoryCache(); // 清除内存缓存
imageLoader.clearDiskCache(); // 清除本地缓存
}
}