数据模型调整
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Fri Nov 21 11:41:04 HKT 2025
|
||||
#Fri Nov 28 13:30:32 GMT 2025
|
||||
stageCount=2
|
||||
libraryProject=libappbase
|
||||
baseVersion=15.11
|
||||
publishVersion=15.11.1
|
||||
buildCount=0
|
||||
buildCount=9
|
||||
baseBetaVersion=15.11.2
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Fri Nov 21 11:41:04 HKT 2025
|
||||
#Fri Nov 28 13:30:32 GMT 2025
|
||||
stageCount=2
|
||||
libraryProject=libappbase
|
||||
baseVersion=15.11
|
||||
publishVersion=15.11.1
|
||||
buildCount=0
|
||||
buildCount=9
|
||||
baseBetaVersion=15.11.2
|
||||
|
||||
@@ -85,6 +85,7 @@ public class GlobalApplication extends Application {
|
||||
super.onCreate();
|
||||
// 初始化单例实例(确保在所有初始化操作前完成)
|
||||
sInstance = this;
|
||||
|
||||
|
||||
// 初始化基础组件(日志、崩溃处理、Toast)
|
||||
initCoreComponents();
|
||||
@@ -169,6 +170,7 @@ public class GlobalApplication extends Application {
|
||||
// 释放单例引用(可选,避免内存泄漏风险)
|
||||
sInstance = null;
|
||||
LogUtils.d(TAG, "GlobalApplication 终止,单例实例已释放");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,240 +0,0 @@
|
||||
package cc.winboll.studio.libappbase;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.ListView;
|
||||
import android.widget.Scroller;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||
* @Date 2025/11/11 20:26
|
||||
* @Describe 水平滚动 ListView 控件
|
||||
* 继承自 ListView,重写布局和测量逻辑,实现子项水平排列和滚动,替代默认垂直布局
|
||||
*/
|
||||
public class HorizontalListView extends ListView {
|
||||
/** 日志标签,用于当前控件的日志输出标识 */
|
||||
public static final String TAG = "HorizontalListView";
|
||||
|
||||
/** 子项垂直偏移量(用于调整子项在垂直方向的位置,默认 0) */
|
||||
private int mVerticalOffset = 0;
|
||||
/** 平滑滚动控制器(用于实现水平方向的平滑滚动动画) */
|
||||
private Scroller mScroller;
|
||||
/** 所有子项总宽度(包含内边距),用于计算滚动范围 */
|
||||
private int mTotalWidth;
|
||||
|
||||
/**
|
||||
* 构造方法:仅上下文
|
||||
* @param context 上下文(Activity/Fragment)
|
||||
*/
|
||||
public HorizontalListView(Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造方法:上下文 + 自定义属性
|
||||
* @param context 上下文
|
||||
* @param attrs 自定义属性集合(如布局文件中设置的属性)
|
||||
*/
|
||||
public HorizontalListView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造方法:上下文 + 自定义属性 + 样式属性
|
||||
* @param context 上下文
|
||||
* @param attrs 自定义属性集合
|
||||
* @param defStyle 样式属性(如系统默认样式)
|
||||
*/
|
||||
public HorizontalListView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件配置
|
||||
* 初始化滚动控制器,设置滚动条显示状态
|
||||
*/
|
||||
private void init() {
|
||||
// 初始化平滑滚动器(上下文为当前控件所在上下文)
|
||||
mScroller = new Scroller(getContext());
|
||||
// 启用水平滚动条(默认显示)
|
||||
setHorizontalScrollBarEnabled(true);
|
||||
// 禁用垂直滚动条(水平列表无需垂直滚动)
|
||||
setVerticalScrollBarEnabled(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置子项垂直偏移量
|
||||
* 用于整体调整所有子项在垂直方向的位置(如居中、偏移)
|
||||
* @param verticalOffset 垂直偏移像素值(正数向下偏移,负数向上偏移)
|
||||
*/
|
||||
public void setVerticalOffset(int verticalOffset) {
|
||||
this.mVerticalOffset = verticalOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写布局方法:实现子项水平排列
|
||||
* 遍历所有子项,按水平方向依次布局(左对齐,叠加排列)
|
||||
* @param changed 布局是否发生变化(true:首次布局或尺寸变化;false:重绘)
|
||||
* @param l 控件左边界坐标
|
||||
* @param t 控件上边界坐标
|
||||
* @param r 控件右边界坐标
|
||||
* @param b 控件下边界坐标
|
||||
*/
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
super.onLayout(changed, l, t, r, b); // 执行父类布局逻辑(确保基础配置生效)
|
||||
|
||||
int childCount = getChildCount(); // 获取当前可见子项数量
|
||||
int left = getPaddingLeft(); // 子项起始左坐标(包含控件左内边距)
|
||||
// 控件可用高度(总高度 - 上下内边距)
|
||||
int viewHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
|
||||
mTotalWidth = left; // 初始化总宽度为左内边距
|
||||
|
||||
// 遍历子项,水平排列
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
View child = getChildAt(i); // 获取当前子项
|
||||
int childWidth = child.getMeasuredWidth(); // 子项测量宽度
|
||||
int childHeight = child.getMeasuredHeight(); // 子项测量高度
|
||||
|
||||
// 布局子项:水平方向从 left 开始,垂直方向偏移 mVerticalOffset
|
||||
child.layout(
|
||||
left, // 子项左边界
|
||||
mVerticalOffset, // 子项上边界(带垂直偏移)
|
||||
left + childWidth, // 子项右边界(左 + 宽度)
|
||||
mVerticalOffset + childHeight // 子项下边界(偏移 + 高度)
|
||||
);
|
||||
|
||||
left += childWidth; // 更新下一个子项的起始左坐标
|
||||
}
|
||||
|
||||
// 计算总宽度(所有子项宽度 + 左右内边距)
|
||||
mTotalWidth = left + getPaddingRight();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写测量方法:设置控件测量规则
|
||||
* 水平方向:允许无限宽度(适应所有子项总宽度);垂直方向:自适应内容高度
|
||||
* @param widthMeasureSpec 父控件传递的宽度测量规格
|
||||
* @param heightMeasureSpec 父控件传递的高度测量规格
|
||||
*/
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
// 重写宽度测量规则:最大值(Integer.MAX_VALUE >> 2 避免溢出),自适应内容
|
||||
int newWidthMeasureSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
|
||||
// 重写高度测量规则:同上,自适应子项高度
|
||||
int newHeightMeasureSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
|
||||
|
||||
// 执行父类测量逻辑(使用重写后的测量规格)
|
||||
super.onMeasure(newWidthMeasureSpec, newHeightMeasureSpec);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写滚动计算方法:实现平滑滚动
|
||||
* 配合 Scroller 实现水平方向的平滑滚动动画(需在滚动时调用 invalidate() 触发)
|
||||
*/
|
||||
@Override
|
||||
public void computeScroll() {
|
||||
// 判断滚动是否正在进行(Scroller 计算当前滚动位置)
|
||||
if (mScroller.computeScrollOffset()) {
|
||||
// 滚动到当前计算的位置(x 轴水平滚动,y 轴固定 0)
|
||||
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
|
||||
// 触发重绘,持续更新滚动状态
|
||||
postInvalidate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 平滑滚动到指定坐标
|
||||
* 基于 Scroller 实现水平方向的平滑滚动(300ms 动画时长)
|
||||
* @param x 目标 x 轴坐标(水平滚动位置)
|
||||
* @param y 目标 y 轴坐标(固定 0,无需垂直滚动)
|
||||
*/
|
||||
public void smoothScrollTo(int x, int y) {
|
||||
// 计算滚动距离(目标坐标 - 当前滚动坐标)
|
||||
int dx = x - getScrollX();
|
||||
int dy = y - getScrollY();
|
||||
|
||||
// 启动平滑滚动:起始坐标(当前滚动位置)、滚动距离、动画时长(300ms)
|
||||
mScroller.startScroll(getScrollX(), getScrollY(), dx, dy, 300);
|
||||
// 触发重绘,启动滚动动画
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算水平滚动总范围(用于滚动条显示比例)
|
||||
* @return 滚动总宽度(所有子项总宽度 + 内边距)
|
||||
*/
|
||||
@Override
|
||||
public int computeHorizontalScrollRange() {
|
||||
return mTotalWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算当前水平滚动偏移量(用于滚动条位置)
|
||||
* @return 当前 x 轴滚动坐标
|
||||
*/
|
||||
@Override
|
||||
public int computeHorizontalScrollOffset() {
|
||||
return getScrollX();
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算水平滚动可视范围(用于滚动条大小)
|
||||
* @return 控件可见宽度(当前显示区域宽度)
|
||||
*/
|
||||
@Override
|
||||
public int computeHorizontalScrollExtent() {
|
||||
return getWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
* 滚动到指定位置的子项(水平方向)
|
||||
* 定位目标子项,计算滚动坐标,执行平滑滚动
|
||||
* @param position 子项索引(从 0 开始,仅当前可见子项有效)
|
||||
*/
|
||||
public void scrollToItem(int position) {
|
||||
// 校验索引有效性(避免数组越界)
|
||||
if (position < 0 || position >= getChildCount()) {
|
||||
LogUtils.d(TAG, "无效的子项索引: " + position);
|
||||
return;
|
||||
}
|
||||
|
||||
View targetView = getChildAt(position); // 获取目标子项
|
||||
int targetLeft = targetView.getLeft(); // 目标子项左边界坐标
|
||||
// 计算目标滚动坐标(子项左边界 - 控件左内边距,确保子项左对齐显示)
|
||||
int scrollX = targetLeft - getPaddingLeft();
|
||||
|
||||
// 修正滚动范围(避免超出总宽度或小于 0)
|
||||
int maxScrollX = mTotalWidth - getWidth(); // 最大滚动坐标(总宽度 - 控件宽度)
|
||||
scrollX = Math.max(0, Math.min(scrollX, maxScrollX));
|
||||
|
||||
// 强制重新布局和绘制(确保子项位置正确)
|
||||
requestLayout();
|
||||
invalidateViews();
|
||||
// 平滑滚动到目标坐标
|
||||
smoothScrollTo(scrollX, 0);
|
||||
|
||||
// 打印滚动日志(调试用)
|
||||
LogUtils.d(TAG, String.format(
|
||||
"滚动到子项索引: %d, 目标滚动X: %d, 总滚动范围: %d",
|
||||
position, scrollX, computeHorizontalScrollRange()
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置滚动到起始位置(最左侧)
|
||||
* 强制重新布局后,平滑滚动到 x=0 坐标
|
||||
*/
|
||||
public void resetScrollToStart() {
|
||||
// 强制重新布局和绘制(确保滚动位置准确)
|
||||
requestLayout();
|
||||
invalidateViews();
|
||||
// 平滑滚动到最左侧(x=0,y=0)
|
||||
smoothScrollTo(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package cc.winboll.studio.libappbase;
|
||||
|
||||
import android.content.Context;
|
||||
import cc.winboll.studio.libappbase.model.TagModel;
|
||||
import dalvik.system.DexFile;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
@@ -15,7 +16,6 @@ import java.lang.reflect.Modifier;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@@ -57,7 +57,7 @@ public class LogUtils {
|
||||
/** 日志配置实体类(封装日志级别等配置) */
|
||||
private static LogUtilsBean sLogConfigBean;
|
||||
/** TAG 过滤映射表(key:TAG 名称;value:是否启用该 TAG 的日志输出) */
|
||||
public static Map<String, Boolean> sTagEnableMap = new HashMap<String, Boolean>();
|
||||
public static ArrayList<TagModel> sTagEnableTagModelList = new ArrayList<TagModel>();
|
||||
|
||||
/**
|
||||
* 初始化日志工具(默认日志级别:Off,不输出日志)
|
||||
@@ -115,15 +115,15 @@ public class LogUtils {
|
||||
sIsInited = true;
|
||||
|
||||
// 打印初始化日志(调试用)
|
||||
d(TAG, String.format("TAG 过滤映射表初始化完成:%s", sTagEnableMap.toString()));
|
||||
d(TAG, String.format("TAG 过滤映射表初始化完成:%s", sTagEnableTagModelList.toString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 TAG 过滤映射表(外部可通过此方法获取所有 TAG 及其启用状态)
|
||||
* @return TAG 名称与启用状态的映射
|
||||
*/
|
||||
public static Map<String, Boolean> getTagEnableMap() {
|
||||
return sTagEnableMap;
|
||||
public static ArrayList<TagModel> getTagEnableTagModelList() {
|
||||
return sTagEnableTagModelList;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -140,9 +140,15 @@ public class LogUtils {
|
||||
String tag = tagSetting.getTag();
|
||||
boolean isEnable = tagSetting.getEnable();
|
||||
// 仅更新已存在的 TAG(避免无效配置)
|
||||
if (sTagEnableMap.containsKey(tag)) {
|
||||
sTagEnableMap.put(tag, isEnable);
|
||||
}
|
||||
for (TagModel tagModel : sTagEnableTagModelList) {
|
||||
if (tagModel.equals(tag)) {
|
||||
tagModel.setChecked(isEnable);
|
||||
}
|
||||
}
|
||||
// if (sTagEnableTagModelList.containsKey(tag)) {
|
||||
// sTagEnableTagModelList.put(tag, isEnable);
|
||||
// }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,11 +159,16 @@ public class LogUtils {
|
||||
private static void saveTagEnableSettings() {
|
||||
ArrayList<LogUtilsClassTAGBean> tagSettingList = new ArrayList<LogUtilsClassTAGBean>();
|
||||
// 遍历映射表,构建配置列表(Java 7 迭代器遍历)
|
||||
Iterator<Map.Entry<String, Boolean>> iterator = sTagEnableMap.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry<String, Boolean> entry = iterator.next();
|
||||
tagSettingList.add(new LogUtilsClassTAGBean(entry.getKey(), entry.getValue()));
|
||||
}
|
||||
for (TagModel tagModel : sTagEnableTagModelList) {
|
||||
tagSettingList.add(new LogUtilsClassTAGBean(tagModel.getTagName(), tagModel.isChecked()));
|
||||
}
|
||||
|
||||
// Iterator<Map.Entry<String, Boolean>> iterator = sTagEnableTagModelList.entrySet().iterator();
|
||||
// while (iterator.hasNext()) {
|
||||
// Map.Entry<String, Boolean> entry = iterator.next();
|
||||
// tagSettingList.add(new LogUtilsClassTAGBean(entry.getKey(), entry.getValue()));
|
||||
// }
|
||||
|
||||
// 保存配置列表到文件
|
||||
LogUtilsClassTAGBean.saveBeanList(sContext, tagSettingList, LogUtilsClassTAGBean.class);
|
||||
}
|
||||
@@ -210,7 +221,8 @@ public class LogUtils {
|
||||
// 获取 TAG 字段的值(静态字段,传入 null 即可)
|
||||
String tagValue = (String) field.get(null);
|
||||
// 添加到映射表,默认禁用
|
||||
sTagEnableMap.put(tagValue, false);
|
||||
sTagEnableTagModelList.add(new TagModel(false, tagValue));
|
||||
// sTagEnableTagModelList.put(tagValue, false);
|
||||
}
|
||||
}
|
||||
} catch (NoClassDefFoundError e) {
|
||||
@@ -235,17 +247,23 @@ public class LogUtils {
|
||||
*/
|
||||
public static void setTagEnable(String tag, boolean isEnable) {
|
||||
// 遍历映射表,更新目标 TAG 的状态(Java 7 迭代器遍历)
|
||||
Iterator<Map.Entry<String, Boolean>> iterator = sTagEnableMap.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry<String, Boolean> entry = iterator.next();
|
||||
if (tag.equals(entry.getKey())) {
|
||||
entry.setValue(isEnable);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (TagModel tagModel : sTagEnableTagModelList) {
|
||||
if (tagModel.getTagName().equals(tag)) {
|
||||
tagModel.setChecked(isEnable);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Iterator<Map.Entry<String, Boolean>> iterator = sTagEnableTagModelList.entrySet().iterator();
|
||||
// while (iterator.hasNext()) {
|
||||
// Map.Entry<String, Boolean> entry = iterator.next();
|
||||
// if (tag.equals(entry.getKey())) {
|
||||
// entry.setValue(isEnable);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// 保存配置到文件(持久化)
|
||||
saveTagEnableSettings();
|
||||
d(TAG, String.format("TAG 配置更新:%s", sTagEnableMap.toString()));
|
||||
d(TAG, String.format("TAG 配置更新:%s", sTagEnableTagModelList.toString()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -254,14 +272,17 @@ public class LogUtils {
|
||||
*/
|
||||
public static void setAllTagsEnable(boolean isEnable) {
|
||||
// 遍历映射表,批量更新所有 TAG 的状态(Java 7 迭代器遍历)
|
||||
Iterator<Map.Entry<String, Boolean>> iterator = sTagEnableMap.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry<String, Boolean> entry = iterator.next();
|
||||
entry.setValue(isEnable);
|
||||
}
|
||||
for (TagModel tagModel : sTagEnableTagModelList) {
|
||||
tagModel.setChecked(isEnable);
|
||||
}
|
||||
// Iterator<Map.Entry<String, Boolean>> iterator = sTagEnableTagModelList.entrySet().iterator();
|
||||
// while (iterator.hasNext()) {
|
||||
// Map.Entry<String, Boolean> entry = iterator.next();
|
||||
// entry.setValue(isEnable);
|
||||
// }
|
||||
// 保存配置到文件(持久化)
|
||||
saveTagEnableSettings();
|
||||
d(TAG, String.format("所有 TAG 配置更新:%s", sTagEnableMap.toString()));
|
||||
d(TAG, String.format("所有 TAG 配置更新:%s", sTagEnableTagModelList.toString()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -295,10 +316,20 @@ public class LogUtils {
|
||||
if (!sIsInited) {
|
||||
return false;
|
||||
}
|
||||
if(sTagEnableTagModelList == null || sTagEnableTagModelList.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
boolean isTagExist = false;
|
||||
for (TagModel tagModel : sTagEnableTagModelList) {
|
||||
if(tagModel.equals(tag)) {
|
||||
isTagExist = true;
|
||||
}
|
||||
}
|
||||
// TAG 未配置或未启用:不输出
|
||||
if (sTagEnableMap.get(tag) == null || !sTagEnableMap.get(tag)) {
|
||||
if (!isTagExist) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 日志级别未达到:不输出
|
||||
if (!isLevelMatched(logLevel)) {
|
||||
return false;
|
||||
|
||||
@@ -27,6 +27,7 @@ import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import cc.winboll.studio.libappbase.model.TagModel;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
@@ -58,7 +59,7 @@ public class LogView extends RelativeLayout {
|
||||
/** 全选 TAG 开关(控制所有 TAG 的启用/禁用) */
|
||||
private CheckBox mSelectAllTagCb;
|
||||
/** TAG 列表适配器(绑定 TAG 数据与视图,处理勾选状态) */
|
||||
private TAGListAdapter mTagListAdapter;
|
||||
//private TAGListAdapter mTagListAdapter;
|
||||
/** 日志监听线程(监听日志文件变化,触发视图刷新) */
|
||||
private LogViewThread mLogViewThread;
|
||||
/** 日志视图 Handler(主线程更新 UI,避免跨线程操作) */
|
||||
@@ -68,7 +69,7 @@ public class LogView extends RelativeLayout {
|
||||
/** 日志级别适配器(绑定 LogUtils.LOG_LEVEL 枚举与 Spinner) */
|
||||
private ArrayAdapter<CharSequence> mLogLevelAdapter;
|
||||
/** TAG 水平列表视图(横向展示所有 TAG,支持滚动) */
|
||||
private HorizontalListView mTagHorizontalListView;
|
||||
private TagsHorizontalListView mTagsHorizontalListView;
|
||||
|
||||
// ====================== 构造方法(初始化视图) ======================
|
||||
public LogView(Context context) {
|
||||
@@ -162,7 +163,7 @@ public class LogView extends RelativeLayout {
|
||||
mLogLevelSpinner = rootView.findViewById(R.id.viewlogSpinner1);
|
||||
mTextSelectableCb = rootView.findViewById(R.id.viewlogCheckBoxSelectable);
|
||||
mSelectAllTagCb = rootView.findViewById(R.id.viewlogCheckBox1);
|
||||
mTagHorizontalListView = rootView.findViewById(R.id.tags_listview);
|
||||
mTagsHorizontalListView = rootView.findViewById(R.id.tags_listview);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -177,15 +178,17 @@ public class LogView extends RelativeLayout {
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
String searchText = s.toString().trim();
|
||||
LogUtils.d(TAG, "TAG 搜索内容:" + searchText);
|
||||
if (!searchText.isEmpty()) {
|
||||
// 搜索文本非空,定位匹配的 TAG
|
||||
scrollToTargetTag(searchText);
|
||||
} else {
|
||||
// 搜索文本为空,重置滚动位置
|
||||
HorizontalScrollView parentHs = findViewById(R.id.viewlogHorizontalScrollView1);
|
||||
parentHs.smoothScrollTo(0, 0);
|
||||
mTagHorizontalListView.resetScrollToStart();
|
||||
}
|
||||
// 定位匹配的 TAG
|
||||
scrollToTargetTag(searchText);
|
||||
// if (!searchText.isEmpty()) {
|
||||
// // 搜索文本非空,定位匹配的 TAG
|
||||
// scrollToTargetTag(searchText);
|
||||
// } else {
|
||||
// // 搜索文本为空,重置滚动位置
|
||||
// HorizontalScrollView parentHs = findViewById(R.id.viewlogHorizontalScrollView1);
|
||||
// parentHs.smoothScrollTo(0, 0);
|
||||
// mTagsHorizontalListView.resetScrollToStart();
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -277,16 +280,16 @@ public class LogView extends RelativeLayout {
|
||||
*/
|
||||
private void initTagListView() {
|
||||
// 获取 LogUtils 中的 TAG 启用状态映射表
|
||||
Map<String, Boolean> tagEnableMap = LogUtils.getTagEnableMap();
|
||||
ArrayList<TagModel> tagEnableTagModel = LogUtils.getTagEnableTagModelList();
|
||||
// 判断是否所有 TAG 都已启用(初始化全选开关状态)
|
||||
boolean isAllTagEnabled = isAllTagsEnabled(tagEnableMap);
|
||||
boolean isAllTagEnabled = isAllTagsEnabled(tagEnableTagModel);
|
||||
mSelectAllTagCb.setChecked(isAllTagEnabled);
|
||||
|
||||
// 初始化 TAG 水平列表(设置垂直偏移,绑定适配器)
|
||||
mTagHorizontalListView.setVerticalOffset(10);
|
||||
mTagListAdapter = new TAGListAdapter(mContext, tagEnableMap);
|
||||
mTagHorizontalListView.setAdapter(mTagListAdapter);
|
||||
mTagListAdapter.notifyDataSetChanged(); // 刷新列表数据
|
||||
//mTagHorizontalListView.setVerticalOffset(10);
|
||||
//mTagListAdapter = new TAGListAdapter(mContext, tagEnableMap);
|
||||
mTagsHorizontalListView.setTagList(tagEnableTagModel);
|
||||
//mTagListAdapter.notifyDataSetChanged(); // 刷新列表数据
|
||||
|
||||
// 全选 TAG 开关监听(点击时启用/禁用所有 TAG)
|
||||
mSelectAllTagCb.setOnClickListener(new View.OnClickListener() {
|
||||
@@ -294,7 +297,9 @@ public class LogView extends RelativeLayout {
|
||||
public void onClick(View v) {
|
||||
boolean isSelectAll = mSelectAllTagCb.isChecked();
|
||||
LogUtils.setAllTagsEnable(isSelectAll); // 批量更新所有 TAG 状态
|
||||
mTagListAdapter.reload(); // 重新加载 TAG 数据并刷新视图
|
||||
|
||||
LogUtils.d(TAG, "mTagListAdapter.reload() not yet.");
|
||||
//mTagListAdapter.reload(); // 重新加载 TAG 数据并刷新视图
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -304,12 +309,17 @@ public class LogView extends RelativeLayout {
|
||||
* @param tagEnableMap TAG 启用状态映射表
|
||||
* @return true:所有 TAG 均启用;false:存在未启用的 TAG
|
||||
*/
|
||||
private boolean isAllTagsEnabled(Map<String, Boolean> tagEnableMap) {
|
||||
for (Map.Entry<String, Boolean> entry : tagEnableMap.entrySet()) {
|
||||
if (!entry.getValue()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
private boolean isAllTagsEnabled(ArrayList<TagModel> tagEnableTagModelList) {
|
||||
for(TagModel tagModel : tagEnableTagModelList) {
|
||||
if(tagModel.isChecked()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// for (Map.Entry<String, Boolean> entry : tagEnableTagModelList.entrySet()) {
|
||||
// if (!entry.getValue()) {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -344,19 +354,19 @@ public class LogView extends RelativeLayout {
|
||||
* @param prefix 搜索文本(TAG 前缀)
|
||||
*/
|
||||
private void scrollToTargetTag(final String prefix) {
|
||||
if (mTagListAdapter == null || prefix == null || prefix.isEmpty()) {
|
||||
if (prefix == null || prefix.isEmpty()) {
|
||||
LogUtils.d(TAG, "TAG 搜索参数为空,无法定位");
|
||||
return;
|
||||
}
|
||||
|
||||
final List<TAGItemModel> tagItemList = mTagListAdapter.getItemList();
|
||||
mTagHorizontalListView.post(new Runnable() {
|
||||
final List<TagModel> tagItemList = mTagsHorizontalListView.getTagList();
|
||||
mTagsHorizontalListView.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
int targetPosition = -1;
|
||||
// 遍历 TAG 列表,查找前缀匹配的 TAG(忽略大小写)
|
||||
for (int i = 0; i < tagItemList.size(); i++) {
|
||||
String tag = tagItemList.get(i).getTag();
|
||||
String tag = tagItemList.get(i).getTagName();
|
||||
if (tag != null && tag.toLowerCase().startsWith(prefix.toLowerCase())) {
|
||||
targetPosition = i;
|
||||
break;
|
||||
@@ -366,11 +376,13 @@ public class LogView extends RelativeLayout {
|
||||
if (targetPosition != -1) {
|
||||
final int targetPositionFinal = targetPosition;
|
||||
// 延迟滚动(确保布局完成,避免滚动失效)
|
||||
mTagHorizontalListView.postDelayed(new Runnable() {
|
||||
mTagsHorizontalListView.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
LogUtils.d(TAG, "定位到 TAG 位置:" + targetPositionFinal);
|
||||
mTagHorizontalListView.scrollToItem(targetPositionFinal);
|
||||
|
||||
LogUtils.d(TAG, "mTagsHorizontalListView.scrollToItem(targetPositionFinal); not yet.");
|
||||
//mTagsHorizontalListView.scrollToItem(targetPositionFinal);
|
||||
}
|
||||
}, 100);
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
package cc.winboll.studio.libappbase;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.widget.HorizontalScrollView;
|
||||
import android.widget.LinearLayout;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.model.TagModel;
|
||||
import cc.winboll.studio.libappbase.views.TagItemView;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||
* @Date 2025/11/11 20:26
|
||||
* @Describe 水平排列 TagItemView 列表控件(继承 HorizontalScrollView)
|
||||
* 核心:以 HorizontalScrollView 为父容器,内部用 LinearLayout 水平承载 TagItemView,支持左右拉动滚动
|
||||
*/
|
||||
public class TagsHorizontalListView extends HorizontalScrollView {
|
||||
public static final String TAG = "TagsHorizontalListView";
|
||||
|
||||
// 内部水平容器(承载所有 TagItemView,核心子布局)
|
||||
private LinearLayout mTagContainer;
|
||||
// Tag 数据列表(存储所有 Tag 数据,与视图联动)
|
||||
private List<TagModel> mTagList;
|
||||
// Tag 选中状态全局监听(供外部获取所有 Tag 选中变化)
|
||||
private OnTagCheckedChangeListener mGlobalCheckedListener;
|
||||
// Tag 项之间的水平间距(默认 10dp,可外部设置)
|
||||
private int mTagHorizontalSpacing = 10;
|
||||
|
||||
// 构造方法(Java 7 完整兼容)
|
||||
public TagsHorizontalListView(Context context) {
|
||||
super(context);
|
||||
initView();
|
||||
}
|
||||
|
||||
public TagsHorizontalListView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initView();
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public TagsHorizontalListView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
initView();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化视图:创建内部水平容器,配置 ScrollView 基础属性
|
||||
*/
|
||||
private void initView() {
|
||||
// 1. 配置 HorizontalScrollView 基础属性
|
||||
setHorizontalScrollBarEnabled(true); // 显示水平滚动条
|
||||
setVerticalScrollBarEnabled(false); // 禁用垂直滚动条
|
||||
setOverScrollMode(OVER_SCROLL_NEVER); // 禁用过度滚动效果(避免边缘阴影)
|
||||
|
||||
// 2. 创建内部水平容器(LinearLayout,承载所有 TagItemView)
|
||||
mTagContainer = new LinearLayout(getContext());
|
||||
mTagContainer.setOrientation(LinearLayout.HORIZONTAL); // 水平排列
|
||||
mTagContainer.setGravity(Gravity.CENTER_VERTICAL); // 子项垂直居中
|
||||
// 设置容器内边距(左右 16dp,上下 8dp,避免 Tag 贴边)
|
||||
int padding = dp2px(16);
|
||||
mTagContainer.setPadding(padding, dp2px(8), padding, dp2px(8));
|
||||
// 将容器添加到 HorizontalScrollView 中(ScrollView 只能有一个直接子View)
|
||||
addView(mTagContainer, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
|
||||
|
||||
// 3. 初始化 Tag 数据列表
|
||||
mTagList = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 Tag 数据列表(核心方法:数据驱动视图,批量创建 TagItemView)
|
||||
* @param tagList 所有 Tag 数据(含 isChecked + tagName)
|
||||
*/
|
||||
public void setTagList(List<TagModel> tagList) {
|
||||
// 清空原有数据和视图,避免重复添加
|
||||
clearAllTags();
|
||||
|
||||
if (tagList == null || tagList.isEmpty()) {
|
||||
LogUtils.d(TAG, "Tag 数据列表为空,不创建视图");
|
||||
return;
|
||||
}
|
||||
|
||||
// 保存新数据
|
||||
mTagList.addAll(tagList);
|
||||
|
||||
// 批量创建 TagItemView 并添加到容器
|
||||
for (int i = 0; i < mTagList.size(); i++) {
|
||||
final int position = i;
|
||||
TagModel tagModel = mTagList.get(position);
|
||||
// 创建单个 Tag 控件
|
||||
TagItemView tagItemView = new TagItemView(getContext());
|
||||
// 绑定数据(自动显示标签名称和选中状态)
|
||||
tagItemView.setTagModel(tagModel);
|
||||
// 设置 Tag 项布局参数(添加水平间距)
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||||
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
||||
// 除了最后一个 Tag,其余都添加右侧间距
|
||||
if (position != mTagList.size() - 1) {
|
||||
params.rightMargin = dp2px(mTagHorizontalSpacing);
|
||||
}
|
||||
tagItemView.setLayoutParams(params);
|
||||
|
||||
// 绑定单个 Tag 选中状态监听(同步到全局监听)
|
||||
tagItemView.setOnTagCheckedChangeListener(new TagItemView.OnTagCheckedChangeListener() {
|
||||
@Override
|
||||
public void onTagCheckedChanged(TagItemView tagItemView, TagModel tagModel, boolean isChecked) {
|
||||
// 触发全局监听,传递当前 Tag 位置、控件、数据
|
||||
if (mGlobalCheckedListener != null) {
|
||||
mGlobalCheckedListener.onTagCheckedChanged(position, tagItemView, tagModel, isChecked);
|
||||
}
|
||||
// 同步数据到列表(确保数据与视图一致)
|
||||
mTagList.set(position, tagModel);
|
||||
}
|
||||
});
|
||||
|
||||
// 将 Tag 控件添加到内部水平容器
|
||||
mTagContainer.addView(tagItemView);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前所有 Tag 数据列表(含最新选中状态)
|
||||
* @return List<TagModel> 完整数据列表
|
||||
*/
|
||||
public List<TagModel> getTagList() {
|
||||
return mTagList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有 Tag 数据和视图(避免内存泄漏)
|
||||
*/
|
||||
public void clearAllTags() {
|
||||
// 清空视图
|
||||
if (mTagContainer != null) {
|
||||
mTagContainer.removeAllViews();
|
||||
}
|
||||
// 清空数据
|
||||
if (mTagList != null) {
|
||||
mTagList.clear();
|
||||
}
|
||||
// 重置滚动位置到最左侧
|
||||
scrollTo(0, 0);
|
||||
LogUtils.d(TAG, "已清空所有 Tag 数据和视图");
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 Tag 项之间的水平间距(单位:dp,外部调用更直观)
|
||||
* @param spacingDp 水平间距(dp)
|
||||
*/
|
||||
public void setTagHorizontalSpacing(int spacingDp) {
|
||||
this.mTagHorizontalSpacing = spacingDp;
|
||||
// 重新布局(生效间距)
|
||||
if (mTagContainer != null) {
|
||||
mTagContainer.requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 滚动到指定位置的 Tag 项(水平平滑滚动)
|
||||
* @param position Tag 索引(从 0 开始)
|
||||
*/
|
||||
public void scrollToTag(int position) {
|
||||
// 校验索引有效性
|
||||
if (position < 0 || position >= mTagList.size() || mTagContainer == null) {
|
||||
LogUtils.d(TAG, "无效的 Tag 索引: " + position);
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取目标 Tag 控件
|
||||
View targetTag = mTagContainer.getChildAt(position);
|
||||
if (targetTag == null) {
|
||||
LogUtils.e(TAG, "获取目标 Tag 控件失败,无法滚动");
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算滚动目标坐标(目标 Tag 左边界 - 容器左内边距,确保 Tag 左对齐显示)
|
||||
int scrollX = targetTag.getLeft() - mTagContainer.getPaddingLeft();
|
||||
// 平滑滚动到目标位置(Java 7 兼容,使用 ScrollView 原生方法)
|
||||
smoothScrollTo(scrollX, 0);
|
||||
|
||||
LogUtils.d(TAG, "已平滑滚动到 Tag 索引: " + position);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置滚动到最左侧
|
||||
*/
|
||||
public void resetScrollToStart() {
|
||||
smoothScrollTo(0, 0);
|
||||
LogUtils.d(TAG, "已重置滚动到最左侧");
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置全局 Tag 选中状态监听(供外部获取所有 Tag 变化)
|
||||
* @param listener 全局监听接口
|
||||
*/
|
||||
public void setOnTagCheckedChangeListener(OnTagCheckedChangeListener listener) {
|
||||
this.mGlobalCheckedListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* 全局 Tag 选中状态监听接口
|
||||
* 携带索引、控件、数据、选中状态,便于外部批量处理
|
||||
*/
|
||||
public interface OnTagCheckedChangeListener {
|
||||
/**
|
||||
* 选中状态变化回调
|
||||
* @param position 当前 Tag 索引(从 0 开始)
|
||||
* @param tagItemView 当前 Tag 控件
|
||||
* @param tagModel 绑定的数据模型(含最新状态)
|
||||
* @param isChecked 最新选中状态
|
||||
*/
|
||||
void onTagCheckedChanged(int position, TagItemView tagItemView, TagModel tagModel, boolean isChecked);
|
||||
}
|
||||
|
||||
/**
|
||||
* 工具方法:dp 转 px(适配不同分辨率屏幕)
|
||||
* @param dpValue dp 值
|
||||
* @return 对应的 px 值
|
||||
*/
|
||||
private int dp2px(float dpValue) {
|
||||
if (dpValue <= 0) {
|
||||
return 0;
|
||||
}
|
||||
final float scale = getContext().getResources().getDisplayMetrics().density;
|
||||
return (int) (dpValue * scale + 0.5f); // 四舍五入,确保精度
|
||||
}
|
||||
|
||||
/**
|
||||
* 生命周期方法:控件销毁时清空资源,避免内存泄漏
|
||||
*/
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
clearAllTags();
|
||||
mGlobalCheckedListener = null; // 置空监听,避免内存泄漏
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package cc.winboll.studio.libappbase.model;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||
* @Date 2025/11/28 20:36
|
||||
* @Describe Tag 数据模型
|
||||
* 存储 TagItemView 所需的核心数据:选中状态 + 标签名称
|
||||
*/
|
||||
public class TagModel {
|
||||
public static final String TAG = "TagModel";
|
||||
private boolean isChecked; // 选中状态(true:选中,false:未选中)
|
||||
private String tagName; // 标签名称(显示文本)
|
||||
|
||||
// Java 7 无参构造(便于外部实例化)
|
||||
public TagModel() {
|
||||
}
|
||||
|
||||
// Java 7 有参构造(快速初始化数据)
|
||||
public TagModel(boolean isChecked, String tagName) {
|
||||
this.isChecked = isChecked;
|
||||
this.tagName = tagName;
|
||||
}
|
||||
|
||||
// Getter/Setter 方法(Java 7 标准写法,无 lambda 简化)
|
||||
public boolean isChecked() {
|
||||
return isChecked;
|
||||
}
|
||||
|
||||
public void setChecked(boolean checked) {
|
||||
isChecked = checked;
|
||||
}
|
||||
|
||||
public String getTagName() {
|
||||
return tagName;
|
||||
}
|
||||
|
||||
public void setTagName(String tagName) {
|
||||
this.tagName = tagName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
package cc.winboll.studio.libappbase.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import cc.winboll.studio.libappbase.model.TagModel;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
|
||||
* @Date 2025/11/28 20:37
|
||||
* @Describe Tag 单项展示控件(继承 CheckBox)
|
||||
* 单独承载单个 Tag 数据,关联 TagModel,实现选中状态+标签名称的联动展示
|
||||
*/
|
||||
public class TagItemView extends CheckBox {
|
||||
public static final String TAG = "TagItemView";
|
||||
|
||||
private TagModel mTagModel; // 绑定的 Tag 数据模型
|
||||
// 选中状态变化监听(供外部回调)
|
||||
private OnTagCheckedChangeListener mCheckedChangeListener;
|
||||
|
||||
// 构造方法(Java 7 完整兼容,覆盖所有重载)
|
||||
public TagItemView(Context context) {
|
||||
super(context);
|
||||
initView();
|
||||
}
|
||||
|
||||
public TagItemView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initView();
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public TagItemView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
initView();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件(默认样式 + 事件监听)
|
||||
*/
|
||||
private void initView() {
|
||||
// 初始化默认样式(可根据需求调整,如字体大小、内边距)
|
||||
setTextSize(14); // 标签字体大小
|
||||
setPadding(20, 10, 20, 10); // 内边距(避免文本贴边)
|
||||
|
||||
// 绑定 CheckBox 选中状态变化事件(联动 TagModel)
|
||||
setOnCheckedChangeListener(new OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
// 同步选中状态到数据模型
|
||||
if (mTagModel != null) {
|
||||
mTagModel.setChecked(isChecked);
|
||||
}
|
||||
// 触发外部监听回调
|
||||
if (mCheckedChangeListener != null) {
|
||||
mCheckedChangeListener.onTagCheckedChanged(TagItemView.this, mTagModel, isChecked);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定 Tag 数据模型(核心方法:数据驱动视图)
|
||||
* @param tagModel 单个 Tag 数据(含 isChecked + tagName)
|
||||
*/
|
||||
public void setTagModel(TagModel tagModel) {
|
||||
if (tagModel == null) {
|
||||
return;
|
||||
}
|
||||
this.mTagModel = tagModel;
|
||||
// 同步数据到视图:标签名称 + 选中状态
|
||||
setText(tagModel.getTagName());
|
||||
setChecked(tagModel.isChecked());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前绑定的 Tag 数据模型
|
||||
* @return TagModel (含最新选中状态和标签名称)
|
||||
*/
|
||||
public TagModel getTagModel() {
|
||||
return mTagModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置选中状态变化监听(供外部获取选中事件)
|
||||
* @param listener 监听接口实例
|
||||
*/
|
||||
public void setOnTagCheckedChangeListener(OnTagCheckedChangeListener listener) {
|
||||
this.mCheckedChangeListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义监听接口:Tag 选中状态变化时回调
|
||||
* 携带当前控件、数据模型、选中状态,便于外部处理
|
||||
*/
|
||||
public interface OnTagCheckedChangeListener {
|
||||
/**
|
||||
* 选中状态变化回调
|
||||
* @param tagItemView 当前 Tag 控件
|
||||
* @param tagModel 绑定的数据模型(含最新状态)
|
||||
* @param isChecked 最新选中状态
|
||||
*/
|
||||
void onTagCheckedChanged(TagItemView tagItemView, TagModel tagModel, boolean isChecked);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,15 +91,10 @@
|
||||
android:id="@+id/tagsearch_et"/>
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/bg_border"
|
||||
android:scrollbars="none"
|
||||
android:padding="5dp"
|
||||
android:layout_weight="1.0"
|
||||
android:id="@+id/viewlogHorizontalScrollView1">
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<cc.winboll.studio.libappbase.HorizontalListView
|
||||
<cc.winboll.studio.libappbase.TagsHorizontalListView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/tags_listview"/>
|
||||
|
||||
Reference in New Issue
Block a user