金职位_移动端架构师

阶段 1:Kotlin x Java 打造 UI 通用组件

阶段 1:Kotlin x Java 打造 UI 通用组件

01 走进移动端架构师

第 1 章、本周目标

第 2 章、移动端架构师需要具备的技术栈与能力


第3章、如何从0开始架构一个中大型APP


第4章、如何做好项目的技术选型


第5章、移动端架构师开发套件介绍


第6章、HiLog库架构设计与开发

6-1、HiLog库疑难点分析与架构设计


new module

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);
    }
}

2-2 HiTabBottom的单Tab组件封装_免费IT课程加11[2]


  目录