android 日志文件LogUtils實(shí)例
背景
這是好久之前在網(wǎng)上找的一個(gè)常用類,已經(jīng)忘記原文鏈接了,但是覺(jué)得很好用一直都在用,可以將日志寫(xiě)到file里面也可以定位你是在哪個(gè)類哪一行打印的日志,保存到文件的路徑就是android/data/你的包名/files/目錄下,然后我們就可以愉快的找問(wèn)題了
import android.text.TextUtils;
import android.util.Log;
import com.smartlink.suixing.App;
import com.smartlink.suixing.BuildConfig;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Formatter;
import java.util.Locale;
public class LogUtils {
public static String customTagPrefix = "log"; // 自定義Tag的前綴,可以是作者名
private static final boolean isSaveLog = true; // 是否把保存日志到SD卡中
private static String cacheDirPath;
private LogUtils() {
}
// 容許打印日志的類型,默認(rèn)是true,設(shè)置為false則不打印
public static boolean allowD = BuildConfig.DEBUG;
public static boolean allowE = BuildConfig.DEBUG;
public static boolean allowI = BuildConfig.DEBUG;
public static boolean allowV = BuildConfig.DEBUG;
public static boolean allowW = BuildConfig.DEBUG;
public static boolean allowWtf = BuildConfig.DEBUG;
// public static boolean allowD = true;
// public static boolean allowE = true;
// public static boolean allowI = true;
// public static boolean allowV = true;
// public static boolean allowW = true;
// public static boolean allowWtf = true;
private static String generateTag(StackTraceElement caller) {
String tag = "%s.%s(Line:%d)"; // 占位符
String callerClazzName = caller.getClassName(); // 獲取到類名
callerClazzName = callerClazzName.substring(callerClazzName.lastIndexOf(".") + 1);
tag = String.format(tag, callerClazzName, caller.getMethodName(), caller.getLineNumber()); // 替換
tag = TextUtils.isEmpty(customTagPrefix) ? tag : customTagPrefix + ":" + tag;
return tag;
}
/***
* 打印控制臺(tái)顯示不了那么長(zhǎng)的日志問(wèn)題
*
* @param msg
*/
public static void logE(String msg) { // 信息太長(zhǎng),分段打印
if (!allowE) return;
StackTraceElement caller = getCallerStackTraceElement();
String tag = generateTag(caller);
// 因?yàn)镾tring的length是字符數(shù)量不是字節(jié)數(shù)量所以為了防止中文字符過(guò)多,
// 把4*1024的MAX字節(jié)打印長(zhǎng)度改為2001字符數(shù)
int max_str_length = 2001 - tag.length();
// 大于4000時(shí)
while (msg.length() > max_str_length) {
// Log.e(tag, msg.substring(0, max_str_length));
LogUtils.e(msg.substring(0, max_str_length));
msg = msg.substring(max_str_length);
}
// 剩余部分
// Log.e(tag, msg);
LogUtils.e(msg);
}
/**
* 自定義的logger
*/
public static CustomLogger customLogger;
public interface CustomLogger {
void d(String tag, String content);
void d(String tag, String content, Throwable tr);
void e(String tag, String content);
void e(String tag, String content, Throwable tr);
void i(String tag, String content);
void i(String tag, String content, Throwable tr);
void v(String tag, String content);
void v(String tag, String content, Throwable tr);
void w(String tag, String content);
void w(String tag, String content, Throwable tr);
void w(String tag, Throwable tr);
void wtf(String tag, String content);
void wtf(String tag, String content, Throwable tr);
void wtf(String tag, Throwable tr);
}
public static void d(String content) {
if (!allowD) return;
StackTraceElement caller = getCallerStackTraceElement();
String tag = generateTag(caller);
if (customLogger != null) {
customLogger.d(tag, content);
} else {
Log.d(tag, content);
}
}
public static void d(String content, Throwable tr) {
if (!allowD) return;
StackTraceElement caller = getCallerStackTraceElement();
String tag = generateTag(caller);
if (customLogger != null) {
customLogger.d(tag, content, tr);
} else {
Log.d(tag, content, tr);
}
}
public static void e(String content) {
if (!allowE) return;
StackTraceElement caller = getCallerStackTraceElement();
String tag = generateTag(caller);
if (customLogger != null) {
customLogger.e(tag, content);
} else {
Log.e(tag, content);
}
if (isSaveLog) {
point(cacheDirPath, tag, content);
}
}
public static void e(String content, Throwable tr) {
if (!allowE) return;
StackTraceElement caller = getCallerStackTraceElement();
String tag = generateTag(caller);
if (customLogger != null) {
customLogger.e(tag, content, tr);
} else {
Log.e(tag, content, tr);
}
if (isSaveLog) {
point(cacheDirPath, tag, tr.getMessage());
}
}
public static void e(Throwable tr) {
if (!allowE) return;
StackTraceElement caller = getCallerStackTraceElement();
String tag = generateTag(caller);
if (customLogger != null) {
customLogger.e(tag, "", tr);
} else {
Log.e(tag, "", tr);
}
if (isSaveLog) {
point(cacheDirPath, tag, tr.getMessage());
}
}
public static void i(String content) {
if (!allowI) return;
StackTraceElement caller = getCallerStackTraceElement();
String tag = generateTag(caller);
if (customLogger != null) {
customLogger.i(tag, content);
} else {
Log.i(tag, content);
}
}
public static void i(String content, Throwable tr) {
if (!allowI) return;
StackTraceElement caller = getCallerStackTraceElement();
String tag = generateTag(caller);
if (customLogger != null) {
customLogger.i(tag, content, tr);
} else {
Log.i(tag, content, tr);
}
}
public static void v(String content) {
if (!allowV) return;
StackTraceElement caller = getCallerStackTraceElement();
String tag = generateTag(caller);
if (customLogger != null) {
customLogger.v(tag, content);
} else {
Log.v(tag, content);
}
}
public static void v(String content, Throwable tr) {
if (!allowV) return;
StackTraceElement caller = getCallerStackTraceElement();
String tag = generateTag(caller);
if (customLogger != null) {
customLogger.v(tag, content, tr);
} else {
Log.v(tag, content, tr);
}
}
public static void w(String content) {
if (!allowW) return;
StackTraceElement caller = getCallerStackTraceElement();
String tag = generateTag(caller);
if (customLogger != null) {
customLogger.w(tag, content);
} else {
Log.w(tag, content);
}
}
public static void w(String content, Throwable tr) {
if (!allowW) return;
StackTraceElement caller = getCallerStackTraceElement();
String tag = generateTag(caller);
if (customLogger != null) {
customLogger.w(tag, content, tr);
} else {
Log.w(tag, content, tr);
}
}
public static void w(Throwable tr) {
if (!allowW) return;
StackTraceElement caller = getCallerStackTraceElement();
String tag = generateTag(caller);
if (customLogger != null) {
customLogger.w(tag, tr);
} else {
Log.w(tag, tr);
}
}
public static void wtf(String content) {
if (!allowWtf) return;
StackTraceElement caller = getCallerStackTraceElement();
String tag = generateTag(caller);
if (customLogger != null) {
customLogger.wtf(tag, content);
} else {
Log.wtf(tag, content);
}
}
public static void wtf(String content, Throwable tr) {
if (!allowWtf) return;
StackTraceElement caller = getCallerStackTraceElement();
String tag = generateTag(caller);
if (customLogger != null) {
customLogger.wtf(tag, content, tr);
} else {
Log.wtf(tag, content, tr);
}
}
public static void wtf(Throwable tr) {
if (!allowWtf) return;
StackTraceElement caller = getCallerStackTraceElement();
String tag = generateTag(caller);
if (customLogger != null) {
customLogger.wtf(tag, tr);
} else {
Log.wtf(tag, tr);
}
}
private static StackTraceElement getCallerStackTraceElement() {
return Thread.currentThread().getStackTrace()[4];
}
public static void point(String path, String tag, String msg) {
if (isSDAva()) {
path = cacheDirPath;
Date date = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("", Locale.SIMPLIFIED_CHINESE);
dateFormat.applyPattern("yyyy");
path = path + dateFormat.format(date) + "/";
dateFormat.applyPattern("MM");
path += dateFormat.format(date) + "/";
dateFormat.applyPattern("dd");
path += dateFormat.format(date) + ".log";
dateFormat.applyPattern("[yyyy-MM-dd HH:mm:ss]");
String time = dateFormat.format(date);
File file = new File(path);
if (!file.exists()) createDipPath(path);
BufferedWriter out = null;
try {
out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, true)));
out.write(time + " " + tag + " " + msg + "\r\n");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 根據(jù)文件路徑 遞歸創(chuàng)建文件
*
* @param file
*/
public static void createDipPath(String file) {
String parentFile = file.substring(0, file.lastIndexOf("/"));
File file1 = new File(file);
File parent = new File(parentFile);
if (!file1.exists()) {
parent.mkdirs();
try {
file1.createNewFile();
LogUtils.e("日志文件的路徑是" + file1.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* A little trick to reuse a formatter in the same thread
*/
private static class ReusableFormatter {
private Formatter formatter;
private StringBuilder builder;
public ReusableFormatter() {
builder = new StringBuilder();
formatter = new Formatter(builder);
}
public String format(String msg, Object... args) {
formatter.format(msg, args);
String s = builder.toString();
builder.setLength(0);
return s;
}
}
private static final ThreadLocal<ReusableFormatter> thread_local_formatter = new ThreadLocal<ReusableFormatter>() {
protected ReusableFormatter initialValue() {
return new ReusableFormatter();
}
};
public static String format(String msg, Object... args) {
ReusableFormatter formatter = thread_local_formatter.get();
return formatter.format(msg, args);
}
public static boolean isSDAva() {
if (cacheDirPath == null) cacheDirPath = App.getAppContext().getExternalFilesDir("log").getAbsolutePath();
if (!TextUtils.isEmpty(cacheDirPath)) {
return true;
} else {
return false;
}
}
}
補(bǔ)充知識(shí):Android日志打印類LogUtils,能夠定位到類名,方法名以及出現(xiàn)錯(cuò)誤的行數(shù)并保存日志文件
在開(kāi)發(fā)中,我們常常用打印log的方式來(lái)調(diào)試我們的應(yīng)用。在Java中我們常常使用方法System.out.println()來(lái)在控制臺(tái)打印日志,以便我們的調(diào)試。Android中有一個(gè)專門的類Log來(lái)實(shí)現(xiàn)在Android系統(tǒng)下日志的打印,更加方便我們定位程序出現(xiàn)問(wèn)題的地方?! ?/p>
但是Android官方提供的Log類在實(shí)際項(xiàng)目使用中,也不是非常方便。當(dāng)程序出現(xiàn)錯(cuò)誤時(shí),我們最希望的就是這個(gè)Log類能幫我們定位到是哪個(gè)類的哪個(gè)方法,甚至于是那一行出現(xiàn)了錯(cuò)誤。這樣就能給我們的調(diào)試帶來(lái)很大的便利?! ?/p>
同時(shí)我們也應(yīng)該想到為了應(yīng)用程序的安全起見(jiàn),在app正式上線之前,我們應(yīng)該要把打印日志的功能關(guān)閉掉,以防別人通過(guò)Log來(lái)破解你的應(yīng)用。生產(chǎn)模式的下打印Log,正式模式就把打印日志的功能關(guān)閉掉,要做到Log的靈活關(guān)閉與打開(kāi),也需要在原生的Log類上進(jìn)行一些封裝?! ?/p>
還有一種時(shí)候,當(dāng)我們的程序出現(xiàn)問(wèn)題崩潰了,我們希望能夠收集到出現(xiàn)異常的原因進(jìn)行分析,所以可以把Log日志保存到一個(gè)文件中,放在SD卡程序創(chuàng)建的目錄下。也可以在用戶聯(lián)網(wǎng)的情況下,在程序的后臺(tái)把出異常的Log日志文件上傳到服務(wù)端,方便程序員進(jìn)行分析,解決bug。
今天就給大家分享一個(gè)做項(xiàng)目中很實(shí)用的一個(gè)Log類LogUtils,這個(gè)類是從xUtils中提取出來(lái),稍作修改的,有注釋。
示例:
我們?cè)贛ainActivity中調(diào)用一些LogUtils中的方法,注意看行數(shù)?! ?1
接著看看控制臺(tái)打印的日志是不是像MainActivity調(diào)用的那樣,Log中有這個(gè)類的名字和oncreate方法名,已及當(dāng)前行數(shù); 2
看到上圖中的Log日志是不是很方便定位程序中的問(wèn)題了,出現(xiàn)異常也能快速的定位到。然后把下面的Log類型在程序的任何地方設(shè)置為false則不會(huì)打印日志,使用起來(lái)相當(dāng)?shù)姆奖?。?/p>
// 容許打印日志的類型,默認(rèn)是true,設(shè)置為false則不打印 public static boolean allowD = true; public static boolean allowE = true; public static boolean allowI = true; public static boolean allowV = true; public static boolean allowW = true; public static boolean allowWtf = true;
代碼貼在下面:
package com.finddreams.log;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Formatter;
import java.util.Locale;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;
/**
* Log工具,類似android.util.Log。 tag自動(dòng)產(chǎn)生,格式:
* customTagPrefix:className.methodName(Line:lineNumber),
* customTagPrefix為空時(shí)只輸出:className.methodName(Line:lineNumber)。
*/
public class LogUtils {
public static String customTagPrefix = "finddreams"; // 自定義Tag的前綴,可以是作者名
private static final boolean isSaveLog = false; // 是否把保存日志到SD卡中
public static final String ROOT = Environment.getExternalStorageDirectory()
.getPath() + "/finddreams/"; // SD卡中的根目錄
private static final String PATH_LOG_INFO = ROOT + "info/";
private LogUtils() {
}
// 容許打印日志的類型,默認(rèn)是true,設(shè)置為false則不打印
public static boolean allowD = true;
public static boolean allowE = true;
public static boolean allowI = true;
public static boolean allowV = true;
public static boolean allowW = true;
public static boolean allowWtf = true;
private static String generateTag(StackTraceElement caller) {
String tag = "%s.%s(Line:%d)"; // 占位符
String callerClazzName = caller.getClassName(); // 獲取到類名
callerClazzName = callerClazzName.substring(callerClazzName
.lastIndexOf(".") + 1);
tag = String.format(tag, callerClazzName, caller.getMethodName(),
caller.getLineNumber()); // 替換
tag = TextUtils.isEmpty(customTagPrefix) ? tag : customTagPrefix + ":"
+ tag;
return tag;
}
/**
* 自定義的logger
*/
public static CustomLogger customLogger;
public interface CustomLogger {
void d(String tag, String content);
void d(String tag, String content, Throwable tr);
void e(String tag, String content);
void e(String tag, String content, Throwable tr);
void i(String tag, String content);
void i(String tag, String content, Throwable tr);
void v(String tag, String content);
void v(String tag, String content, Throwable tr);
void w(String tag, String content);
void w(String tag, String content, Throwable tr);
void w(String tag, Throwable tr);
void wtf(String tag, String content);
void wtf(String tag, String content, Throwable tr);
void wtf(String tag, Throwable tr);
}
public static void d(String content) {
if (!allowD)
return;
StackTraceElement caller = getCallerStackTraceElement();
String tag = generateTag(caller);
if (customLogger != null) {
customLogger.d(tag, content);
} else {
Log.d(tag, content);
}
}
public static void d(String content, Throwable tr) {
if (!allowD)
return;
StackTraceElement caller = getCallerStackTraceElement();
String tag = generateTag(caller);
if (customLogger != null) {
customLogger.d(tag, content, tr);
} else {
Log.d(tag, content, tr);
}
}
public static void e(String content) {
if (!allowE)
return;
StackTraceElement caller = getCallerStackTraceElement();
String tag = generateTag(caller);
if (customLogger != null) {
customLogger.e(tag, content);
} else {
Log.e(tag, content);
}
if (isSaveLog) {
point(PATH_LOG_INFO, tag, content);
}
}
public static void e(String content, Throwable tr) {
if (!allowE)
return;
StackTraceElement caller = getCallerStackTraceElement();
String tag = generateTag(caller);
if (customLogger != null) {
customLogger.e(tag, content, tr);
} else {
Log.e(tag, content, tr);
}
if (isSaveLog) {
point(PATH_LOG_INFO, tag, tr.getMessage());
}
}
public static void i(String content) {
if (!allowI)
return;
StackTraceElement caller = getCallerStackTraceElement();
String tag = generateTag(caller);
if (customLogger != null) {
customLogger.i(tag, content);
} else {
Log.i(tag, content);
}
}
public static void i(String content, Throwable tr) {
if (!allowI)
return;
StackTraceElement caller = getCallerStackTraceElement();
String tag = generateTag(caller);
if (customLogger != null) {
customLogger.i(tag, content, tr);
} else {
Log.i(tag, content, tr);
}
}
public static void v(String content) {
if (!allowV)
return;
StackTraceElement caller = getCallerStackTraceElement();
String tag = generateTag(caller);
if (customLogger != null) {
customLogger.v(tag, content);
} else {
Log.v(tag, content);
}
}
public static void v(String content, Throwable tr) {
if (!allowV)
return;
StackTraceElement caller = getCallerStackTraceElement();
String tag = generateTag(caller);
if (customLogger != null) {
customLogger.v(tag, content, tr);
} else {
Log.v(tag, content, tr);
}
}
public static void w(String content) {
if (!allowW)
return;
StackTraceElement caller = getCallerStackTraceElement();
String tag = generateTag(caller);
if (customLogger != null) {
customLogger.w(tag, content);
} else {
Log.w(tag, content);
}
}
public static void w(String content, Throwable tr) {
if (!allowW)
return;
StackTraceElement caller = getCallerStackTraceElement();
String tag = generateTag(caller);
if (customLogger != null) {
customLogger.w(tag, content, tr);
} else {
Log.w(tag, content, tr);
}
}
public static void w(Throwable tr) {
if (!allowW)
return;
StackTraceElement caller = getCallerStackTraceElement();
String tag = generateTag(caller);
if (customLogger != null) {
customLogger.w(tag, tr);
} else {
Log.w(tag, tr);
}
}
public static void wtf(String content) {
if (!allowWtf)
return;
StackTraceElement caller = getCallerStackTraceElement();
String tag = generateTag(caller);
if (customLogger != null) {
customLogger.wtf(tag, content);
} else {
Log.wtf(tag, content);
}
}
public static void wtf(String content, Throwable tr) {
if (!allowWtf)
return;
StackTraceElement caller = getCallerStackTraceElement();
String tag = generateTag(caller);
if (customLogger != null) {
customLogger.wtf(tag, content, tr);
} else {
Log.wtf(tag, content, tr);
}
}
public static void wtf(Throwable tr) {
if (!allowWtf)
return;
StackTraceElement caller = getCallerStackTraceElement();
String tag = generateTag(caller);
if (customLogger != null) {
customLogger.wtf(tag, tr);
} else {
Log.wtf(tag, tr);
}
}
private static StackTraceElement getCallerStackTraceElement() {
return Thread.currentThread().getStackTrace()[4];
}
public static void point(String path, String tag, String msg) {
if (isSDAva()) {
Date date = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("",
Locale.SIMPLIFIED_CHINESE);
dateFormat.applyPattern("yyyy");
path = path + dateFormat.format(date) + "/";
dateFormat.applyPattern("MM");
path += dateFormat.format(date) + "/";
dateFormat.applyPattern("dd");
path += dateFormat.format(date) + ".log";
dateFormat.applyPattern("[yyyy-MM-dd HH:mm:ss]");
String time = dateFormat.format(date);
File file = new File(path);
if (!file.exists())
createDipPath(path);
BufferedWriter out = null;
try {
out = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(file, true)));
out.write(time + " " + tag + " " + msg + "\r\n");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 根據(jù)文件路徑 遞歸創(chuàng)建文件
*
* @param file
*/
public static void createDipPath(String file) {
String parentFile = file.substring(0, file.lastIndexOf("/"));
File file1 = new File(file);
File parent = new File(parentFile);
if (!file1.exists()) {
parent.mkdirs();
try {
file1.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* A little trick to reuse a formatter in the same thread
*/
private static class ReusableFormatter {
private Formatter formatter;
private StringBuilder builder;
public ReusableFormatter() {
builder = new StringBuilder();
formatter = new Formatter(builder);
}
public String format(String msg, Object... args) {
formatter.format(msg, args);
String s = builder.toString();
builder.setLength(0);
return s;
}
}
private static final ThreadLocal<ReusableFormatter> thread_local_formatter = new ThreadLocal<ReusableFormatter>() {
protected ReusableFormatter initialValue() {
return new ReusableFormatter();
}
};
public static String format(String msg, Object... args) {
ReusableFormatter formatter = thread_local_formatter.get();
return formatter.format(msg, args);
}
public static boolean isSDAva() {
if (Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)
|| Environment.getExternalStorageDirectory().exists()) {
return true;
} else {
return false;
}
}
}
以上這篇android 日志文件LogUtils實(shí)例就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Android自定義View實(shí)現(xiàn)搜索框(SearchView)功能
這篇文章主要為大家詳細(xì)介紹了Android自定義View實(shí)現(xiàn)搜索框SearchView功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11
Android EditText實(shí)現(xiàn)輸入表情
editText是TextView的子類,TextView能用的工具EditText都能用,接下來(lái)通過(guò)實(shí)例代碼給大家分享Android EditText實(shí)現(xiàn)輸入表情功能,感興趣的朋友一起看看吧2017-08-08
android Launcher AppWidget添加步驟介紹
大家好,本篇文章主要講的是android Launcher AppWidget添加步驟介紹,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下,方便下次瀏覽2022-01-01
Android實(shí)戰(zhàn)教程第十篇仿騰訊手機(jī)助手小火箭發(fā)射效果
這篇文章主要為大家詳細(xì)介紹了Android實(shí)戰(zhàn)教程第十篇仿騰訊手機(jī)助手小火箭發(fā)射效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11
Flutter實(shí)現(xiàn)固定header底部滑動(dòng)頁(yè)效果示例
這篇文章主要為大家介紹了Flutter實(shí)現(xiàn)固定header底部滑動(dòng)頁(yè)效果示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
Android裁剪圖片為圓形圖片的實(shí)現(xiàn)原理與代碼
這個(gè)方法是根據(jù)傳入的圖片的高度(height)和寬度(width)決定的,如果是 width <= height時(shí),則會(huì)裁剪高度,裁剪的區(qū)域是寬度不變高度從頂部到寬度width的長(zhǎng)度2013-01-01
關(guān)于WebView 重定向行為導(dǎo)致的多次加載的問(wèn)題
這篇文章主要介紹了關(guān)于WebView 重定向行為導(dǎo)致的多次加載的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-03-03
解決Bitmap通過(guò)getWidth和getHeight獲取尺寸不符的問(wèn)題
這篇文章主要介紹了解決Bitmap通過(guò)getWidth和getHeight獲取尺寸不符的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-08-08
Android 實(shí)現(xiàn)IOS選擇拍照相冊(cè)底部彈出的實(shí)例
這篇文章主要介紹了Android 實(shí)現(xiàn)IOS選擇拍照相冊(cè)底部彈出的實(shí)例的相關(guān)資料,這里提供了實(shí)現(xiàn)效果圖及實(shí)現(xiàn)代碼,需要的朋友可以參考下2017-07-07
Flutter獲取ListView當(dāng)前正在顯示的Widget信息(應(yīng)用場(chǎng)景)
ListView是Flutter里最常用的Widget了,當(dāng)屏幕放不下的時(shí)候,它可以自帶滾動(dòng)功能,用法也很簡(jiǎn)單,本文通過(guò)實(shí)例代碼給大家介紹Flutter獲取ListView當(dāng)前正在顯示的Widget信息,感興趣的朋友一起看看吧2022-05-05

