阶段 1:Kotlin x Java 打造 UI 通用组件
阶段 1:Kotlin x Java 打造 UI 通用组件
01 走进移动端架构师
第 1 章、本周目标
第 2 章、移动端架构师需要具备的技术栈与能力
第3章、如何从0开始架构一个中大型APP
第4章、如何做好项目的技术选型
第5章、移动端架构师开发套件介绍
第6章、HiLog库架构设计与开发
6-1、HiLog库疑难点分析与架构设计
6-2、HiLog基础框架搭建
package org.malred.hilibrary.log;
import android.util.Log;
import androidx.annotation.NonNull;
public class HiLog {
// @IntDef({V, D, I, W, E, A})
public static void v(Object... contents) {
log(HiLogType.V, contents);
}
public static void vt(String tag, Object... contents) {
log(HiLogType.V, tag, contents);
}
public static void d(Object... contents) {
log(HiLogType.D, contents);
}
public static void dt(String tag, Object... contents) {
log(HiLogType.D, tag, contents);
}
public static void i(Object... contents) {
log(HiLogType.I, contents);
}
public static void it(String tag, Object... contents) {
log(HiLogType.I, tag, contents);
}
public static void w(Object... contents) {
log(HiLogType.W, contents);
}
public static void wt(String tag, Object... contents) {
log(HiLogType.W, tag, contents);
}
public static void e(Object... contents) {
log(HiLogType.E, contents);
}
public static void et(String tag, Object... contents) {
log(HiLogType.E, tag, contents);
}
public static void a(Object... contents) {
log(HiLogType.A, contents);
}
public static void at(String tag, Object... contents) {
log(HiLogType.A, tag, contents);
}
public static void log(@HiLogType.TYPE int type, Object... contents) {
log(type, HiLogManager.getInstance().getConfig().getGlobalTag(), contents);
}
public static void log(@HiLogType.TYPE int type, @NonNull String tag, Object... contents) {
log(HiLogManager.getInstance().getConfig(), type, tag, contents);
}
public static void log(@NonNull HiLogConfig config, @HiLogType.TYPE int type,
@NonNull String tag, Object... contents) {
if (!config.enable()) {
return;
}
StringBuilder sb = new StringBuilder();
String body = parseBody(contents);
sb.append(body);
Log.println(type, tag, body);
}
private static String parseBody(@NonNull Object[] contents) {
StringBuilder sb = new StringBuilder();
for (Object o : contents) {
sb.append(o.toString()).append(";");
}
if (sb.length() > 0) {
// 删除最后一个分号
sb.deleteCharAt(sb.length() - 1);
}
return sb.toString();
}
}
package org.malred.hilibrary.log;
public abstract class HiLogConfig {
public String getGlobalTag() {
return "HiLog";
}
public boolean enable() {
return true;
}
}
package org.malred.hilibrary.log;
import androidx.annotation.NonNull;
public class HiLogManager {
private HiLogConfig config;
private static HiLogManager instance;
private HiLogManager(HiLogConfig config) {
this.config = config;
}
public static HiLogManager getInstance() {
return instance;
}
public static void init(@NonNull HiLogConfig config) {
instance = new HiLogManager(config);
}
public HiLogConfig getConfig() {
return config;
}
}
package org.malred.hilibrary.log;
import android.util.Log;
import androidx.annotation.IntDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
public class HiLogType {
@Retention(RetentionPolicy.SOURCE) // 保留到源代码级别
// 1 -> V ...
@IntDef({V, D, I, W, E, A}) // 简化从枚举到整型值的转换
public @interface TYPE {
}
public final static int V = Log.VERBOSE;
public final static int D = Log.DEBUG;
public final static int I = Log.INFO;
public final static int W = Log.WARN;
public final static int E = Log.ERROR;
public final static int A = Log.ASSERT;
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_hilog"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:onClick="onClick"
android:text="HiLog"
android:textColor="#0077cc"
android:textSize="20dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
activity_hi_log_demo.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:theme="@style/Theme.AppCompat.Light"
tools:context=".demo.HiLogDemoActivity">
<Button
android:id="@+id/btn_log"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Print Log"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
package org.malred.hi_library.demo
import android.os.Bundle
import android.view.View
import androidx.activity.ComponentActivity
import org.malred.hi_library.R
import org.malred.hilibrary.log.HiLog
// ComponentActivity而不是AppCompatActivity
// 否则报错
/*
Unable to start activity
ComponentInfo{org.malred.hi_library/org.malred.hi_library.demo.HiLogDemoActivity}:
java.lang.IllegalStateException:
You need to use a Theme.AppCompat theme (or descendant) with this activity.
*/
class HiLogDemoActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_hi_log_demo)
// 这里用了lambda
findViewById<View>(R.id.btn_log).setOnClickListener {
printLog()
}
}
private fun printLog() {
HiLog.a("9900")
}
}
package org.malred.hi_library
import android.app.Application
import org.malred.hilibrary.log.HiLogConfig
import org.malred.hilibrary.log.HiLogManager
class MApplication : Application() {
override fun onCreate() {
super.onCreate()
HiLogManager.init(object : HiLogConfig() {
override fun getGlobalTag(): String {
return "MApplication"
}
override fun enable(): Boolean {
return true;
}
})
}
}
package org.malred.hi_library
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.activity.ComponentActivity
import org.malred.hi_library.demo.HiLogDemoActivity
import org.malred.hilibrary.log.HiLog
class MainActivity : ComponentActivity(), View.OnClickListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
override fun onClick(v: View?) {
HiLog.a("hello")
when (v!!.id) {
R.id.tv_hilog -> {
startActivity(Intent(this, HiLogDemoActivity::class.java))
}
}
}
}
6-3、HiLog堆栈信息打印与日志格式化功能实现-1
package org.malred.hilibrary.log;
import androidx.annotation.NonNull;
public interface HiLogPrinter {
void print(@NonNull HiLogConfig config,int level,String tag,@NonNull String printString);
}
package org.malred.hilibrary.log;
import static org.malred.hilibrary.log.HiLogConfig.MAX_LEN;
import android.util.Log;
import androidx.annotation.NonNull;
public class HiConsolePrinter implements HiLogPrinter {
@Override
public void print(@NonNull HiLogConfig config, int level, String tag, @NonNull String printString) {
int len = printString.length();
// 行数
int countOfSub = len / MAX_LEN;
if (countOfSub > 0) {
int index = 0;
for (int i = 0; i < countOfSub; i++) {
Log.println(level, tag, printString.substring(index, index + MAX_LEN));
index += MAX_LEN;
}
// len/MAX_LEN无法整除的情况
// 打印剩余内容
if (index != len) {
Log.println(level, tag, printString.substring(index, len));
}
} else {
Log.println(level, tag, printString);
}
}
}
package org.malred.hilibrary.log;
public abstract class HiLogConfig {
// 每行最大长度
static int MAX_LEN = 512;
static HiThreadFormatter HI_THREAD_FORMATTER = new HiThreadFormatter();
static HiStackTraceFormatter HI_STACK_TRACE_FORMATTER = new HiStackTraceFormatter();
// 注入json序列化器
public JsonParser injectJsonParser() {
return null;
}
public String getGlobalTag() {
return "HiLog";
}
public boolean enable() {
return true;
}
// 是否要包含线程信息
public boolean includeThread() {
return false;
}
// 堆栈信息深度
public int stackTraceDepth() {
return 5;
}
public HiLogPrinter[] printers() {
return null;
}
// json序列化器接口
// 与具体json工具解耦合
public interface JsonParser {
String toJson(Object src);
}
}
package org.malred.hilibrary.log;
public interface HiLogFormatter<T> {
String format(T data);
}
package org.malred.hilibrary.log;
public class HiStackTraceFormatter implements HiLogFormatter<StackTraceElement[]> {
@Override
public String format(StackTraceElement[] stackTraces) {
StringBuilder sb = new StringBuilder(128);
if (stackTraces == null || stackTraces.length == 0) {
return null;
} else if (stackTraces.length == 1) {
return "\t-" + stackTraces[0].toString();
} else {
for (int i = 0, len = stackTraces.length; i < len; i++) {
if (i == 0) {
sb.append("StackTrace: \n");
}
if (i != len - 1) {
sb.append("\t- ");
sb.append(stackTraces[i].toString());
sb.append("\n");
} else {
sb.append("\t");
sb.append(stackTraces[i].toString());
}
}
}
return sb.toString();
}
}
package org.malred.hilibrary.log;
public class HiThreadFormatter implements HiLogFormatter<Thread> {
@Override
public String format(Thread data) {
return "Thread:" + data.getName();
}
}
package org.malred.hilibrary.log;
public class HiStackTraceUtil {
public static StackTraceElement[] getCroppedRealStackTrace(
StackTraceElement[] stackTrace, String ignorePackage, int maxDepth) {
return cropStackTrace(getRealStackTrace(stackTrace, ignorePackage), maxDepth);
}
/**
* 获取真实堆栈信息
*
* @param stackTrace 原始堆栈信息
* @param ignorePackage 要忽略的报错位置
* @return
*/
private static StackTraceElement[] getRealStackTrace(
StackTraceElement[] stackTrace, String ignorePackage) {
int ignoreDepth = 0;
int allDepth = stackTrace.length;
String className;
for (int i = allDepth - 1; i >= 0; i--) {
className = stackTrace[i].getClassName();
if (ignorePackage != null && className.startsWith(ignorePackage)) {
ignoreDepth = i + 1;
break;
}
}
int realDepth = allDepth - ignoreDepth;
StackTraceElement[] realStack = new StackTraceElement[realDepth];
System.arraycopy(stackTrace, ignoreDepth, realStack, 0, realDepth);
return realStack;
}
/**
* 裁剪堆栈信息
*
* @param callStack
* @param maxDepth
* @return
*/
private static StackTraceElement[] cropStackTrace(StackTraceElement[] callStack, int maxDepth) {
int realDepth = callStack.length;
if (maxDepth > 0) {
realDepth = Math.min(maxDepth, realDepth);
}
StackTraceElement[] realStack = new StackTraceElement[realDepth];
// callStack内容copy到realStack
System.arraycopy(callStack, 0, realStack, 0, realDepth);
return realStack;
}
}
package org.malred.hi_library
import android.app.Application
import com.google.gson.Gson
import org.malred.hilibrary.log.HiConsolePrinter
import org.malred.hilibrary.log.HiLogConfig
import org.malred.hilibrary.log.HiLogManager
class MApplication : Application() {
override fun onCreate() {
super.onCreate()
HiLogManager.init(object : HiLogConfig() {
override fun injectJsonParser(): JsonParser {
return JsonParser { src -> Gson().toJson(src) }
}
// ...
}, HiConsolePrinter())
}
}
public class HiLog {
public static final String HI_LOG_PACKAGE;
static {
String className = HiLog.class.getName();
HI_LOG_PACKAGE = className.substring(0, className.lastIndexOf('.') - 1);
}
// ...
public static void log(@NonNull HiLogConfig config, @HiLogType.TYPE int type,
@NonNull String tag, Object... contents) {
if (!config.enable()) {
return;
}
StringBuilder sb = new StringBuilder();
if (config.includeThread()) {
String threadInfo = HiLogConfig.HI_THREAD_FORMATTER.format(Thread.currentThread());
sb.append(threadInfo).append("\n");
}
if (config.stackTraceDepth() > 0) {
String stackTrace = HiLogConfig.HI_STACK_TRACE_FORMATTER
.format(HiStackTraceUtil.getCroppedRealStackTrace(
new Throwable().getStackTrace(), HI_LOG_PACKAGE, config.stackTraceDepth()
));
sb.append(stackTrace).append("\n");
}
String body = parseBody(contents, config);
sb.append(body);
// Log.println(type, tag, body);
List<HiLogPrinter> printers = config.printers() != null ?
Arrays.asList(config.printers()) :
HiLogManager.getInstance().getPrinters();
if (printers == null) {
return;
}
// 打印log
for (HiLogPrinter printer : printers) {
printer.print(config, type, tag, sb.toString());
}
}
private static String parseBody(@NonNull Object[] contents, HiLogConfig config) {
if (config.injectJsonParser() != null) {
return config.injectJsonParser().toJson(contents);
}
StringBuilder sb = new StringBuilder();
for (Object o : contents) {
sb.append(o.toString()).append(";");
}
if (sb.length() > 0) {
// 删除最后一个分号
sb.deleteCharAt(sb.length() - 1);
}
return sb.toString();
}
}
package org.malred.hilibrary.log;
import androidx.annotation.NonNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class HiLogManager {
private HiLogConfig config;
private static HiLogManager instance;
private List<HiLogPrinter> printers = new ArrayList<>();
// ...
private HiLogManager(HiLogConfig config, HiLogPrinter[] printers) {
this.config = config;
this.printers.addAll(Arrays.asList(printers));
}
public List<HiLogPrinter> getPrinters() {
return printers;
}
public void addPrinters(HiLogPrinter printer) {
printers.add(printer);
}
public void removePrinters(HiLogPrinter printer) {
if (printer != null) {
printers.remove(printer);
}
}
}
package org.malred.hi_library.demo
// ComponentActivity而不是AppCompatActivity
// 否则报错
/*
Unable to start activity
ComponentInfo{org.malred.hi_library/org.malred.hi_library.demo.HiLogDemoActivity}:
java.lang.IllegalStateException:
You need to use a Theme.AppCompat theme (or descendant) with this activity.
*/
class HiLogDemoActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
// ...
}
private fun printLog() {
// 自定义log配置
HiLog.log(object : HiLogConfig() {
override fun includeThread(): Boolean {
return true
}
override fun stackTraceDepth(): Int {
return 0
}
}, HiLogType.E, "-----", "5566")
HiLog.a("9900")
}
}
6-5、基于HiLogPrinter实现日志可视化模块
package org.malred.hilibrary.log;
import java.text.SimpleDateFormat;
import java.util.Locale;
public class HiLogMo {
private static SimpleDateFormat sdf =
new SimpleDateFormat("yy-MM-dd HH:mm:ss", Locale.CHINA);
public long timeMillis;
public int level;
public String tag;
public String log;
public HiLogMo(long timeMillis, int level, String tag, String log) {
this.timeMillis = timeMillis;
this.level = level;
this.tag = tag;
this.log = log;
}
public String flattenedLog() {
return getFlattened() + "\n" + log;
}
public String getFlattened() {
return format(timeMillis) + '|' + level + '|' + tag + "|:";
}
public String format(long timeMillis) {
return sdf.format(timeMillis);
}
}
package org.malred.hilibrary.util;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Point;
import android.util.TypedValue;
import android.view.Display;
import android.view.Window;
import android.view.WindowManager;
import androidx.annotation.NonNull;
public class HiDisplayUtil {
public static int dp2px(float dp, Resources resources) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, resources.getDisplayMetrics());
}
/**
* 获取屏幕宽度
*
* @param context
* @return
*/
public static int getDisplayWidthInPx(@NonNull Context context) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
if (wm != null) {
Display display = wm.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
return size.x;
}
return 0;
}
/**
* 获取屏幕高度
*
* @param context
* @return
*/
public static int getDisplayHeightInPx(@NonNull Context context) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
if (wm != null) {
Display display = wm.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
return size.y;
}
return 0;
}
}
package org.malred.hilibrary.log;
import android.graphics.Color;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import org.malred.hilibrary.util.HiDisplayUtil;
public class HiViewPrinterProvider {
private FrameLayout rootView;
private View floatingView;
private boolean isOpen;
private FrameLayout logView;
private RecyclerView recyclerView;
public HiViewPrinterProvider(FrameLayout rootView, RecyclerView recyclerView) {
this.rootView = rootView;
this.recyclerView = recyclerView;
}
private static final String TAG_FLOATING_VIEW = "TAG_FLOATING_VIEW";
private static final String TAG_LOG_VIEW = "TAG_LOG_VIEW";
public void showFloatingView() {
// 已经添加了悬浮窗
if (rootView.findViewWithTag(TAG_FLOATING_VIEW) != null) {
return;
}
FrameLayout.LayoutParams params =
new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
params.gravity = Gravity.BOTTOM | Gravity.END;
View floatingView = genFloatingView();
floatingView.setTag(TAG_FLOATING_VIEW);
floatingView.setBackgroundColor(Color.BLACK);
floatingView.setAlpha(0.8f); // 透明度
params.bottomMargin = HiDisplayUtil.dp2px(100, recyclerView.getResources());
rootView.addView(genFloatingView(), params);
}
/**
* 展示log 悬浮按钮
*/
public void closeFloatingView() {
rootView.removeView(genFloatingView());
}
/**
* 生成floatView
*
* @return
*/
private View genFloatingView() {
if (floatingView != null) {
return floatingView;
}
TextView tv = new TextView(rootView.getContext());
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 没有展开log
if (!isOpen) {
showLogView();
}
}
});
tv.setText("HiLog");
tv.setTextColor(Color.WHITE);
return floatingView = tv;
}
private void showLogView() {
if (rootView.findViewWithTag(TAG_LOG_VIEW) != null) {
return;
}
FrameLayout.LayoutParams params =
new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
HiDisplayUtil.dp2px(160, rootView.getResources())
);
params.gravity = Gravity.BOTTOM;
View logView = genLogView();
logView.setTag(TAG_LOG_VIEW);
rootView.addView(genLogView(), params);
isOpen = true;
}
private View genLogView() {
if (logView != null) {
return logView;
}
FrameLayout logView = new FrameLayout(rootView.getContext());
logView.setBackgroundColor(Color.BLACK);
logView.addView(recyclerView);
FrameLayout.LayoutParams params =
new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
params.gravity = Gravity.END;
TextView closeView = new TextView(rootView.getContext());
closeView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 关闭logview
closeLogView();
}
});
closeView.setText("Close");
closeView.setTextColor(Color.WHITE);
//
logView.addView(closeView, params);
return this.logView = logView;
}
/**
* 关闭logview
*/
private void closeLogView() {
isOpen = false;
rootView.removeView(genLogView());
}
}
package org.malred.hilibrary.log;
import android.app.Activity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import org.malred.hilibrary.R;
import java.util.ArrayList;
import java.util.List;
public class HiViewPrinter implements HiLogPrinter {
private RecyclerView recyclerView;
private LogAdapter adapter;
private HiViewPrinterProvider viewProvider;
public HiViewPrinterProvider getViewProvider() {
return viewProvider;
}
public HiViewPrinter(Activity activity) {
FrameLayout rootView = activity.findViewById(android.R.id.content);
recyclerView = new RecyclerView(activity);
adapter = new LogAdapter(LayoutInflater.from(recyclerView.getContext()));
LinearLayoutManager manager = new LinearLayoutManager(recyclerView.getContext());
recyclerView.setLayoutManager(manager);
recyclerView.setAdapter(adapter);
viewProvider = new HiViewPrinterProvider(rootView, recyclerView);
}
@Override
public void print(@NonNull HiLogConfig config, int level, String tag, @NonNull String printString) {
}
private static class LogViewHolder extends RecyclerView.ViewHolder {
TextView tagView;
TextView messageView;
public LogViewHolder(@NonNull View itemView) {
super(itemView);
tagView = itemView.findViewById(R.id.tag);
messageView = itemView.findViewById(R.id.message);
}
}
private static class LogAdapter extends RecyclerView.Adapter<LogViewHolder> {
private LayoutInflater inflater;
private List<HiLogMo> logs = new ArrayList<>();
void addItem(HiLogMo logItem) {
logs.add(logItem);
// 通知刷新
// 列表position位置添加一条数据时可以调用,伴有动画效果
// RecyclerView.Adapter中为我们提供了很多自带酷炫的增加删除动画,包括局部刷新的方法。
notifyItemInserted(logs.size() - 1);
}
public LogAdapter(LayoutInflater inflater) {
this.inflater = inflater;
}
@NonNull
@Override
public LogViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View item = inflater.inflate(R.layout.hilog_item, parent, false);
return new LogViewHolder(item);
}
@Override
public void onBindViewHolder(@NonNull LogViewHolder holder, int position) {
HiLogMo logItem = logs.get(position);
int color = getHighlightColor(logItem.level);
holder.tagView.setTextColor(color);
holder.messageView.setTextColor(color);
holder.tagView.setText(logItem.getFlattened());
holder.messageView.setText(logItem.log);
}
/**
* 根据不同log级别获取不同颜色
*
* @param logLevel
* @return
*/
private int getHighlightColor(int logLevel) {
int highlight;
switch (logLevel) {
case HiLogType.V:
highlight = 0xffbbbbbb;
break;
case HiLogType.D:
highlight = 0xffffffff;
break;
case HiLogType.I:
highlight = 0xff6a8759;
break;
case HiLogType.W:
highlight = 0xffbbb529;
break;
case HiLogType.E:
highlight = 0xffff6b68;
break;
default:
highlight = 0xffffff00;
break;
}
return highlight;
}
@Override
public int getItemCount() {
return logs.size();
}
}
}
hilog_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:theme="@style/Theme.Hilibrary">
<TextView
android:id="@+id/tag"
android:layout_width="wrap_content"
tools:text="dfadfnhsdjnhf"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
package org.malred.hi_library.demo
import android.os.Bundle
import android.view.View
import androidx.activity.ComponentActivity
import org.malred.hi_library.R
import org.malred.hilibrary.log.HiLog
import org.malred.hilibrary.log.HiLogConfig
import org.malred.hilibrary.log.HiLogType
import org.malred.hilibrary.log.HiViewPrinter
// ComponentActivity而不是AppCompatActivity
// 否则报错
/*
Unable to start activity
ComponentInfo{org.malred.hi_library/org.malred.hi_library.demo.HiLogDemoActivity}:
java.lang.IllegalStateException:
You need to use a Theme.AppCompat theme (or descendant) with this activity.
*/
class HiLogDemoActivity : ComponentActivity() {
var viewPrinter: HiViewPrinter? = null;
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_hi_log_demo)
viewPrinter = HiViewPrinter(this);
// 这里用了lambda
findViewById<View>(R.id.btn_log).setOnClickListener {
printLog()
}
viewPrinter!!.viewProvider.showFloatingView()
}
private fun printLog() {
// ...
}
}
6-6、HiLogPrinter日志可视化测试
// HiViewPrinter.java
@Override
public void print(@NonNull HiLogConfig config, int level, String tag, @NonNull String printString) {
// 将log展示添加到recycleview
adapter.addItem(new HiLogMo(System.currentTimeMillis(), level, tag, printString));
// 添加到对应位置
recyclerView.smoothScrollToPosition(adapter.getItemCount() - 1);
}
02 通用UI组件开发与基础框架设计
1-1 本周目标_免费IT课程加11[2]
2-1 HiTabBottom组件疑难点分析与架构设计_免费IT课程加11[2]
package org.malred.hiui.tab.bottom;
import android.graphics.Bitmap;
import androidx.fragment.app.Fragment;
public class HiTabBottomInfo<Color> {
// tab类型
public enum TabType {
BITMAP, ICON
}
// tab对应的页面
public Class<? extends Fragment> fragment;
public String name;
// 默认图标
public Bitmap defaultBitmap;
// 选中状态的图标
public Bitmap selectedBitmap;
public String iconFont;
/**
* Tips: java代码中直接设置iconfont字符串无效,需要定义在string.xml
*/
public String defaultIconName;
public String selectedIconName;
// 可以是int或string类型color
public Color defaultColor;
public Color tintColor;
public TabType tabType;
public HiTabBottomInfo(String name, Bitmap defaultBitmap, Bitmap selectedBitmap) {
this.name = name;
this.defaultBitmap = defaultBitmap;
this.selectedBitmap = selectedBitmap;
}
public HiTabBottomInfo(String name, String iconFont, String defaultIconName,
String selectedIconName, Color defaultColor, Color tintColor) {
this.name = name;
this.iconFont = iconFont;
this.defaultIconName = defaultIconName;
this.selectedIconName = selectedIconName;
this.defaultColor = defaultColor;
this.tintColor = tintColor;
this.tabType = TabType.ICON;
}
}
package org.malred.hiui.tab.common;
import androidx.annotation.NonNull;
import androidx.annotation.Px;
/**
* HiTab对外接口
*/
public interface IHiTab<D> extends IHiTabLayout.OnTabSelectedListener<D> {
void setHiTabInfo(@NonNull D data);
/**
* 动态修改某个item的大小
*
* @param height
*/
void resetHeight(@Px int height);
}
package org.malred.hiui.tab.common;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.List;
public interface IHiTabLayout<Tab extends ViewGroup, D> {
Tab findTab(@NonNull D data);
void addTabSelectedChangeListener(OnTabSelectedListener<D> listener);
void defaultSelected(@NonNull D defaultInfo);
void inflateInfo(@NonNull List<D> infoList);
interface OnTabSelectedListener<D> {
/**
* tab被选中时触发
*
* @param index 被选中的tab的索引
* @param prevInfo
* @param nextInfo
*/
void onTabSelectedChange(int index, @Nullable D prevInfo, @NonNull D nextInfo);
}
}