文档格式(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(); // 清除本地缓存
	}


}