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
|
#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
|
stageCount=8
|
||||||
libraryProject=
|
libraryProject=
|
||||||
baseVersion=15.12
|
baseVersion=15.12
|
||||||
publishVersion=15.12.7
|
publishVersion=15.12.7
|
||||||
buildCount=0
|
buildCount=9
|
||||||
baseBetaVersion=15.12.8
|
baseBetaVersion=15.12.8
|
||||||
|
|||||||
@@ -11,20 +11,29 @@ import android.view.View;
|
|||||||
import cc.winboll.studio.libappbase.LogUtils;
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
import cc.winboll.studio.libappbase.LogView;
|
import cc.winboll.studio.libappbase.LogView;
|
||||||
import cc.winboll.studio.mymessagemanager.R;
|
import cc.winboll.studio.mymessagemanager.R;
|
||||||
|
import cc.winboll.studio.mymessagemanager.views.ProtectModeTextView;
|
||||||
|
|
||||||
public class UnitTestActivity extends Activity {
|
public class UnitTestActivity extends Activity {
|
||||||
|
|
||||||
public static final String TAG = "UnitTestActivity";
|
public static final String TAG = "UnitTestActivity";
|
||||||
|
|
||||||
LogView mLogView;
|
LogView mLogView;
|
||||||
|
// 新增自定义控件
|
||||||
|
ProtectModeTextView mProtectModeTv;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_unittest);
|
setContentView(R.layout.activity_unittest);
|
||||||
|
|
||||||
mLogView = findViewById(R.id.logview);
|
mLogView = findViewById(R.id.logview);
|
||||||
mLogView.start();
|
mLogView.start();
|
||||||
|
|
||||||
|
// 初始化ProtectModeTextView
|
||||||
|
mProtectModeTv = findViewById(R.id.protect_mode_tv);
|
||||||
|
// 设置测试文本,可自行修改
|
||||||
|
String testText = "abcdefghijklmnopqrstuvwxyz消息管理 隐私保护 文本随机组合 滑动刻度测试1234567890";
|
||||||
|
mProtectModeTv.setContentText(testText);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onMain(View view) {
|
public void onMain(View view) {
|
||||||
@@ -34,3 +43,4 @@ public class UnitTestActivity extends Activity {
|
|||||||
AddressUtils_Test.main(this);
|
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"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<ScrollView
|
<ScrollView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="500dp">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="10dp">
|
||||||
|
|
||||||
<LinearLayout
|
<!-- 新增:自定义ProtectModeTextView -->
|
||||||
android:orientation="horizontal"
|
<cc.winboll.studio.mymessagemanager.views.ProtectModeTextView
|
||||||
android:layout_width="match_parent"
|
android:id="@+id/protect_mode_tv"
|
||||||
android:layout_height="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:gravity="right">
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="15dp"/>
|
||||||
|
|
||||||
<Button
|
<LinearLayout
|
||||||
android:layout_width="wrap_content"
|
android:orientation="horizontal"
|
||||||
android:layout_height="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:text="Test Main"
|
android:layout_height="wrap_content"
|
||||||
android:onClick="onMain"
|
android:gravity="right">
|
||||||
android:textAllCaps="false"/>
|
|
||||||
|
|
||||||
</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
|
</ScrollView>
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="0dp"
|
<cc.winboll.studio.libappbase.LogView
|
||||||
android:layout_weight="1.0"
|
android:layout_width="match_parent"
|
||||||
android:id="@+id/logview"/>
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1.0"
|
||||||
|
android:id="@+id/logview"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</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