實(shí)例詳解Android快速開發(fā)工具類總結(jié)
一、日志工具類 Log.java
public class L
{
private L()
{
/* 不可被實(shí)例化 */
throw new UnsupportedOperationException("Cannot be instantiated!");
}
// 是否需要打印bug,可以在application的onCreate函數(shù)里面初始化
public static boolean isDebug = true;
private static final String TAG = "DefaultTag";
// 下面四個(gè)是默認(rèn)tag的函數(shù)
public static void i(String msg)
{
if (isDebug)
Log.i(TAG, msg);
}
public static void d(String msg)
{
if (isDebug)
Log.d(TAG, msg);
}
public static void e(String msg)
{
if (isDebug)
Log.e(TAG, msg);
}
public static void v(String msg)
{
if (isDebug)
Log.v(TAG, msg);
}
// 下面是傳入自定義tag的函數(shù)
public static void i(String tag, String msg)
{
if (isDebug)
Log.i(tag, msg);
}
public static void d(String tag, String msg)
{
if (isDebug)
Log.i(tag, msg);
}
public static void e(String tag, String msg)
{
if (isDebug)
Log.i(tag, msg);
}
public static void v(String tag, String msg)
{
if (isDebug)
Log.i(tag, msg);
}
}
二、Toast統(tǒng)一管理類 Tost.java
public class T
{
private T()
{
/* cannot be instantiated */
throw new UnsupportedOperationException("cannot be instantiated");
}
public static boolean isShow = true;
/**
* 短時(shí)間顯示Toast
*/
public static void showShort(Context context, CharSequence message)
{
if (isShow)
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
}
/**
* 短時(shí)間顯示Toast
* @param message 要顯示的字符串資源的id
*/
public static void showShort(Context context, int message)
{
if (isShow)
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
}
/**
* 長時(shí)間顯示Toast
*/
public static void showLong(Context context, CharSequence message)
{
if (isShow)
Toast.makeText(context, message, Toast.LENGTH_LONG).show();
}
/**
* 長時(shí)間顯示Toast
*/
public static void showLong(Context context, int message)
{
if (isShow)
Toast.makeText(context, message, Toast.LENGTH_LONG).show();
}
/**
* 自定義顯示Toast時(shí)間
*/
public static void show(Context context, CharSequence message, int duration)
{
if (isShow)
Toast.makeText(context, message, duration).show();
}
/**
* 自定義顯示Toast時(shí)間
*/
public static void show(Context context, int message, int duration)
{
if (isShow)
Toast.makeText(context, message, duration).show();
}
}
三、SharedPreferences封裝類 SPUtils.java 和 PreferencesUtils.java
1. SPUtils.java
public class SPUtils
{
/**
* 保存在手機(jī)里面的文件名
*/
public static final String FILE_NAME = "share_data";
/**
* 保存數(shù)據(jù)的方法,我們需要拿到保存數(shù)據(jù)的具體類型,然后根據(jù)類型調(diào)用不同的保存方法
*
* @param context
* @param key
* @param object
*/
public static void put(Context context, String key, Object object)
{
SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
if (object instanceof String)
{
editor.putString(key, (String) object);
} else if (object instanceof Integer)
{
editor.putInt(key, (Integer) object);
} else if (object instanceof Boolean)
{
editor.putBoolean(key, (Boolean) object);
} else if (object instanceof Float)
{
editor.putFloat(key, (Float) object);
} else if (object instanceof Long)
{
editor.putLong(key, (Long) object);
} else
{
editor.putString(key, object.toString());
}
SharedPreferencesCompat.apply(editor);
}
/**
* 得到保存數(shù)據(jù)的方法,我們根據(jù)默認(rèn)值得到保存的數(shù)據(jù)的具體類型,然后調(diào)用相對(duì)于的方法獲取值
*
* @param context
* @param key
* @param defaultObject
* @return
*/
public static Object get(Context context, String key, Object defaultObject)
{
SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
Context.MODE_PRIVATE);
if (defaultObject instanceof String)
{
return sp.getString(key, (String) defaultObject);
} else if (defaultObject instanceof Integer)
{
return sp.getInt(key, (Integer) defaultObject);
} else if (defaultObject instanceof Boolean)
{
return sp.getBoolean(key, (Boolean) defaultObject);
} else if (defaultObject instanceof Float)
{
return sp.getFloat(key, (Float) defaultObject);
} else if (defaultObject instanceof Long)
{
return sp.getLong(key, (Long) defaultObject);
}
return null;
}
/**
* 移除某個(gè)key值已經(jīng)對(duì)應(yīng)的值
* @param context
* @param key
*/
public static void remove(Context context, String key)
{
SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.remove(key);
SharedPreferencesCompat.apply(editor);
}
/**
* 清除所有數(shù)據(jù)
* @param context
*/
public static void clear(Context context)
{
SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.clear();
SharedPreferencesCompat.apply(editor);
}
/**
* 查詢某個(gè)key是否已經(jīng)存在
* @param context
* @param key
* @return
*/
public static boolean contains(Context context, String key)
{
SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
Context.MODE_PRIVATE);
return sp.contains(key);
}
/**
* 返回所有的鍵值對(duì)
*
* @param context
* @return
*/
public static Map<String, ?> getAll(Context context)
{
SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
Context.MODE_PRIVATE);
return sp.getAll();
}
/**
* 創(chuàng)建一個(gè)解決SharedPreferencesCompat.apply方法的一個(gè)兼容類
*
* @author zhy
*
*/
private static class SharedPreferencesCompat
{
private static final Method sApplyMethod = findApplyMethod();
/**
* 反射查找apply的方法
*
* @return
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
private static Method findApplyMethod()
{
try
{
Class clz = SharedPreferences.Editor.class;
return clz.getMethod("apply");
} catch (NoSuchMethodException e)
{
}
return null;
}
/**
* 如果找到則使用apply執(zhí)行,否則使用commit
*
* @param editor
*/
public static void apply(SharedPreferences.Editor editor)
{
try
{
if (sApplyMethod != null)
{
sApplyMethod.invoke(editor);
return;
}
} catch (IllegalArgumentException e)
{
} catch (IllegalAccessException e)
{
} catch (InvocationTargetException e)
{
}
editor.commit();
}
}
}
對(duì)SharedPreference的使用做了建議的封裝,對(duì)外公布出put,get,remove,clear等等方法;
注意一點(diǎn),里面所有的commit操作使用了SharedPreferencesCompat.apply進(jìn)行了替代,目的是盡可能的使用apply代替commit.
首先說下為什么,因?yàn)閏ommit方法是同步的,并且我們很多時(shí)候的commit操作都是UI線程中,畢竟是IO操作,盡可能異步;
所以我們使用apply進(jìn)行替代,apply異步的進(jìn)行寫入;
但是apply相當(dāng)于commit來說是new API呢,為了更好的兼容,我們做了適配;
SharedPreferencesCompat也可以給大家創(chuàng)建兼容類提供了一定的參考
2. SPUtils.java
public class PreferencesUtils {
public static String PREFERENCE_NAME = "TrineaAndroidCommon";
private PreferencesUtils() {
throw new AssertionError();
}
/**
* put string preferences
*
* @param context
* @param key The name of the preference to modify
* @param value The new value for the preference
* @return True if the new values were successfully written to persistent storage.
*/
public static boolean putString(Context context, String key, String value) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.putString(key, value);
return editor.commit();
}
/**
* get string preferences
*
* @param context
* @param key The name of the preference to retrieve
* @return The preference value if it exists, or null. Throws ClassCastException if there is a preference with this
* name that is not a string
* @see #getString(Context, String, String)
*/
public static String getString(Context context, String key) {
return getString(context, key, null);
}
/**
* get string preferences
*
* @param context
* @param key The name of the preference to retrieve
* @param defaultValue Value to return if this preference does not exist
* @return The preference value if it exists, or defValue. Throws ClassCastException if there is a preference with
* this name that is not a string
*/
public static String getString(Context context, String key, String defaultValue) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
return settings.getString(key, defaultValue);
}
/**
* put int preferences
*
* @param context
* @param key The name of the preference to modify
* @param value The new value for the preference
* @return True if the new values were successfully written to persistent storage.
*/
public static boolean putInt(Context context, String key, int value) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.putInt(key, value);
return editor.commit();
}
/**
* get int preferences
*
* @param context
* @param key The name of the preference to retrieve
* @return The preference value if it exists, or -. Throws ClassCastException if there is a preference with this
* name that is not a int
* @see #getInt(Context, String, int)
*/
public static int getInt(Context context, String key) {
return getInt(context, key, -);
}
/**
* get int preferences
*
* @param context
* @param key The name of the preference to retrieve
* @param defaultValue Value to return if this preference does not exist
* @return The preference value if it exists, or defValue. Throws ClassCastException if there is a preference with
* this name that is not a int
*/
public static int getInt(Context context, String key, int defaultValue) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
return settings.getInt(key, defaultValue);
}
/**
* put long preferences
*
* @param context
* @param key The name of the preference to modify
* @param value The new value for the preference
* @return True if the new values were successfully written to persistent storage.
*/
public static boolean putLong(Context context, String key, long value) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.putLong(key, value);
return editor.commit();
}
/**
* get long preferences
*
* @param context
* @param key The name of the preference to retrieve
* @return The preference value if it exists, or -. Throws ClassCastException if there is a preference with this
* name that is not a long
* @see #getLong(Context, String, long)
*/
public static long getLong(Context context, String key) {
return getLong(context, key, -);
}
/**
* get long preferences
*
* @param context
* @param key The name of the preference to retrieve
* @param defaultValue Value to return if this preference does not exist
* @return The preference value if it exists, or defValue. Throws ClassCastException if there is a preference with
* this name that is not a long
*/
public static long getLong(Context context, String key, long defaultValue) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
return settings.getLong(key, defaultValue);
}
/**
* put float preferences
*
* @param context
* @param key The name of the preference to modify
* @param value The new value for the preference
* @return True if the new values were successfully written to persistent storage.
*/
public static boolean putFloat(Context context, String key, float value) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.putFloat(key, value);
return editor.commit();
}
/**
* get float preferences
*
* @param context
* @param key The name of the preference to retrieve
* @return The preference value if it exists, or -. Throws ClassCastException if there is a preference with this
* name that is not a float
* @see #getFloat(Context, String, float)
*/
public static float getFloat(Context context, String key) {
return getFloat(context, key, -);
}
/**
* get float preferences
*
* @param context
* @param key The name of the preference to retrieve
* @param defaultValue Value to return if this preference does not exist
* @return The preference value if it exists, or defValue. Throws ClassCastException if there is a preference with
* this name that is not a float
*/
public static float getFloat(Context context, String key, float defaultValue) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
return settings.getFloat(key, defaultValue);
}
/**
* put boolean preferences
*
* @param context
* @param key The name of the preference to modify
* @param value The new value for the preference
* @return True if the new values were successfully written to persistent storage.
*/
public static boolean putBoolean(Context context, String key, boolean value) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.putBoolean(key, value);
return editor.commit();
}
/**
* get boolean preferences, default is false
*
* @param context
* @param key The name of the preference to retrieve
* @return The preference value if it exists, or false. Throws ClassCastException if there is a preference with this
* name that is not a boolean
* @see #getBoolean(Context, String, boolean)
*/
public static boolean getBoolean(Context context, String key) {
return getBoolean(context, key, false);
}
/**
* get boolean preferences
*
* @param context
* @param key The name of the preference to retrieve
* @param defaultValue Value to return if this preference does not exist
* @return The preference value if it exists, or defValue. Throws ClassCastException if there is a preference with
* this name that is not a boolean
*/
public static boolean getBoolean(Context context, String key, boolean defaultValue) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
return settings.getBoolean(key, defaultValue);
}
}
四、單位轉(zhuǎn)換類 DensityUtils.java
public class DensityUtils
{
private DensityUtils()
{
/* cannot be instantiated */
throw new UnsupportedOperationException("cannot be instantiated");
}
/**
* dp轉(zhuǎn)px
*
* @param context
* @param val
* @return
*/
public static int dppx(Context context, float dpVal)
{
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
dpVal, context.getResources().getDisplayMetrics());
}
/**
* sp轉(zhuǎn)px
*
* @param context
* @param val
* @return
*/
public static int sppx(Context context, float spVal)
{
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
spVal, context.getResources().getDisplayMetrics());
}
/**
* px轉(zhuǎn)dp
*
* @param context
* @param pxVal
* @return
*/
public static float pxdp(Context context, float pxVal)
{
final float scale = context.getResources().getDisplayMetrics().density;
return (pxVal / scale);
}
/**
* px轉(zhuǎn)sp
*
* @param fontScale
* @param pxVal
* @return
*/
public static float pxsp(Context context, float pxVal)
{
return (pxVal / context.getResources().getDisplayMetrics().scaledDensity);
}
}
TypedValue:
Container for a dynamically typed data value. Primarily used with Resources for holding resource values.
applyDimension(int unit, float value, DisplayMetrics metrics):
Converts an unpacked complex data value holding a dimension to its final floating point value.
五、SD卡相關(guān)輔助類 SDCardUtils.java
public class SDCardUtils
{
private SDCardUtils()
{
/* cannot be instantiated */
throw new UnsupportedOperationException("cannot be instantiated");
}
/**
* 判斷SDCard是否可用
*
* @return
*/
public static boolean isSDCardEnable()
{
return Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED);
}
/**
* 獲取SD卡路徑
*
* @return
*/
public static String getSDCardPath()
{
return Environment.getExternalStorageDirectory().getAbsolutePath()
+ File.separator;
}
/**
* 獲取SD卡的剩余容量 單位byte
*
* @return
*/
public static long getSDCardAllSize()
{
if (isSDCardEnable())
{
StatFs stat = new StatFs(getSDCardPath());
// 獲取空閑的數(shù)據(jù)塊的數(shù)量
long availableBlocks = (long) stat.getAvailableBlocks() - ;
// 獲取單個(gè)數(shù)據(jù)塊的大?。╞yte)
long freeBlocks = stat.getAvailableBlocks();
return freeBlocks * availableBlocks;
}
return ;
}
/**
* 獲取指定路徑所在空間的剩余可用容量字節(jié)數(shù),單位byte
*
* @param filePath
* @return 容量字節(jié) SDCard可用空間,內(nèi)部存儲(chǔ)可用空間
*/
public static long getFreeBytes(String filePath)
{
// 如果是sd卡的下的路徑,則獲取sd卡可用容量
if (filePath.startsWith(getSDCardPath()))
{
filePath = getSDCardPath();
} else
{// 如果是內(nèi)部存儲(chǔ)的路徑,則獲取內(nèi)存存儲(chǔ)的可用容量
filePath = Environment.getDataDirectory().getAbsolutePath();
}
StatFs stat = new StatFs(filePath);
long availableBlocks = (long) stat.getAvailableBlocks() - ;
return stat.getBlockSize() * availableBlocks;
}
/**
* 獲取系統(tǒng)存儲(chǔ)路徑
*
* @return
*/
public static String getRootDirectoryPath()
{
return Environment.getRootDirectory().getAbsolutePath();
}
}
StatFs 是Android提供的一個(gè)類:
Retrieve overall information about the space on a filesystem. This is a wrapper for Unix statvfs().
檢索一個(gè)文件系統(tǒng)的整體信息空間。這是一個(gè)Unix statvfs() 包裝器
六、屏幕相關(guān)輔助類 ScreenUtils.java
public class ScreenUtils
{
private ScreenUtils()
{
/* cannot be instantiated */
throw new UnsupportedOperationException("cannot be instantiated");
}
/**
* 獲得屏幕高度
*
* @param context
* @return
*/
public static int getScreenWidth(Context context)
{
WindowManager wm = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(outMetrics);
return outMetrics.widthPixels;
}
/**
* 獲得屏幕寬度
*
* @param context
* @return
*/
public static int getScreenHeight(Context context)
{
WindowManager wm = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(outMetrics);
return outMetrics.heightPixels;
}
/**
* 獲得狀態(tài)欄的高度
*
* @param context
* @return
*/
public static int getStatusHeight(Context context)
{
int statusHeight = -;
try
{
Class<?> clazz = Class.forName("com.android.internal.R$dimen");
Object object = clazz.newInstance();
int height = Integer.parseInt(clazz.getField("status_bar_height")
.get(object).toString());
statusHeight = context.getResources().getDimensionPixelSize(height);
} catch (Exception e)
{
e.printStackTrace();
}
return statusHeight;
}
/**
* 獲取當(dāng)前屏幕截圖,包含狀態(tài)欄
*
* @param activity
* @return
*/
public static Bitmap snapShotWithStatusBar(Activity activity)
{
View view = activity.getWindow().getDecorView();
view.setDrawingCacheEnabled(true);
view.buildDrawingCache();
Bitmap bmp = view.getDrawingCache();
int width = getScreenWidth(activity);
int height = getScreenHeight(activity);
Bitmap bp = null;
bp = Bitmap.createBitmap(bmp, , , width, height);
view.destroyDrawingCache();
return bp;
}
/**
* 獲取當(dāng)前屏幕截圖,不包含狀態(tài)欄
*
* @param activity
* @return
*/
public static Bitmap snapShotWithoutStatusBar(Activity activity)
{
View view = activity.getWindow().getDecorView();
view.setDrawingCacheEnabled(true);
view.buildDrawingCache();
Bitmap bmp = view.getDrawingCache();
Rect frame = new Rect();
activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
int statusBarHeight = frame.top;
int width = getScreenWidth(activity);
int height = getScreenHeight(activity);
Bitmap bp = null;
bp = Bitmap.createBitmap(bmp, , statusBarHeight, width, height
- statusBarHeight);
view.destroyDrawingCache();
return bp;
}
}
七、App相關(guān)輔助類 APPUtils.java
public class AppUtils
{
private AppUtils()
{
/* cannot be instantiated */
throw new UnsupportedOperationException("cannot be instantiated");
}
/**
* 獲取應(yīng)用程序名稱
*/
public static String getAppName(Context context)
{
try
{
PackageManager packageManager = context.getPackageManager();
PackageInfo packageInfo = packageManager.getPackageInfo(
context.getPackageName(), );
int labelRes = packageInfo.applicationInfo.labelRes;
return context.getResources().getString(labelRes);
} catch (NameNotFoundException e)
{
e.printStackTrace();
}
return null;
}
/**
* [獲取應(yīng)用程序版本名稱信息]
*
* @param context
* @return 當(dāng)前應(yīng)用的版本名稱
*/
public static String getVersionName(Context context)
{
try
{
PackageManager packageManager = context.getPackageManager();
PackageInfo packageInfo = packageManager.getPackageInfo(
context.getPackageName(), );
return packageInfo.versionName;
} catch (NameNotFoundException e)
{
e.printStackTrace();
}
return null;
}
}
八、軟鍵盤相關(guān)輔助類KeyBoardUtils.java
/**
* 打開或關(guān)閉軟鍵盤
*/
public class KeyBoardUtils
{
/**
* 打卡軟鍵盤
*
* @param mEditText 輸入框
* @param mContext 上下文
*/
public static void openKeybord(EditText mEditText, Context mContext)
{
InputMethodManager imm = (InputMethodManager) mContext
.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(mEditText, InputMethodManager.RESULT_SHOWN);
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED,
InputMethodManager.HIDE_IMPLICIT_ONLY);
}
/**
* 關(guān)閉軟鍵盤
*
* @param mEditText 輸入框
* @param mContext 上下文
*/
public static void closeKeybord(EditText mEditText, Context mContext)
{
InputMethodManager imm = (InputMethodManager) mContext
.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mEditText.getWindowToken(), );
}
}
九、網(wǎng)絡(luò)相關(guān)輔助類 NetUtils.java
public class NetUtils
{
private NetUtils()
{
/* cannot be instantiated */
throw new UnsupportedOperationException("cannot be instantiated");
}
/**
* 判斷網(wǎng)絡(luò)是否連接
*/
public static boolean isConnected(Context context)
{
ConnectivityManager connectivity = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
if (null != connectivity)
{
NetworkInfo info = connectivity.getActiveNetworkInfo();
if (null != info && info.isConnected())
{
if (info.getState() == NetworkInfo.State.CONNECTED)
{
return true;
}
}
}
return false;
}
/**
* 判斷是否是wifi連接
*/
public static boolean isWifi(Context context)
{
ConnectivityManager cm = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
if (cm == null)
return false;
return cm.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI;
}
/**
* 打開網(wǎng)絡(luò)設(shè)置界面
*/
public static void openSetting(Activity activity)
{
Intent intent = new Intent("/");
ComponentName cm = new ComponentName("com.android.settings",
"com.android.settings.WirelessSettings");
intent.setComponent(cm);
intent.setAction("android.intent.action.VIEW");
activity.startActivityForResult(intent, );
}
}
十、Http相關(guān)輔助類 HttpUtils.java
/**
* Http請(qǐng)求的工具類
*/
public class HttpUtils
{
private static final int TIMEOUT_IN_MILLIONS = ;
public interface CallBack
{
void onRequestComplete(String result);
}
/**
* 異步的Get請(qǐng)求
*
* @param urlStr
* @param callBack
*/
public static void doGetAsyn(final String urlStr, final CallBack callBack)
{
new Thread()
{
public void run()
{
try
{
String result = doGet(urlStr);
if (callBack != null)
{
callBack.onRequestComplete(result);
}
} catch (Exception e)
{
e.printStackTrace();
}
};
}.start();
}
/**
* 異步的Post請(qǐng)求
* @param urlStr
* @param params
* @param callBack
* @throws Exception
*/
public static void doPostAsyn(final String urlStr, final String params,
final CallBack callBack) throws Exception
{
new Thread()
{
public void run()
{
try
{
String result = doPost(urlStr, params);
if (callBack != null)
{
callBack.onRequestComplete(result);
}
} catch (Exception e)
{
e.printStackTrace();
}
};
}.start();
}
/**
* Get請(qǐng)求,獲得返回?cái)?shù)據(jù)
*
* @param urlStr
* @return
* @throws Exception
*/
public static String doGet(String urlStr)
{
URL url = null;
HttpURLConnection conn = null;
InputStream is = null;
ByteArrayOutputStream baos = null;
try
{
url = new URL(urlStr);
conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(TIMEOUT_IN_MILLIONS);
conn.setConnectTimeout(TIMEOUT_IN_MILLIONS);
conn.setRequestMethod("GET");
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
if (conn.getResponseCode() == )
{
is = conn.getInputStream();
baos = new ByteArrayOutputStream();
int len = -;
byte[] buf = new byte[];
while ((len = is.read(buf)) != -)
{
baos.write(buf, , len);
}
baos.flush();
return baos.toString();
} else
{
throw new RuntimeException(" responseCode is not ... ");
}
} catch (Exception e)
{
e.printStackTrace();
} finally
{
try
{
if (is != null)
is.close();
} catch (IOException e)
{
}
try
{
if (baos != null)
baos.close();
} catch (IOException e)
{
}
conn.disconnect();
}
return null ;
}
/**
* 向指定 URL 發(fā)送POST方法的請(qǐng)求
*
* @param url
* 發(fā)送請(qǐng)求的 URL
* @param param
* 請(qǐng)求參數(shù),請(qǐng)求參數(shù)應(yīng)該是 name=value&name=value 的形式。
* @return 所代表遠(yuǎn)程資源的響應(yīng)結(jié)果
* @throws Exception
*/
public static String doPost(String url, String param)
{
PrintWriter out = null;
BufferedReader in = null;
String result = "";
try
{
URL realUrl = new URL(url);
// 打開和URL之間的連接
HttpURLConnection conn = (HttpURLConnection) realUrl
.openConnection();
// 設(shè)置通用的請(qǐng)求屬性
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
conn.setRequestProperty("charset", "utf-");
conn.setUseCaches(false);
// 發(fā)送POST請(qǐng)求必須設(shè)置如下兩行
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setReadTimeout(TIMEOUT_IN_MILLIONS);
conn.setConnectTimeout(TIMEOUT_IN_MILLIONS);
if (param != null && !param.trim().equals(""))
{
// 獲取URLConnection對(duì)象對(duì)應(yīng)的輸出流
out = new PrintWriter(conn.getOutputStream());
// 發(fā)送請(qǐng)求參數(shù)
out.print(param);
// flush輸出流的緩沖
out.flush();
}
// 定義BufferedReader輸入流來讀取URL的響應(yīng)
in = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
String line;
while ((line = in.readLine()) != null)
{
result += line;
}
} catch (Exception e)
{
e.printStackTrace();
}
// 使用finally塊來關(guān)閉輸出流、輸入流
finally
{
try
{
if (out != null)
{
out.close();
}
if (in != null)
{
in.close();
}
} catch (IOException ex)
{
ex.printStackTrace();
}
}
return result;
}
}
十一、時(shí)間工具類 TimeUtils.java
public class TimeUtils {
public static final SimpleDateFormat DEFAULT_DATE_FORMAT =
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static final SimpleDateFormat DATE_FORMAT_DATE =
new SimpleDateFormat("yyyy-MM-dd");
private TimeUtils() {
throw new AssertionError();
}
/**
* long time to string
*
* @param timeInMillis
* @param dateFormat
* @return
*/
public static String getTime(long timeInMillis, SimpleDateFormat dateFormat) {
return dateFormat.format(new Date(timeInMillis));
}
/**
* long time to string, format is {@link #DEFAULT_DATE_FORMAT}
*
* @param timeInMillis
* @return
*/
public static String getTime(long timeInMillis) {
return getTime(timeInMillis, DEFAULT_DATE_FORMAT);
}
/**
* get current time in milliseconds
*
* @return
*/
public static long getCurrentTimeInLong() {
return System.currentTimeMillis();
}
/**
* get current time in milliseconds, format is {@link #DEFAULT_DATE_FORMAT}
*
* @return
*/
public static String getCurrentTimeInString() {
return getTime(getCurrentTimeInLong());
}
/**
* get current time in milliseconds
*
* @return
*/
public static String getCurrentTimeInString(SimpleDateFormat dateFormat) {
return getTime(getCurrentTimeInLong(), dateFormat);
}
}
十二、文件工具類 FileUtils.java
public class FileUtils {
public final static String FILE_EXTENSION_SEPARATOR = ".";
private FileUtils() {
throw new AssertionError();
}
/**
* read file
*
* @param filePath
* @param charsetName The name of a supported {@link java.nio.charset.Charset </code>charset<code>}
* @return if file not exist, return null, else return content of file
* @throws RuntimeException if an error occurs while operator BufferedReader
*/
public static StringBuilder readFile(String filePath, String charsetName) {
File file = new File(filePath);
StringBuilder fileContent = new StringBuilder("");
if (file == null || !file.isFile()) {
return null;
}
BufferedReader reader = null;
try {
InputStreamReader is = new InputStreamReader(new FileInputStream(file), charsetName);
reader = new BufferedReader(is);
String line = null;
while ((line = reader.readLine()) != null) {
if (!fileContent.toString().equals("")) {
fileContent.append("\r\n");
}
fileContent.append(line);
}
return fileContent;
} catch (IOException e) {
throw new RuntimeException("IOException occurred. ", e);
} finally {
IOUtils.close(reader);
}
}
/**
* write file
*
* @param filePath
* @param content
* @param append is append, if true, write to the end of file, else clear content of file and write into it
* @return return false if content is empty, true otherwise
* @throws RuntimeException if an error occurs while operator FileWriter
*/
public static boolean writeFile(String filePath, String content, boolean append) {
if (StringUtils.isEmpty(content)) {
return false;
}
FileWriter fileWriter = null;
try {
makeDirs(filePath);
fileWriter = new FileWriter(filePath, append);
fileWriter.write(content);
return true;
} catch (IOException e) {
throw new RuntimeException("IOException occurred. ", e);
} finally {
IOUtils.close(fileWriter);
}
}
/**
* write file
*
* @param filePath
* @param contentList
* @param append is append, if true, write to the end of file, else clear content of file and write into it
* @return return false if contentList is empty, true otherwise
* @throws RuntimeException if an error occurs while operator FileWriter
*/
public static boolean writeFile(String filePath, List<String> contentList, boolean append) {
if (ListUtils.isEmpty(contentList)) {
return false;
}
FileWriter fileWriter = null;
try {
makeDirs(filePath);
fileWriter = new FileWriter(filePath, append);
int i = ;
for (String line : contentList) {
if (i++ > ) {
fileWriter.write("\r\n");
}
fileWriter.write(line);
}
return true;
} catch (IOException e) {
throw new RuntimeException("IOException occurred. ", e);
} finally {
IOUtils.close(fileWriter);
}
}
/**
* write file, the string will be written to the begin of the file
*
* @param filePath
* @param content
* @return
*/
public static boolean writeFile(String filePath, String content) {
return writeFile(filePath, content, false);
}
/**
* write file, the string list will be written to the begin of the file
*
* @param filePath
* @param contentList
* @return
*/
public static boolean writeFile(String filePath, List<String> contentList) {
return writeFile(filePath, contentList, false);
}
/**
* write file, the bytes will be written to the begin of the file
*
* @param filePath
* @param stream
* @return
* @see {@link #writeFile(String, InputStream, boolean)}
*/
public static boolean writeFile(String filePath, InputStream stream) {
return writeFile(filePath, stream, false);
}
/**
* write file
*
* @param file the file to be opened for writing.
* @param stream the input stream
* @param append if <code>true</code>, then bytes will be written to the end of the file rather than the beginning
* @return return true
* @throws RuntimeException if an error occurs while operator FileOutputStream
*/
public static boolean writeFile(String filePath, InputStream stream, boolean append) {
return writeFile(filePath != null ? new File(filePath) : null, stream, append);
}
/**
* write file, the bytes will be written to the begin of the file
*
* @param file
* @param stream
* @return
* @see {@link #writeFile(File, InputStream, boolean)}
*/
public static boolean writeFile(File file, InputStream stream) {
return writeFile(file, stream, false);
}
/**
* write file
*
* @param file the file to be opened for writing.
* @param stream the input stream
* @param append if <code>true</code>, then bytes will be written to the end of the file rather than the beginning
* @return return true
* @throws RuntimeException if an error occurs while operator FileOutputStream
*/
public static boolean writeFile(File file, InputStream stream, boolean append) {
OutputStream o = null;
try {
makeDirs(file.getAbsolutePath());
o = new FileOutputStream(file, append);
byte data[] = new byte[];
int length = -;
while ((length = stream.read(data)) != -) {
o.write(data, , length);
}
o.flush();
return true;
} catch (FileNotFoundException e) {
throw new RuntimeException("FileNotFoundException occurred. ", e);
} catch (IOException e) {
throw new RuntimeException("IOException occurred. ", e);
} finally {
IOUtils.close(o);
IOUtils.close(stream);
}
}
/**
* move file
*
* @param sourceFilePath
* @param destFilePath
*/
public static void moveFile(String sourceFilePath, String destFilePath) {
if (TextUtils.isEmpty(sourceFilePath) || TextUtils.isEmpty(destFilePath)) {
throw new RuntimeException("Both sourceFilePath and destFilePath cannot be null.");
}
moveFile(new File(sourceFilePath), new File(destFilePath));
}
/**
* move file
*
* @param srcFile
* @param destFile
*/
public static void moveFile(File srcFile, File destFile) {
boolean rename = srcFile.renameTo(destFile);
if (!rename) {
copyFile(srcFile.getAbsolutePath(), destFile.getAbsolutePath());
deleteFile(srcFile.getAbsolutePath());
}
}
/**
* copy file
*
* @param sourceFilePath
* @param destFilePath
* @return
* @throws RuntimeException if an error occurs while operator FileOutputStream
*/
public static boolean copyFile(String sourceFilePath, String destFilePath) {
InputStream inputStream = null;
try {
inputStream = new FileInputStream(sourceFilePath);
} catch (FileNotFoundException e) {
throw new RuntimeException("FileNotFoundException occurred. ", e);
}
return writeFile(destFilePath, inputStream);
}
/**
* read file to string list, a element of list is a line
*
* @param filePath
* @param charsetName The name of a supported {@link java.nio.charset.Charset </code>charset<code>}
* @return if file not exist, return null, else return content of file
* @throws RuntimeException if an error occurs while operator BufferedReader
*/
public static List<String> readFileToList(String filePath, String charsetName) {
File file = new File(filePath);
List<String> fileContent = new ArrayList<String>();
if (file == null || !file.isFile()) {
return null;
}
BufferedReader reader = null;
try {
InputStreamReader is = new InputStreamReader(new FileInputStream(file), charsetName);
reader = new BufferedReader(is);
String line = null;
while ((line = reader.readLine()) != null) {
fileContent.add(line);
}
return fileContent;
} catch (IOException e) {
throw new RuntimeException("IOException occurred. ", e);
} finally {
IOUtils.close(reader);
}
}
/**
* get file name from path, not include suffix
*
* <pre>
* getFileNameWithoutExtension(null) = null
* getFileNameWithoutExtension("") = ""
* getFileNameWithoutExtension(" ") = " "
* getFileNameWithoutExtension("abc") = "abc"
* getFileNameWithoutExtension("a.mp") = "a"
* getFileNameWithoutExtension("a.b.rmvb") = "a.b"
* getFileNameWithoutExtension("c:\\") = ""
* getFileNameWithoutExtension("c:\\a") = "a"
* getFileNameWithoutExtension("c:\\a.b") = "a"
* getFileNameWithoutExtension("c:a.txt\\a") = "a"
* getFileNameWithoutExtension("/home/admin") = "admin"
* getFileNameWithoutExtension("/home/admin/a.txt/b.mp") = "b"
* </pre>
*
* @param filePath
* @return file name from path, not include suffix
* @see
*/
public static String getFileNameWithoutExtension(String filePath) {
if (StringUtils.isEmpty(filePath)) {
return filePath;
}
int extenPosi = filePath.lastIndexOf(FILE_EXTENSION_SEPARATOR);
int filePosi = filePath.lastIndexOf(File.separator);
if (filePosi == -) {
return (extenPosi == - ? filePath : filePath.substring(, extenPosi));
}
if (extenPosi == -) {
return filePath.substring(filePosi + );
}
return (filePosi < extenPosi ? filePath.substring(filePosi + , extenPosi) : filePath.substring(filePosi + ));
}
/**
* get file name from path, include suffix
*
* <pre>
* getFileName(null) = null
* getFileName("") = ""
* getFileName(" ") = " "
* getFileName("a.mp") = "a.mp"
* getFileName("a.b.rmvb") = "a.b.rmvb"
* getFileName("abc") = "abc"
* getFileName("c:\\") = ""
* getFileName("c:\\a") = "a"
* getFileName("c:\\a.b") = "a.b"
* getFileName("c:a.txt\\a") = "a"
* getFileName("/home/admin") = "admin"
* getFileName("/home/admin/a.txt/b.mp") = "b.mp"
* </pre>
*
* @param filePath
* @return file name from path, include suffix
*/
public static String getFileName(String filePath) {
if (StringUtils.isEmpty(filePath)) {
return filePath;
}
int filePosi = filePath.lastIndexOf(File.separator);
return (filePosi == -) ? filePath : filePath.substring(filePosi + );
}
/**
* get folder name from path
*
* <pre>
* getFolderName(null) = null
* getFolderName("") = ""
* getFolderName(" ") = ""
* getFolderName("a.mp") = ""
* getFolderName("a.b.rmvb") = ""
* getFolderName("abc") = ""
* getFolderName("c:\\") = "c:"
* getFolderName("c:\\a") = "c:"
* getFolderName("c:\\a.b") = "c:"
* getFolderName("c:a.txt\\a") = "c:a.txt"
* getFolderName("c:a\\b\\c\\d.txt") = "c:a\\b\\c"
* getFolderName("/home/admin") = "/home"
* getFolderName("/home/admin/a.txt/b.mp") = "/home/admin/a.txt"
* </pre>
*
* @param filePath
* @return
*/
public static String getFolderName(String filePath) {
if (StringUtils.isEmpty(filePath)) {
return filePath;
}
int filePosi = filePath.lastIndexOf(File.separator);
return (filePosi == -) ? "" : filePath.substring(, filePosi);
}
/**
* get suffix of file from path
*
* <pre>
* getFileExtension(null) = ""
* getFileExtension("") = ""
* getFileExtension(" ") = " "
* getFileExtension("a.mp") = "mp"
* getFileExtension("a.b.rmvb") = "rmvb"
* getFileExtension("abc") = ""
* getFileExtension("c:\\") = ""
* getFileExtension("c:\\a") = ""
* getFileExtension("c:\\a.b") = "b"
* getFileExtension("c:a.txt\\a") = ""
* getFileExtension("/home/admin") = ""
* getFileExtension("/home/admin/a.txt/b") = ""
* getFileExtension("/home/admin/a.txt/b.mp") = "mp"
* </pre>
*
* @param filePath
* @return
*/
public static String getFileExtension(String filePath) {
if (StringUtils.isBlank(filePath)) {
return filePath;
}
int extenPosi = filePath.lastIndexOf(FILE_EXTENSION_SEPARATOR);
int filePosi = filePath.lastIndexOf(File.separator);
if (extenPosi == -) {
return "";
}
return (filePosi >= extenPosi) ? "" : filePath.substring(extenPosi + );
}
/**
* Creates the directory named by the trailing filename of this file, including the complete directory path required
* to create this directory. <br/>
* <br/>
* <ul>
* <strong>Attentions:</strong>
* <li>makeDirs("C:\\Users\\Trinea") can only create users folder</li>
* <li>makeFolder("C:\\Users\\Trinea\\") can create Trinea folder</li>
* </ul>
*
* @param filePath
* @return true if the necessary directories have been created or the target directory already exists, false one of
* the directories can not be created.
* <ul>
* <li>if {@link FileUtils#getFolderName(String)} return null, return false</li>
* <li>if target directory already exists, return true</li>
* <li>return {@link java.io.File#makeFolder}</li>
* </ul>
*/
public static boolean makeDirs(String filePath) {
String folderName = getFolderName(filePath);
if (StringUtils.isEmpty(folderName)) {
return false;
}
File folder = new File(folderName);
return (folder.exists() && folder.isDirectory()) ? true : folder.mkdirs();
}
/**
* @param filePath
* @return
* @see #makeDirs(String)
*/
public static boolean makeFolders(String filePath) {
return makeDirs(filePath);
}
/**
* Indicates if this file represents a file on the underlying file system.
*
* @param filePath
* @return
*/
public static boolean isFileExist(String filePath) {
if (StringUtils.isBlank(filePath)) {
return false;
}
File file = new File(filePath);
return (file.exists() && file.isFile());
}
/**
* Indicates if this file represents a directory on the underlying file system.
*
* @param directoryPath
* @return
*/
public static boolean isFolderExist(String directoryPath) {
if (StringUtils.isBlank(directoryPath)) {
return false;
}
File dire = new File(directoryPath);
return (dire.exists() && dire.isDirectory());
}
/**
* delete file or directory
* <ul>
* <li>if path is null or empty, return true</li>
* <li>if path not exist, return true</li>
* <li>if path exist, delete recursion. return true</li>
* <ul>
*
* @param path
* @return
*/
public static boolean deleteFile(String path) {
if (StringUtils.isBlank(path)) {
return true;
}
File file = new File(path);
if (!file.exists()) {
return true;
}
if (file.isFile()) {
return file.delete();
}
if (!file.isDirectory()) {
return false;
}
for (File f : file.listFiles()) {
if (f.isFile()) {
f.delete();
} else if (f.isDirectory()) {
deleteFile(f.getAbsolutePath());
}
}
return file.delete();
}
/**
* get file size
* <ul>
* <li>if path is null or empty, return -</li>
* <li>if path exist and it is a file, return file size, else return -</li>
* <ul>
*
* @param path
* @return returns the length of this file in bytes. returns - if the file does not exist.
*/
public static long getFileSize(String path) {
if (StringUtils.isBlank(path)) {
return -;
}
File file = new File(path);
return (file.exists() && file.isFile() ? file.length() : -);
}
}
十三、assets和raw資源工具類 ResourceUtils.java
public class ResourceUtils {
private ResourceUtils() {
throw new AssertionError();
}
/**
* get an asset using ACCESS_STREAMING mode. This provides access to files that have been bundled with an
* application as assets -- that is, files placed in to the "assets" directory.
*
* @param context
* @param fileName The name of the asset to open. This name can be hierarchical.
* @return
*/
public static String geFileFromAssets(Context context, String fileName) {
if (context == null || StringUtils.isEmpty(fileName)) {
return null;
}
StringBuilder s = new StringBuilder("");
try {
InputStreamReader in = new InputStreamReader(context.getResources().getAssets().open(fileName));
BufferedReader br = new BufferedReader(in);
String line;
while ((line = br.readLine()) != null) {
s.append(line);
}
return s.toString();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
/**
* get content from a raw resource. This can only be used with resources whose value is the name of an asset files
* -- that is, it can be used to open drawable, sound, and raw resources; it will fail on string and color
* resources.
*
* @param context
* @param resId The resource identifier to open, as generated by the appt tool.
* @return
*/
public static String geFileFromRaw(Context context, int resId) {
if (context == null) {
return null;
}
StringBuilder s = new StringBuilder();
try {
InputStreamReader in = new InputStreamReader(context.getResources().openRawResource(resId));
BufferedReader br = new BufferedReader(in);
String line;
while ((line = br.readLine()) != null) {
s.append(line);
}
return s.toString();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
/**
* same to {@link ResourceUtils#geFileFromAssets(Context, String)}, but return type is List<String>
*
* @param context
* @param fileName
* @return
*/
public static List<String> geFileToListFromAssets(Context context, String fileName) {
if (context == null || StringUtils.isEmpty(fileName)) {
return null;
}
List<String> fileContent = new ArrayList<String>();
try {
InputStreamReader in = new InputStreamReader(context.getResources().getAssets().open(fileName));
BufferedReader br = new BufferedReader(in);
String line;
while ((line = br.readLine()) != null) {
fileContent.add(line);
}
br.close();
return fileContent;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
/**
* same to {@link ResourceUtils#geFileFromRaw(Context, int)}, but return type is List<String>
*
* @param context
* @param resId
* @return
*/
public static List<String> geFileToListFromRaw(Context context, int resId) {
if (context == null) {
return null;
}
List<String> fileContent = new ArrayList<String>();
BufferedReader reader = null;
try {
InputStreamReader in = new InputStreamReader(context.getResources().openRawResource(resId));
reader = new BufferedReader(in);
String line = null;
while ((line = reader.readLine()) != null) {
fileContent.add(line);
}
reader.close();
return fileContent;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
十四、單例工具類 SingletonUtils.java
public abstract class SingletonUtils<T> {
private T instance;
protected abstract T newInstance();
public final T getInstance() {
if (instance == null) {
synchronized (SingletonUtils.class) {
if (instance == null) {
instance = newInstance();
}
}
}
return instance;
}
}
十五、數(shù)據(jù)庫工具類 SqliteUtils.java
public class SqliteUtils {
private static volatile SqliteUtils instance;
private DbHelper dbHelper;
private SQLiteDatabase db;
private SqliteUtils(Context context) {
dbHelper = new DbHelper(context);
db = dbHelper.getWritableDatabase();
}
public static SqliteUtils getInstance(Context context) {
if (instance == null) {
synchronized (SqliteUtils.class) {
if (instance == null) {
instance = new SqliteUtils(context);
}
}
}
return instance;
}
public SQLiteDatabase getDb() {
return db;
}
}
相關(guān)文章
DownloadManager實(shí)現(xiàn)文件下載功能
這篇文章主要為大家詳細(xì)介紹了DownloadManager實(shí)現(xiàn)文件下載功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11
Android 使用Vitamio打造自己的萬能播放器(4)——本地播放(快捷搜索、數(shù)據(jù)存儲(chǔ))
本文主要介紹android Vitamio 本地播放功能(快捷搜索,數(shù)據(jù)存儲(chǔ)),這里提供實(shí)例代碼和效果圖,有需要的小伙伴可以參考下2016-07-07
簡單實(shí)現(xiàn)Android讀取網(wǎng)絡(luò)圖片到本地
這篇文章主要為大家詳細(xì)介紹了如何簡單實(shí)現(xiàn)Android讀取網(wǎng)絡(luò)圖片到本地的方法,感興趣的小伙伴們可以參考一下2016-08-08
Android實(shí)現(xiàn)LED發(fā)光字效果
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)LED發(fā)光字效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-03-03
Android如何判斷當(dāng)前點(diǎn)擊位置是否在圓的內(nèi)部
這篇文章主要為大家詳細(xì)介紹了Android如何判斷當(dāng)前點(diǎn)擊位置是否在圓的內(nèi)部,解析拖動(dòng)圓形控件之內(nèi)響應(yīng)觸摸事件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05
Android自定義控件實(shí)現(xiàn)圓形進(jìn)度CircleProgressBar
這篇文章主要為大家詳細(xì)介紹了Android自定義控件實(shí)現(xiàn)圓形進(jìn)度CircleProgressBar,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-05-05
Flutter學(xué)習(xí)之SliverList和SliverGird的使用詳解
Sliver的組件一般都用在CustomScrollView中,除了SliverAppBar之外,我們還可以為CustomScrollView添加List或者Grid來實(shí)現(xiàn)更加復(fù)雜的組合效果。本文就來聊聊SliverList和SliverGird的使用吧2023-02-02

