引言
在上篇博客中Android進(jìn)階之圖片加載框架搭建(一)消息機(jī)制原理分析及在并發(fā)中的應(yīng)用,結(jié)合Android消息機(jī)制構(gòu)建了圖片請(qǐng)求的任務(wù)管理模型,在此基礎(chǔ)上极景,本篇博客將說明一個(gè)簡單的圖片加載器的完全實(shí)現(xiàn)机断。主要有:
- 圖片的縮放
- 圖片的緩存
圖片的縮放
ImageSize:提供獲得ImageView寬高和縮放比的工具方法
package com.qicode.imageloaderdr.util;
/**
* Created by chenming on 16/9/26.
* 圖片寬高封裝
*/
public class ImageSize {
public int width;
public int height;
}
圖片縮放工具ImageSizeUtil:
package com.qicode.imageloaderdr.util;
import android.graphics.BitmapFactory;
import android.util.DisplayMetrics;
import android.view.ViewGroup;
import android.widget.ImageView;
import java.lang.reflect.Field;
/**
* Created by chenming on 16/9/26.
*/
public class ImageSizeUtil {
/**
* 獲得imageview的寬高
* @param imageView
* @return
*/
public static ImageSize getImageViewSize(ImageView imageView){
ImageSize result = new ImageSize();
DisplayMetrics displayMetrics = imageView.getContext().getResources()
.getDisplayMetrics();
ViewGroup.LayoutParams lp = imageView.getLayoutParams();
/**
* step1:獲得實(shí)際寬度
* step2:獲得布局指定寬度
* step3:獲得mMaxWidth
* step4:獲得屏幕寬度
* 高度也做同樣處理
*/
//高度
int width = imageView.getWidth();//step1
if(width <= 0){
width = lp.width;//step 2
}
if(width <= 0){
width = getMaxWidth(imageView);//step 3
}
if(width <= 0){
width = displayMetrics.widthPixels;//step 4
}
//高度
int height = imageView.getHeight();//step1
if(height <= 0){
height = lp.height;//step 2
}
if(height <= 0){
height = getMaxHeight(imageView);//step 3
}
if(height <= 0){
height = displayMetrics.heightPixels;//step 4
}
result.width = width;
result.height = height;
return result;
}
/**
* 反射獲得最大寬度
* @param imageView
* @return
*/
private static int getMaxWidth(ImageView imageView) {
try {
Class clazz = Class.forName("android.widget.ImageView");
Field field = clazz.getDeclaredField("mMaxWidth");
field.setAccessible(true);
return field.getInt(imageView);
} catch (ClassNotFoundException e) {
} catch (NoSuchFieldException e) {
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return 0;
}
/**
* 反射獲得最大高度
* @param imageView
* @return
*/
private static int getMaxHeight(ImageView imageView) {
try {
Class clazz = Class.forName("android.widget.ImageView");
Field field = clazz.getDeclaredField("mMaxHeight");
field.setAccessible(true);
return field.getInt(imageView);
} catch (ClassNotFoundException e) {
} catch (NoSuchFieldException e) {
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return 0;
}
/**
* 根據(jù)需求的寬和高以及圖片實(shí)際的寬和高計(jì)算SampleSize
* @param options
* @param reqWidth
* @param reqHeight
* @return
*/
public static int caculateInSampleSize(BitmapFactory.Options options, int reqWidth,
int reqHeight){
int rawWidth = options.outWidth;
int rawHeigh = options.outHeight;
int sampleRatio = 1;
if (rawWidth > reqWidth || rawHeigh > reqHeight){
int widthRatio = Math.round(rawWidth * 1.0f / reqWidth);
int heightRatio = Math.round(rawHeigh * 1.0f / reqHeight);
sampleRatio = Math.max(widthRatio, heightRatio);
}
return sampleRatio;
}
}
兩級(jí)緩存
磁盤緩存采用谷歌推薦的DiskLruCache,內(nèi)存緩存采用LruCache,在ImageLoader中的初始化代碼如下:
//緩存配置
private LruCache<String, Bitmap> mLruCache;
private DiskLruCache mDiskLruCache;
private static final long DISK_CACHE_SIZE = 1024 * 1024 * 50;
private boolean mIsDiskCacheEnable = true;//磁盤緩存開關(guān),默認(rèn)開啟
/**
* 內(nèi)存緩存
*/
private void initMemoryCache() {
int maxMemory = (int) Runtime.getRuntime().maxMemory();
int cacheMemory = maxMemory / 8;
mLruCache = new LruCache<String, Bitmap>(cacheMemory) {
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes() * value.getHeight();
}
};
}
/**
* 磁盤緩存初始化
*
* @param context
*/
private void initDiskCache(Context context) {
//磁盤緩存初始化
String dir = getImageCacheFile(context);
File file = new File(dir);
if (!file.exists()) {
file.mkdirs();
}
File diskCacheDir = new File(dir);
try {
mDiskLruCache = DiskLruCache.open(diskCacheDir, 1, 1, DISK_CACHE_SIZE);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* DiskLru緩存目錄
*
* @param context
* @return
*/
private String getImageCacheFile(Context context) {
return StringUtils
.getString(context.getCacheDir(), "/cacheImage/");
}
內(nèi)存緩存的讀寫操作:
private Bitmap getBitmapFromLruCache(String path) {
Bitmap bp = mLruCache.get(path);
return bp;
}
/**
* 將圖片加入LruCache
*
* @param path
* @param bm
*/
protected void addBitmapToLruCache(String path, Bitmap bm) {
if (getBitmapFromLruCache(path) == null) {
if (bm != null)
mLruCache.put(path, bm);
}
}
磁盤緩存的讀寫操作:
/**
* 寫入bitmap到磁盤
*
* @param path
* @param bp
*/
private void saveBitmapToDisk(String path, Bitmap bp) throws IOException {
boolean isFinish = false;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bp.compress(Bitmap.CompressFormat.PNG, 100, baos);
InputStream in = new ByteArrayInputStream(baos.toByteArray());
OutputStream os = null;
DiskLruCache.Editor editor = null;
String key = StringUtils.toMD5(path);
editor = mDiskLruCache.edit(key);
if (editor != null) {
os = editor.newOutputStream(0);
}
int rbyte;
if (os != null) {
while ((rbyte = in.read()) != -1) {
os.write(rbyte);
}
isFinish = true;
}
//提交
if (editor != null) {
if (isFinish) {
editor.commit();
} else {
editor.abort();
}
}
mDiskLruCache.flush();
}
/**
* 從磁盤緩存中取bp
*
* @param path
* @return
* @throws IOException
*/
private Bitmap getBitmapFromDisk(String path) throws IOException {
Bitmap bp = null;
String key = StringUtils.toMD5(path);
DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
if (snapshot != null) {
FileInputStream fis = (FileInputStream) snapshot.getInputStream(0);
bp = BitmapFactory.decodeStream(fis);
}
return bp;
}
注:磁盤緩存文件名為圖片URL地址的MD5值,實(shí)際項(xiàng)目中同一個(gè)URL可能用到不同大小的ImageView上做祝,因此需要考慮寬高因素囱晴,然后再M(fèi)D5。這里作為示例舟奠,只做簡單處理。
圖片下載單元ImageDownloader
package com.qicode.imageloaderdr.imageloader;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.widget.ImageView;
import com.qicode.imageloaderdr.util.BitmapUtils;
import com.qicode.imageloaderdr.util.ImageSize;
import com.qicode.imageloaderdr.util.ImageSizeUtil;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
/**
* Created by chenming on 16/9/26.
* 圖片下載器,圖片下載線程的執(zhí)行代碼
*/
public class ImageDownloader {
/**
* 無硬盤緩存,下載圖片到內(nèi)存
* @param urlStr
* @param imageView
* @return
* @throws IOException
*/
public static Bitmap downloadImgFromUrl(String urlStr, ImageView imageView) {
InputStream is = null;
try {
URL url = new URL(urlStr);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
is = new BufferedInputStream(connection.getInputStream());
is.mark(1024*1024);
//獲得網(wǎng)絡(luò)圖片的寬高
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.decodeStream(is, null, opts);
ImageSize imageSize = ImageSizeUtil.getImageViewSize(imageView);
opts.inSampleSize = ImageSizeUtil.caculateInSampleSize(opts, imageSize.width, imageSize.height);
opts.inJustDecodeBounds = false;
is.reset();
bitmap = BitmapFactory.decodeStream(is, null, opts);
connection.disconnect();
return bitmap;
} catch (MalformedURLException e) {
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (is != null)
is.close();
} catch (IOException e) {
}
}
return null;
}
/**
* 下載圖片到硬盤
* @param urlStr
* @param imageView
* @return
* @throws IOException
*/
public static Bitmap downloadImgFromUrlToFile(String urlStr, ImageView imageView, String fileName) {
InputStream is = null;
try {
URL url = new URL(urlStr);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
is = new BufferedInputStream(connection.getInputStream());
is.mark(1024*1024);
//獲得網(wǎng)絡(luò)圖片的寬高
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.decodeStream(is, null, opts);
ImageSize imageSize = ImageSizeUtil.getImageViewSize(imageView);
opts.inSampleSize = ImageSizeUtil.caculateInSampleSize(opts, imageSize.width, imageSize.height);
opts.inJustDecodeBounds = false;
is.reset();
bitmap = BitmapFactory.decodeStream(is, null, opts);
BitmapUtils.saveBitmap(imageView.getContext(), bitmap, fileName);
connection.disconnect();
return bitmap;
} catch (MalformedURLException e) {
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (is != null)
is.close();
} catch (IOException e) {
}
}
return null;
}
}
這里提供了下載到內(nèi)存和磁盤兩種方法。
ImageLoader實(shí)現(xiàn)
ImageLoader結(jié)構(gòu):
1.LRU內(nèi)存緩存
2.后臺(tái)線程,用于調(diào)度下載圖片的線程,通過構(gòu)建后臺(tái)消息模型及信號(hào)量實(shí)現(xiàn)并發(fā)下載
3.下載線程池
4.磁盤緩存
5.更新ImageView的handler
有關(guān)任務(wù)管理的代碼已經(jīng)在上篇博客詳細(xì)說明了實(shí)現(xiàn)原理驮配,這里只說明圖片加載的核心代碼。
初始化代碼:
多了緩存的配置:
initMemoryCache();
initDiskCache(context);
調(diào)用入口
/**
* 調(diào)用入口
*
* @param path 文件或者網(wǎng)絡(luò)地址
* @param imageView 目標(biāo)控件
* @param isFromNet true 本地圖片 false 加載本地圖片
*/
public void loadImage(final String path, final ImageView imageView, final boolean isFromNet) {
imageView.setTag(path);//避免錯(cuò)位
if (mUIHandler == null) {
mUIHandler = new Handler() {
public void handleMessage(Message msg) {
// 獲取得到圖片着茸,為imageview回調(diào)設(shè)置圖片
ImgBeanHolder holder = (ImgBeanHolder) msg.obj;
Bitmap bm = holder.bitmap;
ImageView imageview = holder.imageView;
String path = holder.path;
// 將path與getTag存儲(chǔ)路徑進(jìn)行比較
if (imageview.getTag().toString().equals(path)) {
imageview.setImageBitmap(bm);
}
}
};
}
//內(nèi)存緩存檢測(cè)
Bitmap bp = getBitmapFromLruCache(BitmapUtils.toMD5(path));
if (bp == null) {
addTask(buildTask(path, imageView, isFromNet));
} else {
refreshBitmap(path, imageView, bp);
}
}
private class ImgBeanHolder {
private Bitmap bitmap;
private ImageView imageView;
private String path;
}
說明:
1.UIHandler初始化
2.檢測(cè)內(nèi)存壮锻,如果有BP,則直接刷新UI涮阔;如果沒有則構(gòu)建任務(wù)猜绣,加入調(diào)度隊(duì)列
3.imageView.setTag(path)是為了避免在列表組件中顯示錯(cuò)位。
buildTask方法:
/**
* 獲取bp的核心代碼,沒有內(nèi)存緩存時(shí),下載網(wǎng)絡(luò)圖片敬特,加入磁盤和內(nèi)存緩存
*
* @return
*/
private Runnable buildTask(final String path, final ImageView imageView, final boolean isFromNet) {
return new Runnable() {
@Override
public void run() {
Bitmap bp = null;
if (isFromNet) {//加載網(wǎng)絡(luò)圖片
if (mIsDiskCacheEnable) {
//本地緩存處理
//disklrucache讀緩存
try {
bp = getBitmapFromDisk(path);
if (bp == null) {
//下載圖片
bp = ImageDownloader.downloadImgFromUrl(path, imageView);
if (bp != null) {
saveBitmapToDisk(path, bp);
}
}
} catch (IOException e) {
e.printStackTrace();
}
} else {
//直接從網(wǎng)絡(luò)加載
bp = ImageDownloader.downloadImgFromUrl(path, imageView);
}
} else {
//加載本地圖片
bp = loadImageFromLocal(path, imageView);
}
mBackLoopThreadSemaphore.release();//該任務(wù)執(zhí)行完成, 釋放并發(fā)加載圖片信號(hào)量
if (bp != null) {
//更新UI
refreshBitmap(path, imageView, bp);
//加入內(nèi)存緩存
addBitmapToLruCache(BitmapUtils.toMD5(path), bp);
}
}
};
}
說明:如果磁盤緩存開關(guān)開啟掰邢,則查看磁盤是否有已下載的BP,否則走網(wǎng)絡(luò)下載伟阔。其他邏輯注釋已經(jīng)很明了尸变,不再贅述。
加載本地圖片:
/**
* 加載本地圖片
*
* @param path
* @param imageView
* @return
*/
private Bitmap loadImageFromLocal(final String path, final ImageView imageView) {
Bitmap bm;
// 加載圖片
// 圖片的壓縮
// 1减俏、獲得圖片需要顯示的大小
ImageSize imageSize = ImageSizeUtil.getImageViewSize(imageView);
// 2、壓縮圖片
bm = decodeSampledBitmapFromPath(path, imageSize.width,
imageSize.height);
return bm;
}
/**
* 壓縮本地圖片
*
* @param path
* @param width
* @param height
* @return
*/
private Bitmap decodeSampledBitmapFromPath(String path, int width, int height) {
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.decodeFile(path, opts);
opts.inSampleSize = ImageSizeUtil.caculateInSampleSize(opts, width, height);
opts.inJustDecodeBounds = false;
bitmap = BitmapFactory.decodeFile(path, opts);
return bitmap;
}
最后用到的工具類代碼
package com.qicode.imageloaderdr.util;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Environment;
import android.text.TextUtils;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* Created by chenming on 16/10/27.
*/
public class BitmapUtils {
/**
* 將bitmap存成文件
*/
public static String saveBitmap(Context context, Bitmap bitmap, String fileName) {
if (context == null || bitmap == null || TextUtils.isEmpty(fileName)) {
return null;
}
String ret = null;
OutputStream fos = null;
try {
Uri uri = Uri.fromFile(new File(BitmapUtils.getTempSaveDir(context) + fileName));
File file = new File(uri.getPath());
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
fos = context.getContentResolver().openOutputStream(uri);
if (fos != null) {
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.flush();
}
ret = getTempSaveDir(context) + fileName;
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return ret;
}
/**
* 獲得緩存目錄路徑
*
* @param context
* @return
*/
public static String getTempSaveDir(Context context) {
String userDir;
userDir = context.getCacheDir().getAbsolutePath();
File file = new File(userDir);
if (!file.exists()) {
file.mkdirs();
}
return file.getAbsolutePath() + "/";
}
/**
* 將字符串轉(zhuǎn)成MD5值
*/
public static String toMD5(String string) {
byte[] hash;
try {
hash = MessageDigest.getInstance("MD5").digest(string.getBytes("UTF-8"));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}
StringBuilder hex = new StringBuilder(hash.length * 2);
for (byte b : hash) {
if ((b & 0xFF) < 0x10) {
hex.append("0");
}
hex.append(Integer.toHexString(b & 0xFF));
}
return hex.toString();
}
}
package com.qicode.imageloaderdr.util;
import android.app.Activity;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.CharacterStyle;
import android.text.style.ForegroundColorSpan;
import android.util.Base64;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created by huyongsheng on 2014/7/18.
*/
public class StringUtils {
/**
* 生成google play連接地址
*/
public static String getGooglePlayString(Activity activity, String packageName) {
return getGooglePlayString(packageName, "flip", activity.getPackageName());
}
/**
* 生成google play連接地址
*/
public static String getGooglePlayString(String packageName, String source, String medium) {
return StringUtils
.getString("market://details?id=", packageName, "&referrer=", "utm_source%3D", source, "%26utm_medium%3D",
medium);
}
/**
* 最優(yōu)化String的構(gòu)建
*/
public static String getString(Object... objects) {
StringBuffer buffer = new StringBuffer();
for (Object object : objects) {
buffer.append(object);
}
return buffer.toString();
}
/**
* 得到配置文件中的MetaData數(shù)據(jù)
*/
public static String getMetaData(Context context, String keyName) {
try {
ApplicationInfo info = context.getPackageManager().getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
Bundle bundle = info.metaData;
Object value = bundle.get(keyName);
if (value != null) {
return value.toString();
} else {
return null;
}
} catch (NameNotFoundException e) {
return null;
}
}
/**
* 獲取package信息
*/
public static PackageInfo getPackageInfo(Context context) throws NameNotFoundException {
return context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
}
/**
* 生成base64編碼
*/
public static String encodeBase64(String string) {
return Base64.encodeToString(string.getBytes(), Base64.NO_WRAP);
}
/**
* base64解碼
*/
public static String decodeBase64(String string) {
String result = null;
if (!StringUtils.isNullOrEmpty(string)) {
try {
result = new String(Base64.decode(string, Base64.NO_WRAP), "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
}
return result;
}
/**
* 對(duì)double數(shù)據(jù)進(jìn)行截?cái)? */
public static String cutDouble0(double value) {
DecimalFormat format = new DecimalFormat("##0");
return format.format(value);
}
/**
* 對(duì)double數(shù)據(jù)進(jìn)行截?cái)? */
public static String cutFloat0(float value) {
DecimalFormat format = new DecimalFormat("##0");
return format.format(value);
}
/**
* 判斷String是否為空
*/
public static boolean isNullOrEmpty(String inputString) {
return null == inputString || inputString.trim().equals("");
}
/**
* 判斷bytes是否為空
*/
public static boolean isNullOrEmpty(byte[] bytes) {
return null == bytes || bytes.length == 0;
}
/**
* 獲取post請(qǐng)求中的參數(shù)
*/
public static String getPostParams(String preString, Object object) {
String result = getString(preString, "{");
boolean isFirst = true;
// 獲取object對(duì)象對(duì)應(yīng)類中的所有屬性域
Field[] fields = object.getClass().getDeclaredFields();
for (Field field : fields) {
// 對(duì)于每個(gè)屬性碱工,獲取屬性名
String varName = field.getName();
try {
// 獲取原來的訪問控制權(quán)限
boolean accessFlag = field.isAccessible();
// 修改訪問控制權(quán)限
field.setAccessible(true);
// 獲取在對(duì)象object中屬性field對(duì)應(yīng)的對(duì)象中的變量
Object value = field.get(object);
// 生成參數(shù),其實(shí)跟get的URL中'?'后的參數(shù)字符串一致
if (isFirst) {
if (value instanceof String) {
result += getString("\"", URLEncoder.encode(varName, "utf-8"), "\":\"",
URLEncoder.encode(String.valueOf(value), "utf-8"), "\"");
} else {
result += getString("\"", URLEncoder.encode(varName, "utf-8"), "\":",
URLEncoder.encode(String.valueOf(value), "utf-8"));
}
isFirst = false;
} else {
if (value instanceof String) {
result += getString(",\"", URLEncoder.encode(varName, "utf-8"), "\":\"",
URLEncoder.encode(String.valueOf(value), "utf-8"), "\"");
} else {
result += getString(",\"", URLEncoder.encode(varName, "utf-8"), "\":",
URLEncoder.encode(String.valueOf(value), "utf-8"));
}
}
// 恢復(fù)訪問控制權(quán)限
field.setAccessible(accessFlag);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
result += "}";
return result;
}
/**
* 獲取post請(qǐng)求中的參數(shù)
*/
public static String getSimplePostParams(Object object) {
String result = "";
boolean isFirst = true;
// 獲取object對(duì)象對(duì)應(yīng)類中的所有屬性域
Field[] fields = object.getClass().getDeclaredFields();
for (Field field : fields) {
// 對(duì)于每個(gè)屬性娃承,獲取屬性名
String varName = field.getName();
try {
// 獲取原來的訪問控制權(quán)限
boolean accessFlag = field.isAccessible();
// 修改訪問控制權(quán)限
field.setAccessible(true);
// 獲取在對(duì)象object中屬性field對(duì)應(yīng)的對(duì)象中的變量
Object value = field.get(object);
// 生成參數(shù),其實(shí)跟get的URL中'?'后的參數(shù)字符串一致
if (value != null) {
if (isFirst) {
result += getString(URLEncoder.encode(varName, "utf-8"), "=", URLEncoder.encode(String.valueOf(value), "utf-8"));
isFirst = false;
} else {
result +=
getString("&", URLEncoder.encode(varName, "utf-8"), "=", URLEncoder.encode(String.valueOf(value), "utf-8"));
}
}
// 恢復(fù)訪問控制權(quán)限
field.setAccessible(accessFlag);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
return result;
}
/**
* 使用sha加密
*/
public static String getSHA(String val) {
MessageDigest md5 = null;
try {
md5 = MessageDigest.getInstance("SHA-1");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
md5.update(val.getBytes());
byte[] m = md5.digest();//加密
return getString(m);
}
/**
* 手機(jī)號(hào)合法性校驗(yàn)
*/
public static boolean checkPhoneNumber(String value) {
// String regExp = "^((13[0-9])|(15[^4,\\D])|(18[0-9])|(147))\\d{8}$";
String regExp = "^1\\d{10}$";
Pattern p = Pattern.compile(regExp);
Matcher m = p.matcher(value);
return m.find();
}
/**
* 正則檢測(cè)
*
* @param content
* @param format
* @return
*/
public static boolean checkStringFormat(String content, String format) {
Pattern p = Pattern.compile(format);
Matcher m = p.matcher(content);
return m.find();
}
public static boolean isNum(String str) {
Pattern pattern = Pattern.compile("[0-9]*");
return pattern.matcher(str).matches();
}
/**
* 將字符串轉(zhuǎn)成MD5值
*/
public static String toMD5(String string) {
byte[] hash;
try {
hash = MessageDigest.getInstance("MD5").digest(string.getBytes("UTF-8"));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}
StringBuilder hex = new StringBuilder(hash.length * 2);
for (byte b : hash) {
if ((b & 0xFF) < 0x10) {
hex.append("0");
}
hex.append(Integer.toHexString(b & 0xFF));
}
return hex.toString();
}
/**
* 獲取該輸入流的MD5值
*
* @throws NoSuchAlgorithmException
* @throws IOException
*/
public static String getMD5(InputStream is) throws NoSuchAlgorithmException, IOException {
StringBuffer md5 = new StringBuffer();
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] dataBytes = new byte[1024];
int read;
while ((read = is.read(dataBytes)) != -1) {
md.update(dataBytes, 0, read);
}
byte[] bytes = md.digest();
// convert the byte to hex format
for (byte b : bytes) {
md5.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
}
return md5.toString();
}
public static String getPrice(int price) {
DecimalFormat format = new DecimalFormat("###0.00");
float money = price / 100.0f;
return format.format(money);
}
public static boolean isValidEnglish(String name) {
String noSpaceName = name.replace(" ", "");
String reg = "^[A-Za-z]+$";
Matcher m = Pattern.compile(reg).matcher(noSpaceName);
return m.find();
}
/**
* 實(shí)現(xiàn)文本復(fù)制功能
*/
public static void copy(Context context, String content) {
ClipboardManager cmb = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
cmb.setText(content.trim());
}
/**
* 實(shí)現(xiàn)粘貼功能
*/
public static String paste(Context context) {
ClipboardManager cmb = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
return cmb.getText().toString().trim();
}
public static String dateFormat(long timeStamp, String dateFormat) {
SimpleDateFormat sdf = new SimpleDateFormat(dateFormat, Locale.CHINA);
Date date = new Date(timeStamp * 1000);
return sdf.format(date);
}
/**
* 部分編碼后的url
* @param url
* @return
*/
public static String restoreEncodeUrl(String url) {
//修正反斜杠為斜杠
url = url.replace("\\", "/");
//使用長文本代替要保留字符串
url = url.replace(":", "_*colon*_")
.replace("/", "_*slash*_")
.replace("\\", "_*backslash*_")
.replace(" ", "_*blank*_")
.replace("?", "_*question*_")
.replace("=", "_*equal*_")
.replace(";", "_*semicolon*_");
//進(jìn)行編碼
try {
url = URLEncoder.encode(url, "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
url = url.replace("_*colon*_", ":")
.replace("_*slash*_", "/")
.replace("_*backslash*_", "\\")
.replace("_*blank*_", "%20")
.replace("_*question*_", "?")
.replace("_*equal*_", "=")
.replace("_*semicolon*_", ";");
return url;
}
/**
* spannable str
*
* @param context
* @param fullStr
* @param highLightStr
* @param colorId
* @return
*/
public static SpannableStringBuilder getSpannable(Context context, String fullStr, String highLightStr, int colorId, int textSizeId) {
int mStart;
int mEnd;
if (!TextUtils.isEmpty(fullStr) && !TextUtils.isEmpty(highLightStr)) {
if (fullStr.contains(highLightStr)) {
/*
* 返回highlightStr字符串wholeStr字符串中第一次出現(xiàn)處的索引奏夫。
*/
mStart = fullStr.indexOf(highLightStr);
mEnd = mStart + highLightStr.length();
} else {
return new SpannableStringBuilder(fullStr);
}
} else {
return new SpannableStringBuilder(fullStr);
}
SpannableStringBuilder spBuilder = new SpannableStringBuilder(fullStr);
int color = ContextCompat.getColor(context, colorId);
CharacterStyle charaStyle = new ForegroundColorSpan(color);//顏色
spBuilder.setSpan(charaStyle, mStart, mEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
spBuilder.setSpan(new AbsoluteSizeSpan(textSizeId), mStart, mEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return spBuilder;
}
/**
* 大小不一樣的String
* @param context
* @param fullStr
* @param highLightStr
* @return
*/
public static SpannableStringBuilder getTextSizeSpannable(Context context, String fullStr, String highLightStr, int textSizeId, int colorId) {
int mStart;
int mEnd;
if (!TextUtils.isEmpty(fullStr) && !TextUtils.isEmpty(highLightStr)) {
if (fullStr.contains(highLightStr)) {
/*
* 返回highlightStr字符串wholeStr字符串中第一次出現(xiàn)處的索引。
*/
mStart = fullStr.indexOf(highLightStr);
mEnd = mStart + highLightStr.length();
} else {
return new SpannableStringBuilder(fullStr);
}
} else {
return new SpannableStringBuilder(fullStr);
}
SpannableStringBuilder spBuilder = new SpannableStringBuilder(fullStr);
spBuilder.setSpan(new AbsoluteSizeSpan(textSizeId), mStart, mEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
int color = ContextCompat.getColor(context, colorId);
CharacterStyle charaStyle = new ForegroundColorSpan(color);//顏色
spBuilder.setSpan(charaStyle, 0, fullStr.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return spBuilder;
}
}
總結(jié):這里只是實(shí)現(xiàn)了一個(gè)輕量型的圖片加載框架历筝,沒有考慮到可擴(kuò)展性(如Cache的設(shè)計(jì)酗昼、圖片的加載監(jiān)聽、BP處理接口等等),但足以讓你了解它的核心實(shí)現(xiàn)原理和方法梳猪。希望對(duì)讀者有幫助麻削!GitHub地址