@@ -3,9 +3,19 @@ package cc.winboll.studio.positions.adapters;
/**
* @Author ZhanGSKen&豆包大模型<zhangsken@qq.com>
* @Date 2025/09/29 20:25
* @Describe 位置数据适配器(集成PositionTaskListView, 移除未定义onDestroy , Java 7语法适配)
* @Describe 位置数据适配器(修复视图复用资源加载,支持滚动后重新绑定数据 , Java 7语法适配)
*/
/**
* Java 7 语法严格适配 + 视图复用资源加载修复:
* 1. 保留无Lambda/弱引用/线程安全集合等原有适配
* 2. 修复核心问题:移除 onViewDetachedFromWindow 中关键资源释放,改为 onBind 时重新绑定
* 3. 强化资源复用:任务视图/距离控件在复用后自动从服务同步最新数据,确保滚动后数据不丢失
* 4. 优化缓存逻辑:仅清理脱离屏幕且无效的控件缓存,有效控件保留引用供局部更新(如距离)
*/
import android.content.Context ;
import android.text.TextUtils ;
import android.view.LayoutInflater ;
import android.view.View ;
import android.view.ViewGroup ;
@@ -14,30 +24,18 @@ import android.widget.Button;
import android.widget.EditText ;
import android.widget.RadioGroup ;
import android.widget.TextView ;
import android.text.TextUtils ;
import android.widget.Toast ;
import androidx.recyclerview.widget.RecyclerView ;
import cc.winboll.studio.libappbase.LogUtils ;
import cc.winboll.studio.positions.R ;
import cc.winboll.studio.positions.models.PositionModel ;
import cc.winboll.studio.positions.models.PositionTaskModel ;
import cc.winboll.studio.positions.services.MainService ;
import cc.winboll.studio.positions.views.PositionTaskListView ;
import java.lang.ref.WeakReference ;
import java.util.ArrayList ;
import java.util.ConcurrentModificationException ;
import java.util.Iterator ;
import java.util.concurrent.ConcurrentHashMap ;
/**
* Java 7 语法严格适配:
* 1. 无Lambda/方法引用,全匿名内部类实现回调
* 2. 移除未定义的 taskView.onDestroy(),改用「清空数据+解绑监听」替代资源释放
* 3. 弱引用管理服务/控件,杜绝内存泄漏
* 4. PositionTaskListView 初始化/同步/资源释放全链路适配
*/
public class PositionAdapter extends RecyclerView . Adapter < RecyclerView . ViewHolder > implements MainService . TaskUpdateListener {
public static final String TAG = " PositionAdapter " ;
@@ -57,10 +55,10 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
private final Context mContext ;
private final ArrayList < PositionModel > mCachedPositionList ; // 位置缓存( 与MainService同步)
private final WeakReference < MainService > mMainServiceRef ; // 弱引用MainService, 防内存泄漏
// 控件缓存: 位置ID → 对应任务列表视图(分别缓存简单/编辑模式,避免引用混淆 )
// 控件缓存: 位置ID → 对应任务列表视图(分别缓存简单/编辑模式,支持复用后快速同步 )
private final ConcurrentHashMap < String , PositionTaskListView > mSimpleTaskViewMap ;
private final ConcurrentHashMap < String , PositionTaskListView > mEditTaskViewMap ;
// 距离控件缓存( 用于局部更新距离UI)
// 距离控件缓存( 用于局部更新距离UI,保留有效引用避免复用后更新失效 )
private final ConcurrentHashMap < String , TextView > mPosDistanceViewMap ;
// 回调接口( 仅处理位置逻辑, 任务逻辑通过PositionTaskListView+MainService完成)
@@ -81,7 +79,7 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
public PositionAdapter ( Context context , ArrayList < PositionModel > cachedPositionList , MainService mainService ) {
this . mContext = context ;
// 容错处理: 避免传入null导致空指针
this . mCachedPositionList = ( cachedPositionList ! = null ) ? cachedPositionList : new ArrayList < PositionModel > ( ) ;
this . mCachedPositionList = ( cachedPositionList ! = null ) ? cachedPositionList : new ArrayList < PositionModel > ( ) ;
// 弱引用MainService: 防止Adapter持有服务导致内存泄漏( Java 7 弱引用语法)
this . mMainServiceRef = new WeakReference < MainService > ( mainService ) ;
// 初始化控件缓存(线程安全集合,适配多线程更新场景)
@@ -90,7 +88,7 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
this . mPosDistanceViewMap = new ConcurrentHashMap < String , TextView > ( ) ;
// 注册MainService任务监听: 服务任务变化时同步刷新任务列表视图
if ( mainService ! = null ) {
if ( mainService ! = null ) {
mainService . registerTaskUpdateListener ( this ) ;
LogUtils . d ( TAG , " 已注册MainService任务监听, 确保任务数据与服务同步 " ) ;
} else {
@@ -101,12 +99,12 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
}
// =========================================================================
// RecyclerView 核心方法(视图类型判断+创建+绑定,集成任务列表视图 )
// RecyclerView 核心方法(强化视图复用逻辑,复用后自动重新绑定资源 )
// =========================================================================
@Override
public int getItemViewType ( int position ) {
PositionModel posModel = getPositionByIndex ( position ) ;
return ( posModel ! = null & & posModel . isSimpleView ( ) ) ? VIEW_TYPE_SIMPLE : VIEW_TYPE_EDIT ;
return ( posModel ! = null & & posModel . isSimpleView ( ) ) ? VIEW_TYPE_SIMPLE : VIEW_TYPE_EDIT ;
}
@Override
@@ -131,24 +129,26 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
final String posId = posModel . getPositionId ( ) ;
MainService mainService = mMainServiceRef . get ( ) ;
// 按视图类型绑定数据(简单模式/编辑模式)
// 按视图类型绑定数据(简单模式/编辑模式)—— 核心修复:复用后重新绑定所有资源
if ( holder instanceof SimpleViewHolder ) {
LogUtils . d ( TAG , " instanceof SimpleViewHolder( 复用/新创建) : 位置ID= " + posId ) ;
SimpleViewHolder simpleHolder = ( SimpleViewHolder ) holder ;
// 1. 绑定位置基础数据(经纬度/备注/距离)
// 1. 重新 绑定位置基础数据(经纬度/备注/距离,确保复用后显示最新数据 )
bindSimplePositionData ( simpleHolder , posModel ) ;
// 2. 初始化+绑定简单模式任务列表 视图(仅显示已启用任务 )
// 2. 重新 初始化+绑定简单模式任务视图(复用后从服务同步最新任务,避免数据丢失 )
initAndBindSimpleTaskView ( simpleHolder . ptlvSimpleTasks , posId , mainService ) ;
// 3. 缓存简单模式任务视图+距离控件
// 3. 缓存/更新 简单模式任务视图+距离控件(覆盖旧缓存,确保引用最新控件)
mSimpleTaskViewMap . put ( posId , simpleHolder . ptlvSimpleTasks ) ;
mPosDistanceViewMap . put ( posId , simpleHolder . tvSimpleDistance ) ;
} else if ( holder instanceof EditViewHolder ) {
LogUtils . d ( TAG , " instanceof EditViewHolder( 复用/新创建) : 位置ID= " + posId ) ;
EditViewHolder editHolder = ( EditViewHolder ) holder ;
// 1. 绑定位置基础数据(经纬度/备注/距离/距离开关)
// 1. 重新 绑定位置基础数据(经纬度/备注/距离/距离开关,复用后显示最新状态 )
bindEditPositionData ( editHolder , posModel , position ) ;
// 2. 初始化+绑定编辑模式任务列表 视图(支持增删改查 )
// 2. 重新 初始化+绑定编辑模式任务视图(复用后同步服务最新任务, 支持增删改)
initAndBindEditTaskView ( editHolder . ptlvEditTasks , posId , mainService , editHolder . btnAddTask ) ;
// 3. 缓存编辑模式任务视图+距离控件
// 3. 缓存/更新 编辑模式任务视图+距离控件(覆盖旧缓存,确保引用最新控件)
mEditTaskViewMap . put ( posId , editHolder . ptlvEditTasks ) ;
mPosDistanceViewMap . put ( posId , editHolder . tvEditDistance ) ;
}
@@ -157,39 +157,38 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
@Override
public void onViewDetachedFromWindow ( RecyclerView . ViewHolder holder ) {
super . onViewDetachedFromWindow ( holder ) ;
// 视图离开屏幕时:清空对应控件缓存+释放任务列表视图资源( 移除未定义的onDestroy() )
// 核心修改:仅清理「无效/已回收」的控件缓存,不释放关键资源(支持复用后重新绑定 )
PositionModel posModel = getPositionByIndex ( holder . getAdapterPosition ( ) ) ;
if ( posModel = = null | | TextUtils . isEmpty ( posModel . getPositionId ( ) ) ) {
return ;
}
String posId = posModel . getPositionId ( ) ;
// 1. 移除距离控件缓存
// 1. 清理「已脱离视图树/无效」的距离控件缓存(保留有效控件供局部更新)
if ( mPosDistanceViewMap . containsKey ( posId ) ) {
TextView distanceView = mPosDistanceViewMap . get ( posId ) ;
if ( distanceView = = null | | ! distanceView . isAttachedToWindow ( ) ) {
mPosDistanceViewMap . remove ( posId ) ;
LogUtils . d ( TAG , " 视图脱离屏幕: 移除位置ID= " + posId + " 的距离控件缓存 " ) ;
LogUtils . d ( TAG , " 视图脱离屏幕:移除无效距离控件缓存( 位置ID= " + posId + " ) " ) ;
}
}
// 2. 释放简单模式任务视图资源(清空数据+解绑监听, 替代onDestroy() )
// 2. 清理「已脱离视图树/无效」的简单模式任务视图缓存(不解绑监听/不清空数据,复用后重新同步 )
if ( holder instanceof SimpleViewHolder & & mSimpleTaskViewMap . containsKey ( posId ) ) {
PositionTaskListView taskView = mSimpleTaskViewMap . get ( posId ) ;
if ( taskView ! = null ) {
taskView . clearData ( ) ; // 清空任务数据(假设视图已实现该方法,用于释放列表数据)
taskView . setOnTaskUpdatedListener ( null ) ; // 解绑任务更新监听,避免内存泄漏
}
if ( taskView = = null | | ! taskView . isAttachedToWindow ( ) ) {
mSimpleTaskViewMap . remove ( posId ) ;
LogUtils . d ( TAG , " 简单模式视图脱离屏幕:释放任务列表视图资源 ( 位置ID= " + posId + " ) " ) ;
LogUtils . d ( TAG , " 简单模式视图脱离屏幕:移除无效任务视图缓存 ( 位置ID= " + posId + " ) " ) ;
}
}
// 3. 释放编辑模式任务视图资源(清空数据+解绑监听, 替代onDestroy() )
// 3. 清理「已脱离视图树/无效」的编辑模式任务视图缓存(同上,保留数据供复用 )
if ( holder instanceof EditViewHolder & & mEditTaskViewMap . containsKey ( posId ) ) {
PositionTaskListView taskView = mEditTaskViewMap . get ( posId ) ;
if ( taskView ! = null ) {
taskView . clearData ( ) ; // 清空任务数据
taskView . setOnTaskUpdatedListener ( null ) ; // 解绑任务更新监听
}
if ( taskView = = null | | ! taskView . isAttachedToWindow ( ) ) {
mEditTaskViewMap . remove ( posId ) ;
LogUtils . d ( TAG , " 编辑模式视图脱离屏幕:释放任务列表视图资源 ( 位置ID= " + posId + " ) " ) ;
LogUtils . d ( TAG , " 编辑模式视图脱离屏幕:移除无效任务视图缓存 ( 位置ID= " + posId + " ) " ) ;
}
}
}
@@ -199,10 +198,10 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
}
// =========================================================================
// 位置数据绑定(分简单/编辑模式,独立逻辑解耦 )
// 位置数据绑定(保留原有逻辑,确保复用后数据最新 )
// =========================================================================
/**
* 绑定简单模式位置数据(仅显示,无编辑操作)
* 绑定简单模式位置数据(仅显示,无编辑操作)—— 复用后重新执行,显示最新数据
*/
private void bindSimplePositionData ( SimpleViewHolder holder , final PositionModel posModel ) {
// 1. 经纬度显示( 保留6位小数, 格式统一)
@@ -211,9 +210,9 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
// 2. 备注显示(无备注时显示默认文本)
String memo = posModel . getMemo ( ) ;
holder . tvSimpleMemo . setText ( " 备注: " + ( TextUtils . isEmpty ( memo ) ? DEFAULT_MEMO : memo ) ) ;
holder . tvSimpleMemo . setText ( " 备注: " + ( TextUtils . isEmpty ( memo ) ? DEFAULT_MEMO : memo ) ) ;
// 3. 实时距离显示(按状态区分文本+颜色)
// 3. 实时距离显示(按状态区分文本+颜色——复用后重新计算显示最新距离 )
updateDistanceDisplay ( holder . tvSimpleDistance , posModel ) ;
// 4. 点击切换到编辑模式
@@ -229,7 +228,7 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
}
/**
* 绑定编辑模式位置数据(支持备注修改/距离开关/删除/保存)
* 绑定编辑模式位置数据(支持备注修改/距离开关/删除/保存)—— 复用后重新执行,恢复编辑状态
*/
private void bindEditPositionData ( final EditViewHolder holder , final PositionModel posModel , final int position ) {
final String posId = posModel . getPositionId ( ) ;
@@ -238,7 +237,7 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
holder . tvEditLon . setText ( String . format ( " 经度:%.6f " , posModel . getLongitude ( ) ) ) ;
holder . tvEditLat . setText ( String . format ( " 纬度:%.6f " , posModel . getLatitude ( ) ) ) ;
// 2. 备注编辑(填充现有备注,光标定位到末尾)
// 2. 备注编辑(填充现有备注,光标定位到末尾——复用后恢复输入状态 )
String memo = posModel . getMemo ( ) ;
if ( ! TextUtils . isEmpty ( memo ) ) {
holder . etEditMemo . setText ( memo ) ;
@@ -247,10 +246,10 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
holder . etEditMemo . setText ( " " ) ;
}
// 3. 实时距离显示(与简单模式逻辑一致)
// 3. 实时距离显示(与简单模式逻辑一致——复用后显示最新距离 )
updateDistanceDisplay ( holder . tvEditDistance , posModel ) ;
// 4. 距离开关状态(匹配位置模型中的启用状态)
// 4. 距离开关状态(匹配位置模型中的启用状态——复用后恢复开关状态 )
holder . rgDistanceSwitch . check ( posModel . isEnableRealPositionDistance ( )
? R . id . rb_distance_enable
: R . id . rb_distance_disable ) ;
@@ -312,10 +311,10 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
}
// =========================================================================
// PositionTaskListView 集成(分简单/编辑模式,差异化初始化+绑定 )
// PositionTaskListView 集成(强化复用逻辑:复用后重新同步服务数据,确保数据不丢失 )
// =========================================================================
/**
* 初始化+绑定简单模式任务列表视图(仅显示已启用任务,无编辑操作)
* 初始化+绑定简单模式任务列表视图——复用后重新执行,从服务同步最新任务
*/
private void initAndBindSimpleTaskView ( PositionTaskListView taskView , final String posId , MainService mainService ) {
if ( taskView = = null | | TextUtils . isEmpty ( posId ) | | mainService = = null ) {
@@ -323,26 +322,26 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
return ;
}
// 1. 初始化任务视图( 绑定MainService+当前位置ID, 假设视图已实现init方法)
// 1. 初始化任务视图( 绑定MainService+当前位置ID——复用后重新绑定,确保服务引用有效 )
taskView . init ( mainService , posId ) ;
// 2. 设置为简单模式(仅展示,隐藏编辑按钮,需视图支持该状态设置 )
// 2. 设置为简单模式(仅展示,隐藏编辑按钮——复用后恢复视图模式 )
taskView . setViewStatus ( PositionTaskListView . VIEW_MODE_SIMPLE ) ;
// 3. 同步任务数据( 从MainService拉取当前位置已启用任务 )
// 3. 关键:复用后强制从服务同步最新任务(避免显示旧数据,核心修复点 )
taskView . syncTasksFromMainService ( ) ;
// 4. 任务更新回调(服务任务变化时重新同步)
// 4. 任务更新回调(服务任务变化时重新同步——复用后重新绑定回调,避免监听失效 )
taskView . setOnTaskUpdatedListener ( new PositionTaskListView . OnTaskUpdatedListener ( ) {
@Override
public void onTaskUpdated ( String positionId , ArrayList < PositionTaskModel > updatedTasks ) {
public void onTaskUpdated ( String positionId , ArrayList updatedTasks ) {
LogUtils . d ( TAG , " 简单模式任务更新: 位置ID= " + positionId + " ,已启用任务数= " + updatedTasks . size ( ) ) ;
}
} ) ;
LogUtils . d ( TAG , " 初始化简单模式任务视图完成: 位置ID= " + posId ) ;
LogUtils . d ( TAG , " 初始化/复用 简单模式任务视图完成: 位置ID= " + posId ) ;
}
/**
* 初始化+绑定编辑模式任务列表视图(支持增删改查,关联新增任务按钮)
- 初始化+绑定编辑模式任务列表视图——复用后重新执行,从服务同步最新任务+恢复编辑功能
*/
private void initAndBindEditTaskView ( final PositionTaskListView taskView , final String posId ,
MainService mainService , Button btnAddTask ) {
@@ -350,15 +349,12 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
LogUtils . w ( TAG , " 初始化编辑模式任务视图失败: 参数无效( posId= " + posId + " , btnAddTask=" + btnAddTask + " ) " ) ;
return ;
}
// 1. 初始化任务视图( 绑定MainService+当前位置ID)
// 1. 初始化任务视图( 绑定MainService+当前位置ID——复用后重新绑定, 确保服务引用有效)
taskView . init ( mainService , posId ) ;
// 2. 设置为编辑模式(显示编辑/删除按钮,支持修改 )
// 2. 设置为编辑模式(显示编辑/删除按钮——复用后恢复视图模式 )
taskView . setViewStatus ( PositionTaskListView . VIEW_MODE_EDIT ) ;
// 3. 同步任务数据( 从MainService拉取当前位置所有任务 )
taskView . syncTasksFromMainService ( ) ;
// 4. 绑定“新增任务”按钮逻辑(点击创建默认任务,通过任务视图同步到服务)
// 3. 关键:复用后强制从服务同步最新任务(避免显示旧数据,核心修复点 )
taskView . syncTasksFromMainService ( ) ; // 4. 绑定“新增任务”按钮逻辑(复用后重新绑定点击事件,避免按钮点击失效)
btnAddTask . setOnClickListener ( new View . OnClickListener ( ) {
@Override
public void onClick ( View v ) {
@@ -369,54 +365,44 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
newTask . setTaskDescription ( DEFAULT_TASK_DESC ) ; // 默认描述
newTask . setDiscussDistance ( DEFAULT_TASK_DISTANCE ) ; // 默认触发距离( 50米)
newTask . setIsEnable ( true ) ; // 默认启用
newTask . setIsBingo ( false ) ; // 初始未触发
// 调用任务视图的新增方法( 假设视图已实现addNewTask, 内部同步MainService)
newTask . setIsBingo ( false ) ; // 初始未触发// 调用任务视图的新增方法( 假设视图已实现addNewTask, 内部同步MainService)
taskView . addNewTask ( newTask ) ;
hideSoftKeyboard ( v ) ; // 隐藏软键盘,提升体验
LogUtils . d ( TAG , " 编辑模式新增任务: 位置ID= " + posId + " , 任务ID=" + newTask . getTaskId ( ) ) ;
}
} ) ;
// 5. 任务更新回调( 通知外部任务变化, 如Activity需要联动)
} ) ; // 5. 任务更新回调(通知外部任务变化——复用后重新绑定回调,确保交互正常)
taskView . setOnTaskUpdatedListener ( new PositionTaskListView . OnTaskUpdatedListener ( ) {
@Override
public void onTaskUpdated ( String positionId , ArrayList < PositionTaskModel > updatedTasks ) {
public void onTaskUpdated ( String positionId , ArrayList updatedTasks ) {
LogUtils . d ( TAG , " 编辑模式任务更新: 位置ID= " + positionId + " ,当前任务数= " + updatedTasks . size ( ) ) ;
}
} ) ;
LogUtils . d ( TAG , " 初始化编辑模式任务视图完成: 位置ID= " + posId ) ;
LogUtils . d ( TAG , " 初始化/复用编辑模式任务视图完成: 位置ID= " + posId ) ;
}
// =========================================================================
// 工具方法(距离显示/数据校验/控件管理, Java 7 语法适配 )
// 工具方法(保留原有逻辑,适配资源复用场景 )
// =========================================================================
/**
* 更新距离显示(按状态区分文本和颜色,提升视觉区分度)
- 更新距离显示(复用后重新执行,确保显示最新距离+颜色——适配资源复用)
*/
private void updateDistanceDisplay ( TextView distanceView , PositionModel posModel ) {
if ( distanceView = = null | | posModel = = null ) {
LogUtils . w ( TAG , " 更新距离显示失败:参数为空(控件/位置模型) " ) ;
return ;
}
// 场景1: 距离未启用( 灰色文本)
} // 场景1: 距离未启用( 灰色文本)
if ( ! posModel . isEnableRealPositionDistance ( ) ) {
distanceView . setText ( DISTANCE_DISABLED ) ;
distanceView . setTextColor ( mContext . getResources ( ) . getColor ( R . color . gray ) ) ;
return ;
}
// 场景2: 距离计算失败( 红色文本, 用-1标记失败状态)
} // 场景2: 距离计算失败( 红色文本, 用-1标记失败状态)
double distance = posModel . getRealPositionDistance ( ) ;
if ( distance < 0 ) {
distanceView . setText ( DISTANCE_ERROR ) ;
distanceView . setTextColor ( mContext . getResources ( ) . getColor ( R . color . red ) ) ;
return ;
}
// 场景3: 正常显示距离( 按距离范围设置颜色)
} // 场景3: 正常显示距离( 按距离范围设置颜色——复用后重新计算颜色)
distanceView . setText ( String . format ( DISTANCE_FORMAT , distance ) ) ;
if ( distance < = 100 ) {
distanceView . setTextColor ( mContext . getResources ( ) . getColor ( R . color . green ) ) ; // 近距离( ≤100米)
@@ -428,7 +414,8 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
}
/**
* 根据索引获取位置模型(容错处理,避免越界/空指针)
- 根据索引获取位置模型(容错处理,避免越界/空指针——适配复用后索引变化场景)
*/
private PositionModel getPositionByIndex ( int index ) {
if ( mCachedPositionList = = null | | index < 0 | | index > = mCachedPositionList . size ( ) ) {
@@ -439,15 +426,14 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
}
/**
* 根据位置ID获取列表索引( 用于精准刷新视图)
- 根据位置ID获取列表索引( 用于精准刷新视图——适配复用后视图位置变化)
*/
private int getPositionIndexById ( String positionId ) {
if ( TextUtils . isEmpty ( positionId ) | | mCachedPositionList = = null | | mCachedPositionList . isEmpty ( ) ) {
LogUtils . w ( TAG , " 获取位置索引失败: 参数无效( ID/缓存为空) " ) ;
return - 1 ;
}
// Java 7 增强for循环遍历( 替代Lambda, 适配语法)
} // Java 7 增强for循环遍历( 替代Lambda, 适配语法)
for ( int i = 0 ; i < mCachedPositionList . size ( ) ; i + + ) {
PositionModel pos = mCachedPositionList . get ( i ) ;
if ( positionId . equals ( pos . getPositionId ( ) ) ) {
@@ -459,29 +445,28 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
}
/**
* 局部更新距离UI( 仅更新指定位置的距离, 避免全量刷新卡顿)
- 局部更新距离UI( 仅更新指定位置的距离, 避免全量刷新卡顿——适配复用后控件缓存有效场景)
*/
public void updateSinglePositionDistance ( String positionId ) {
if ( TextUtils . isEmpty ( positionId ) | | mPosDistanceViewMap . isEmpty ( ) ) {
LogUtils . w ( TAG , " 局部更新距离失败: ID无效或无控件缓存( ID= " + positionId + " ) " ) ;
return ;
}
// 1. 获取服务端最新位置数据(带重试,避免服务临时回收)
MainService mainService = getMainServiceWithRetry ( 2 ) ;
if ( mainService = = null ) {
LogUtils . e ( TAG , " 局部更新距离失败: 无法获取MainService " ) ;
return ;
}
PositionModel latestPos = null ;
try {
ArrayList < PositionModel > servicePosList = mainService . getPositionList ( ) ;
ArrayList servicePosList = mainService . getPositionList ( ) ;
if ( servicePosList ! = null & & ! servicePosList . isEmpty ( ) ) {
// Java 7 迭代器遍历, 避免ConcurrentModificationException
Iterator < PositionModel > iter = servicePosList . iterator ( ) ;
Iterator iter = servicePosList . iterator ( ) ;
while ( iter . hasNext ( ) ) {
PositionModel pos = iter . next ( ) ;
PositionModel pos = ( PositionModel ) iter . next ( ) ;
if ( positionId . equals ( pos . getPositionId ( ) ) ) {
latestPos = pos ;
break ;
@@ -491,14 +476,11 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
} catch ( Exception e ) {
LogUtils . d ( TAG , " 获取最新位置数据失败( ID= " + positionId + " ) " + e ) ;
return ;
}
if ( latestPos = = null ) {
} if ( latestPos = = null ) {
LogUtils . w ( TAG , " 局部更新距离失败: 未找到位置ID= " + positionId ) ;
return ;
}
// 2. 更新距离控件(确保主线程操作,避免跨线程异常)
// 2. 更新距离控件(确保主线程操作,避免跨线程异常——适配复用后控件已重新绑定)
final TextView distanceView = mPosDistanceViewMap . get ( positionId ) ;
if ( distanceView ! = null & & distanceView . isAttachedToWindow ( ) ) {
final PositionModel finalLatestPos = latestPos ;
@@ -510,20 +492,19 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
} ) ;
LogUtils . d ( TAG , " 局部更新距离完成: 位置ID= " + positionId + " ,距离= " + latestPos . getRealPositionDistance ( ) + " 米 " ) ;
} else {
mPosDistanceViewMap . remove ( positionId ) ; // 移除无效控件缓存
mPosDistanceViewMap . remove ( positionId ) ; // 移除无效控件缓存(如视图已被销毁)
LogUtils . w ( TAG , " 局部更新距离失败:控件已回收/脱离视图树( ID= " + positionId + " ) " ) ;
}
}
/**
* 全量更新位置数据(从服务同步最新数据,过滤无效/重复项)
- 全量更新位置数据(从服务同步最新数据,过滤无效/重复项——适配复用后全量刷新场景 )
*/
public void updateAllPositionData ( ArrayList < PositionModel > newPosList ) {
if ( newPosList = = null ) {
LogUtils . w ( TAG , " 全量更新位置数据失败:新列表为空 " ) ;
return ;
}
// 1. 过滤无效位置( 校验核心字段: ID/经纬度合法)
ArrayList < PositionModel > validPosList = new ArrayList < PositionModel > ( ) ;
for ( PositionModel pos : newPosList ) {
@@ -535,18 +516,15 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
}
validPosList . add ( pos ) ;
}
// 2. 去重( 按位置ID去重, 保留服务端最新数据)
ConcurrentHashMap < String , PositionModel > uniquePosMap = new ConcurrentHashMap < String , PositionModel > ( ) ;
for ( PositionModel pos : validPosList ) {
uniquePosMap . put ( pos . getPositionId ( ) , pos ) ; // 相同ID覆盖, 保留最新
}
ArrayList < PositionModel > uniquePosList = new ArrayList < PositionModel > ( uniquePosMap . values ( ) ) ;
// 3. 同步到本地缓存+刷新UI
ArrayList uniquePosList = new ArrayList ( uniquePosMap . values ( ) ) ; // 3. 同步到本地缓存+刷新UI( 刷新后触发onBind, 自动重新绑定资源)
this . mCachedPositionList . clear ( ) ;
this . mCachedPositionList . addAll ( uniquePosList ) ;
// 清空旧控件缓存(避免引用失效数据)
// 清空旧控件缓存(避免引用失效数据,刷新后重新缓存新控件 )
mPosDistanceViewMap . clear ( ) ;
mSimpleTaskViewMap . clear ( ) ;
mEditTaskViewMap . clear ( ) ;
@@ -555,36 +533,33 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
}
/**
* 隐藏软键盘(编辑完成后调用,提升用户体验)
- 隐藏软键盘(编辑完成后调用,提升用户体验——适配复用后软键盘残留问题 )
*/
private void hideSoftKeyboard ( View view ) {
if ( mContext = = null | | view = = null ) {
LogUtils . w ( TAG , " 隐藏软键盘失败:参数为空(上下文/视图) " ) ;
return ;
}
InputMethodManager imm = ( InputMethodManager ) mContext . getSystemService ( Context . INPUT_METHOD_SERVICE ) ;
} InputMethodManager imm = ( InputMethodManager ) mContext . getSystemService ( Context . INPUT_METHOD_SERVICE ) ;
if ( imm ! = null ) {
imm . hideSoftInputFromWindow ( view . getWindowToken ( ) , 0 ) ; // 强制隐藏
}
}
/**
* 带重试的服务获取( 解决弱引用临时回收问题, 最多重试2次)
- 带重试的服务获取( 解决弱引用临时回收问题, 最多重试2次——适配复用后服务引用失效场景 )
*/
private MainService getMainServiceWithRetry ( int retryCount ) {
MainService mainService = mMainServiceRef . get ( ) ;
if ( mainService ! = null ) {
return mainService ;
}
// 重试逻辑: 每次间隔100ms, 避免频繁重试
for ( int i = 0 ; i < retryCount ; i + + ) {
try {
Thread . sleep ( 100 ) ;
mainService = mMainServiceRef . get ( ) ;
if ( mainService ! = null ) {
LogUtils . d ( TAG , " 重试获取MainService成功( 第 " + ( i + 1 ) + " 次) " ) ;
LogUtils . d ( TAG , " 重试获取MainService成功( 第 " + ( i + 1 ) + " 次) " ) ;
return mainService ;
}
} catch ( InterruptedException e ) {
@@ -592,41 +567,39 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
Thread . currentThread ( ) . interrupt ( ) ; // 恢复中断状态
break ;
}
}
LogUtils . e ( TAG , " 重试 " + retryCount + " 次后仍未获取到MainService " ) ;
} LogUtils . e ( TAG , " 重试 " + retryCount + " 次后仍未获取到MainService " ) ;
return null ;
}
// =========================================================================
// 实现 MainService.TaskUpdateListener 接口(服务任务变化时回调)
// 实现 MainService.TaskUpdateListener 接口(服务任务变化时回调——适配复用后任务同步 )
// =========================================================================
@Override
public void onTaskUpdated ( ) {
LogUtils . d ( TAG , " 收到MainService任务更新通知, 同步所有任务列表视图 " ) ;
LogUtils . d ( TAG , " 收到MainService任务更新通知, 同步所有任务列表视图(含复用视图) " ) ;
// 1. 同步简单模式任务视图(仅显示已启用任务)
// 1. 同步简单模式任务视图(仅显示已启用任务——适配复用后视图已重新缓存 )
if ( ! mSimpleTaskViewMap . isEmpty ( ) ) {
Iterator < ConcurrentHashMap . Entry < String , PositionTaskListView > > iter = mSimpleTaskViewMap . entrySet ( ) . iterator ( ) ;
while ( iter . hasNext ( ) ) {
ConcurrentHashMap . Entry < String , PositionTaskListView > entry = iter . next ( ) ;
PositionTaskListView taskView = entry . getValue ( ) ;
if ( taskView ! = null & & taskView . isAttachedToWindow ( ) ) {
taskView . syncTasksFromMainService ( ) ; // 从服务同步最新任务
taskView . syncTasksFromMainService ( ) ; // 从服务同步最新任务(复用后也能更新)
} else {
iter . remove ( ) ; // 移除无效视图缓存
iter . remove ( ) ; // 移除无效视图缓存(如视图已脱离屏幕且未复用)
}
}
}
// 2. 同步编辑模式任务视图(支持编辑的全量任务)
// 2. 同步编辑模式任务视图(支持编辑的全量任务——适配复用后视图已重新缓存 )
if ( ! mEditTaskViewMap . isEmpty ( ) ) {
Iterator < ConcurrentHashMap . Entry < String , PositionTaskListView > > iter = mEditTaskViewMap . entrySet ( ) . iterator ( ) ;
while ( iter . hasNext ( ) ) {
ConcurrentHashMap . Entry < String , PositionTaskListView > entry = iter . next ( ) ;
PositionTaskListView taskView = entry . getValue ( ) ;
if ( taskView ! = null & & taskView . isAttachedToWindow ( ) ) {
taskView . syncTasksFromMainService ( ) ; // 从服务同步最新任务
taskView . syncTasksFromMainService ( ) ; // 从服务同步最新任务(复用后也能更新)
} else {
iter . remove ( ) ; // 移除无效视图缓存
}
@@ -635,7 +608,7 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
}
// =========================================================================
// 回调设置方法( 供Activity绑定交互逻辑)
// 回调设置方法( 供Activity绑定交互逻辑——适配复用后回调不失效 )
// =========================================================================
public void setOnDeleteClickListener ( OnDeleteClickListener listener ) {
this . mOnDeleteListener = listener ;
@@ -646,7 +619,7 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
}
// =========================================================================
// 资源释放( Activity销毁时调用, 彻底释放引用, 避免内存泄漏)
// 资源释放( Activity销毁时调用, 彻底释放引用, 避免内存泄漏——保留原有安全逻辑 )
// =========================================================================
public void release ( ) {
// 1. 反注册MainService任务监听( 解除服务绑定)
@@ -656,7 +629,7 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
LogUtils . d ( TAG , " 已反注册MainService任务监听 " ) ;
}
// 2. 释放简单模式任务视图资源(清空数据+解绑监听, 替代onDestroy())
// 2. 释放简单模式任务视图资源(清空数据+解绑监听——仅在Activity销毁时执行, 不复用场景 )
if ( ! mSimpleTaskViewMap . isEmpty ( ) ) {
Iterator < ConcurrentHashMap . Entry < String , PositionTaskListView > > iter = mSimpleTaskViewMap . entrySet ( ) . iterator ( ) ;
while ( iter . hasNext ( ) ) {
@@ -669,7 +642,7 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
}
}
// 3. 释放编辑模式任务视图资源(清空数据+解绑监听, 替代onDestroy())
// 3. 释放编辑模式任务视图资源(清空数据+解绑监听——仅在Activity销毁时执行 )
if ( ! mEditTaskViewMap . isEmpty ( ) ) {
Iterator < ConcurrentHashMap . Entry < String , PositionTaskListView > > iter = mEditTaskViewMap . entrySet ( ) . iterator ( ) ;
while ( iter . hasNext ( ) ) {
@@ -682,7 +655,7 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
}
}
// 4. 清空其他缓存+置空引用
// 4. 清空其他缓存+置空引用(彻底释放,避免内存泄漏)
mPosDistanceViewMap . clear ( ) ;
if ( mCachedPositionList ! = null ) {
mCachedPositionList . clear ( ) ;
@@ -697,7 +670,7 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
// 静态内部类: ViewHolder( 与布局严格对应, 避免外部引用导致内存泄漏)
// =========================================================================
/**
* 简单模式ViewHolder( 对应 item_position_simple.xml, 含简单模式任务列表视图)
- 简单模式ViewHolder( 对应 item_position_simple.xml, 含简单模式任务列表视图)
*/
public static class SimpleViewHolder extends RecyclerView . ViewHolder {
TextView tvSimpleLon ; // 经度显示
@@ -705,7 +678,6 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
TextView tvSimpleMemo ; // 备注显示
TextView tvSimpleDistance ; // 距离显示
PositionTaskListView ptlvSimpleTasks ; // 简单模式任务列表视图
public SimpleViewHolder ( View itemView ) {
super ( itemView ) ;
// 绑定布局控件( 与XML中ID严格一致, 避免运行时空指针)
@@ -718,7 +690,7 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
}
/**
* 编辑模式ViewHolder( 对应 item_position_edit.xml, 含编辑模式任务列表视图)
- 编辑模式ViewHolder( 对应 item_position_edit.xml, 含编辑模式任务列表视图)
*/
public static class EditViewHolder extends RecyclerView . ViewHolder {
TextView tvEditLon ; // 经度显示(不可编辑)
@@ -732,7 +704,6 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
Button btnAddTask ; // 新增任务按钮
TextView tvTaskCount ; // 任务数量显示(兼容旧布局,可隐藏)
PositionTaskListView ptlvEditTasks ; // 编辑模式任务列表视图
public EditViewHolder ( View itemView ) {
super ( itemView ) ;
// 绑定布局控件( 与XML中ID严格一致, 避免运行时空指针)
@@ -752,88 +723,87 @@ public class PositionAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
// =========================================================================
// 补充: PositionTaskListView 必要方法适配(确保视图类已实现以下基础方法)
// (若视图类未实现,需在 PositionTaskListView 中添加对应逻辑)
// (若视图类未实现,需在 PositionTaskListView 中添加对应逻辑,否则复用后功能异常 )
// =========================================================================
public static class PositionTaskListViewRequiredMethods {
/**
* 1. init 方法:初始化任务视图(绑定服务+位置ID)
* 需在 PositionTaskListView 中添加:
* public void init(MainService mainService, String positionId) {
* this.mMainServiceRef = new WeakReference<MainService> (mainService); // 弱引用服务
* this.mPositionId = positionId; // 绑定当前位置ID
* }
1. - init 方法:初始化任务视图(绑定服务+位置ID——复用后重新绑定,确保服务引用有效 )
- 需在 PositionTaskListView 中添加:
- public void init(MainService mainService, String positionId) {
- this.mMainServiceRef = new WeakReference(mainService); // 弱引用服务,防泄漏
- this.mPositionId = positionId; // 绑定当前位置ID( 复用后更新为当前位置ID)
- }
*/
/**
* 2. setViewStatus 方法:设置视图模式(简单/编辑)
* 需在 PositionTaskListView 中添加:
* public static final int VIEW_MODE_SIMPLE = 0; // 仅显示
* public static final int VIEW_MODE_EDIT = 1; // 可编辑
* private int mViewMode;
* public void setViewStatus(int viewMode) {
* this.mViewMode = viewMode;
* // 根据模式控制编辑/删除 按钮显示(如 简单模式隐藏,编辑模式显示)
* updateButtonVisibility( );
* }
2. - setViewStatus 方法:设置视图模式(简单/编辑——复用后恢复对应模式UI )
- 需在 PositionTaskListView 中添加:
- public static final int VIEW_MODE_SIMPLE = 0; // 仅显示(隐藏编辑/删除按钮)
- public static final int VIEW_MODE_EDIT = 1; // 可编辑(显示编辑/删除按钮)
- private int mViewMode;
- public void setViewStatus(int viewMode) {
- this.mViewMode = viewMode;
- // 根据模式控制按钮显示: 简单模式隐藏编辑按钮 ,编辑模式显示
- if (mEditBtn != null) mEditBtn.setVisibility(viewMode == VIEW_MODE_EDIT ? View.VISIBLE : View.GONE );
- if (mDeleteBtn != null) mDeleteBtn.setVisibility(viewMode == VIEW_MODE_EDIT ? View.VISIBLE : View.GONE);
- }
*/
/**
* 3. syncTasksFromMainService 方法:从服务同步任务数据
* 需在 PositionTaskListView 中添加:
* public void syncTasksFromMainService() {
* MainService mainService = mMainServiceRef.get();
* if (mainService == null || TextUtils.isEmpty(mPositionId)) return;
* // 根据视图模式拉取任务( 简单模式: 已启用; 编辑模式:所有)
* ArrayList<PositionTaskModel> tasks = (mViewMode == VIEW_MODE_SIMPLE)
* ? mainService.getEnabledTasksByPositionId(mPositionId)
* : mainService.getTasksByPositionId(mPositionId);
* // 更新列表显示( 如设置Adapter数据 )
* setTaskList(tasks);
* }
3. - syncTasksFromMainService 方法:从服务同步任务数据(复用后核心方法,避免旧数据)
- 需在 PositionTaskListView 中添加:
- public void syncTasksFromMainService() {
- MainService mainService = mMainServiceRef.get();
- if (mainService == null || TextUtils.isEmpty(mPositionId)) return;
- // 根据视图模式拉取对应 任务: 简单模式仅拉 已启用, 编辑模式拉全部
- ArrayList tasks = (mViewMode == VIEW_MODE_SIMPLE)
- ? mainService.getEnabledTasksByPositionId(mPositionId)
- : mainService.getTasksByPositionId(mPositionId);
- // 更新列表数据( 刷新UI, 确保复用后显示最新任务 )
- if (mTaskAdapter != null) {
- mTaskAdapter.setTaskList(tasks);
- mTaskAdapter.notifyDataSetChanged();
- }
- // 通知外部任务更新( 如Adapter需要联动)
- if (mTaskUpdateListener != null) mTaskUpdateListener.onTaskUpdated(mPositionId, tasks);
- }
*/
/**
* 4. addNewTask 方法:新增任务(同步服务+刷新列表)
* 需在 PositionTaskListView 中添加:
* public void addNewTask(PositionTaskModel newTask) {
* if (newTask == null || TextUtils.isEmpty(newTask.getPositionId())) return;
* MainService mainService = mMainServiceRef.get();
* if (mainService != null) {
* mainService.addTask(newTask);
* syncTasksFromMainService(); // 新增后同步刷新
* Toast.makeText(getContext(), "任务创建 成功", Toast.LENGTH_SHORT).show();
* } else {
* Toast.makeText(getContext(), "新增失败,请重试 ", Toast.LENGTH_SHORT).show();
* }
* }
4. - addNewTask 方法:新增任务(同步服务+刷新列表——复用后功能正常 )
- 需在 PositionTaskListView 中添加:
- public void addNewTask(PositionTaskModel newTask) {
- if (newTask == null || TextUtils.isEmpty(newTask.getPositionId())) return;
- MainService mainService = mMainServiceRef.get();
- if (mainService != null) {
- mainService.addTask(newTask); // 同步到服务(确保数据持久化)
- syncTasksFromMainService(); // 新增后立即 同步, 刷新列表
- Toast.makeText(getContext(), "任务新增 成功", Toast.LENGTH_SHORT).show();
- } else {
- Toast.makeText(getContext(), "服务未就绪, 新增失败", Toast.LENGTH_SHORT).show();
- }
- }
*/
/**
* 5. clearData 方法:清空任务数据(释放资源 )
* 需在 PositionTaskListView 中添加:
* public void clearData() {
* if (mTaskList != null) mTaskList.clear(); // 清空任务列表数据
* if (mTaskAdapter != null) mTaskAdapter.notifyDataSetChanged(); // 刷新列表
* }
5. - clearData 方法:清空任务数据(仅在Activity销毁时调用, 不复用场景 )
- 需在 PositionTaskListView 中添加:
- public void clearData() {
- if (mTaskList != null) mTaskList.clear(); // 清空本地 任务列表
- if (mTaskAdapter != null) mTaskAdapter.notifyDataSetChanged(); // 刷新空UI
- }
*/
/**
* 6. setOnTaskUpdatedListener 方法:设置任务更新回调
* 需在 PositionTaskListView 中添加:
* public interface OnTaskUpdatedListener {
* void onTaskUpdated(String positionId, ArrayList<PositionTaskModel> updatedTasks);
* }
* private OnTaskUpdatedListener mTaskUpdateListener;
* public void setOnTaskUpdatedListener(OnTaskUpdatedListener listener) {
* this.mTaskUpdateListener = listener;
* }
* // 任务变化时调用(如同步后/编辑后)
* private void notifyTaskUpdated(ArrayList<PositionTaskModel> updatedTasks) {
* if (mTaskUpdateListener != null) {
* mTaskUpdateListener.onTaskUpdated(mPositionId, updatedTasks);
* }
* }
6. - setOnTaskUpdatedListener 方法:设置任务更新回调(复用后重新绑定,避免回调失效)
- 需在 PositionTaskListView 中添加:
- public interface OnTaskUpdatedListener {
- void onTaskUpdated(String positionId, ArrayList updatedTasks);
- }
- private OnTaskUpdatedListener mTaskUpdateListener;
- public void setOnTaskUpdatedListener(OnTaskUpdatedListener listener) {
- this.mTaskUpdateListener = listener;
- }
*/
}
}