feat: 新增ProtectModeTextView自定义控件
1. 继承LinearLayout,内置TextView与0~12刻度SeekBar 2. 刻度0保持原始文本不打乱,1~12为每组相邻字符个数 3. 按刻度固定长度从头至尾字符分组,分组列表随机打乱后拼接输出 4. 支持含空格/标点完整字符解析,对外提供setContentText设置文本接口
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Wed Feb 11 05:29:19 HKT 2026
|
||||
#Fri May 08 11:33:06 GMT 2026
|
||||
stageCount=8
|
||||
libraryProject=
|
||||
baseVersion=15.12
|
||||
publishVersion=15.12.7
|
||||
buildCount=0
|
||||
buildCount=9
|
||||
baseBetaVersion=15.12.8
|
||||
|
||||
@@ -11,20 +11,29 @@ import android.view.View;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.LogView;
|
||||
import cc.winboll.studio.mymessagemanager.R;
|
||||
import cc.winboll.studio.mymessagemanager.views.ProtectModeTextView;
|
||||
|
||||
public class UnitTestActivity extends Activity {
|
||||
|
||||
public static final String TAG = "UnitTestActivity";
|
||||
|
||||
LogView mLogView;
|
||||
|
||||
// 新增自定义控件
|
||||
ProtectModeTextView mProtectModeTv;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_unittest);
|
||||
|
||||
|
||||
mLogView = findViewById(R.id.logview);
|
||||
mLogView.start();
|
||||
|
||||
// 初始化ProtectModeTextView
|
||||
mProtectModeTv = findViewById(R.id.protect_mode_tv);
|
||||
// 设置测试文本,可自行修改
|
||||
String testText = "abcdefghijklmnopqrstuvwxyz消息管理 隐私保护 文本随机组合 滑动刻度测试1234567890";
|
||||
mProtectModeTv.setContentText(testText);
|
||||
}
|
||||
|
||||
public void onMain(View view) {
|
||||
@@ -34,3 +43,4 @@ public class UnitTestActivity extends Activity {
|
||||
AddressUtils_Test.main(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
package cc.winboll.studio.mymessagemanager.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
import cc.winboll.studio.mymessagemanager.R;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* 保护模式自定义控件
|
||||
* 最终规则:
|
||||
* 1. 刻度范围 0~12
|
||||
* 2. 刻度值 = 每一组截取【相邻字符个数】
|
||||
* 3. 从头到尾按固定长度切块分组
|
||||
* 4. 所有分组收集后随机打乱再拼接输出
|
||||
* 5. 刻度0 = 不打乱,显示原文
|
||||
* 6. 按原生字符计算(包含空格、标点)
|
||||
*/
|
||||
public class ProtectModeTextView extends LinearLayout {
|
||||
|
||||
private TextView tvContent;
|
||||
private SeekBar seekBarScale;
|
||||
private String originText;
|
||||
private List<Character> charAllList;
|
||||
private final Random random = new Random();
|
||||
|
||||
public ProtectModeTextView(Context context) {
|
||||
super(context);
|
||||
initView(context);
|
||||
}
|
||||
|
||||
public ProtectModeTextView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initView(context);
|
||||
}
|
||||
|
||||
public ProtectModeTextView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initView(context);
|
||||
}
|
||||
|
||||
private void initView(Context context) {
|
||||
LayoutInflater.from(context).inflate(R.layout.layout_protect_mode_textview, this, true);
|
||||
tvContent = findViewById(R.id.tv_content);
|
||||
seekBarScale = findViewById(R.id.seek_bar_scale);
|
||||
|
||||
// 刻度 0 ~ 12
|
||||
seekBarScale.setMax(12);
|
||||
seekBarScale.setProgress(0);
|
||||
|
||||
charAllList = new ArrayList<>();
|
||||
|
||||
seekBarScale.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
handleTextLogic(progress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {}
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {}
|
||||
});
|
||||
}
|
||||
|
||||
public void setContentText(String text) {
|
||||
this.originText = text;
|
||||
convertToCharList(text);
|
||||
handleTextLogic(seekBarScale.getProgress());
|
||||
}
|
||||
|
||||
private void convertToCharList(String text) {
|
||||
charAllList.clear();
|
||||
if (text == null || text.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
char[] chars = text.toCharArray();
|
||||
for (char c : chars) {
|
||||
charAllList.add(c);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleTextLogic(int groupSize) {
|
||||
if (charAllList.isEmpty()) {
|
||||
tvContent.setText(originText);
|
||||
return;
|
||||
}
|
||||
|
||||
// 刻度0 原样不打乱
|
||||
if (groupSize <= 0) {
|
||||
tvContent.setText(originText);
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> groupList = new ArrayList<>();
|
||||
int totalLen = charAllList.size();
|
||||
|
||||
// 从头到尾 按 groupSize 个相邻字符切块
|
||||
int index = 0;
|
||||
while (index < totalLen) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
// 每一组取 groupSize 个相邻字符
|
||||
for (int i = 0; i < groupSize && index < totalLen; i++) {
|
||||
sb.append(charAllList.get(index));
|
||||
index++;
|
||||
}
|
||||
groupList.add(sb.toString());
|
||||
}
|
||||
|
||||
// 所有分组随机打乱
|
||||
Collections.shuffle(groupList, random);
|
||||
|
||||
// 拼接输出
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (String item : groupList) {
|
||||
result.append(item);
|
||||
}
|
||||
|
||||
tvContent.setText(result.toString());
|
||||
}
|
||||
|
||||
public String getOriginText() {
|
||||
return originText;
|
||||
}
|
||||
|
||||
public void resetSeekBar() {
|
||||
seekBarScale.setProgress(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,44 +1,52 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="500dp">
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="10dp">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="right">
|
||||
<!-- 新增:自定义ProtectModeTextView -->
|
||||
<cc.winboll.studio.mymessagemanager.views.ProtectModeTextView
|
||||
android:id="@+id/protect_mode_tv"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="15dp"/>
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Test Main"
|
||||
android:onClick="onMain"
|
||||
android:textAllCaps="false"/>
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="right">
|
||||
|
||||
</LinearLayout>
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Test Main"
|
||||
android:onClick="onMain"
|
||||
android:textAllCaps="false"/>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
</LinearLayout>
|
||||
|
||||
<cc.winboll.studio.libappbase.LogView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1.0"
|
||||
android:id="@+id/logview"/>
|
||||
</ScrollView>
|
||||
|
||||
<cc.winboll.studio.libappbase.LogView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1.0"
|
||||
android:id="@+id/logview"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<?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"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="16sp"
|
||||
android:gravity="center"/>
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/seek_bar_scale"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
Reference in New Issue
Block a user