█ 【安卓学习之常见问题】初始化Spinner、CheckBox和SeekBar不触发事件 █ 系列文章目录 提示:这里是收集了和文件分享有关的文章
- 【安卓学习之常见问题】android路径及文件问题
- 【安卓学习之常见问题】文件分享–文件不存在
-
- 【安卓学习之常见问题】自定义组件-刷新后跳到第一行
- 【安卓学习之常见问题】初始化spinner、CheckBox和SeekBar不触发事件
- █ 【安卓学习之常见问题】初始化Spinner、CheckBox和SeekBar不触发事件
- █ 系列文章目录
- █ 文章目录
- █ 读前说明
- █ 问题
- █ Spinner不触发事件
- █ CheckBox不触发事件
- █ SeekBar不触发事件
- █ 相关资料
- █ 免责声明
█ 读前说明
- 本文通过学习别人写demo,学习一些课件,参考一些博客,’学习相关知识,如果涉及侵权请告知
- 本文只简单罗列相关的代码实现过程
- 涉及到的逻辑以及说明也只是简单介绍,主要当做笔记,了解过程而已
- 数据会自动刷新,每次控件都要重新赋值,赋值过程又触发监听,写数据,造成 每次读的时候也写。
- 正常初始化
ArrayAdapter adapter = new ArrayAdapter(viewGroup.getContext(),
android.R.layout.simple_spinner_item, datas);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mSpinner.setAdapter(adapter);
mSpinner.setSelection(mPosition, true);
mSpinner.setOnItemSelectedListener(this);
@Override
public void onItemSelected(AdapterView> adapterView, View view, int position,
long id) {
。。。。。。
}
- 刷新数据:习惯是先将监听置null,设置后,再重复赋值
mSpinner.setOnItemSelectedListener(null);
mSpinner.setSelection(true);
// 监听异步执行,延迟执行
mSpinner.setOnItemSelectedListener(this);
>>>>结果发现是先执行
监听置null | setOnItemSelectedListener(null) |
---|---|
设置值 | setSelection(true) |
监听置this | setOnItemSelectedListener(this) |
触发监听 | onItemSelected() |
- 使用setSelection(true,false)
mSpinner.setSelection(true, false);
>>>>结果还是是执行监听onItemSelected()
- 将上面两种方法合起来执行,测试效果
mSpinner.setOnItemSelectedListener(null);
mSpinner.setSelection(true, false);
// 监听同步执行,没延迟执行
mSpinner.setOnItemSelectedListener(this);
>>>>结果不执行监听onItemSelected(),满足要求
监听置null | setOnItemSelectedListener(null) |
---|---|
设置值 | setSelection(true, false) |
触发监听 | onItemSelected() |
监听置this | setOnItemSelectedListener(this) |
- AppCompatSpinner
文章图片
setSelection(int position) | Sets the currently selected item. | 设置当前选中的项目。 |
---|---|---|
setSelection(int position, boolean animate) | Jump directly to a specific item in the adapter data. | 直接跳转到适配器数据中的特定项目。 |
- 研究下源码setSelection(int position)
android.widget.AbsSpinner
@Override
public void setSelection(int position) {// 3
setNextSelectedPositionInt(position);
// android.widget.AdapterView
requestLayout();
invalidate();
// 刷新
}
/**
* 保持 mNextSelectedPosition 和 mNextSelectedRowId 同步的实用程序
* @param position 下次我们去时 mSelectedPosition 的预期值
* 通过布局
*/
void setNextSelectedPositionInt(int position) {
mNextSelectedPosition = position;
// 决定当前选中的是哪一个位置的值:3
mNextSelectedRowId = getItemIdAtPosition(position);
// 如果我们试图同步到选择,也更新它
if (mNeedSync && mSyncMode == SYNC_SELECTED_POSITION && position >= 0) {// mNeedSync = false
。。。
}
}
/**
* 覆盖以防止向自己发送布局请求垃圾邮件
* 当我们放置视图时
* @see android.view.View#requestLayout()
*/
@Override
public void requestLayout() {
if (!mBlockLayoutRequests) {// mBlockLayoutRequests = false,因此执行
super.requestLayout();
// 请求重新布局,此时会触发onLayout方法
}
}
requestLayout() | 要求parent view重新进行一次测量、布局、绘制这三个流程来更新自己位置。 | 重新布局自己在父布局中的位置 |
---|---|---|
invalidate() | view进行重新绘制 | 强制调用自己的onDraw()方法 |
android.widget.Spinner
/**
* @see android.view.View#onLayout(boolean,int,int,int,int)
* Creates and positions all views
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
mInLayout = true;
layout(0, false);
mInLayout = false;
}
/**
* 创建并定位此 Spinner 的所有视图。
* @param delta 更改所选位置。 +1 表示选择向右移动,
* 所以视图向左滚动。 -1 表示选择向左移动。
*/
@Override
void layout(int delta, boolean animate) {
。。。
if (mNextSelectedPosition >= 0) {// 要设置的位置
setSelectedPositionInt(mNextSelectedPosition);
// mSelectedPosition = mNextSelectedPosition = 3
}
recycleAllViews();
removeAllViewsInLayout();
// Make selected view and position it
mFirstPosition = mSelectedPosition;
if (mAdapter != null) {
View sel = makeView(mSelectedPosition, true);
// 生成AppCompatTextView{mText ="3"}
。。。。。。
}
// Flush any cached views that did not get reused above
mRecycler.clear();
invalidate();
//刷新
checkSelectionChanged();
// android.widget.AdapterView
mDataChanged = false;
mNeedSync = false;
setNextSelectedPositionInt(mSelectedPosition);
}
android.widget.AdapterView
/**
* 布局后调用判断选择位置是否需要被更新。 也用于触发任何未决的选择事件。
*/
void checkSelectionChanged() {
// 3 != -1,执行selectionChanged();
if ((mSelectedPosition != mOldSelectedPosition) || (mSelectedRowId != mOldSelectedRowId)) {
selectionChanged();
mOldSelectedPosition = mSelectedPosition;
mOldSelectedRowId = mSelectedRowId;
}// If we have a pending selection notification -- and we won't if we
// just fired one in selectionChanged() -- run it now.
if (mPendingSelectionNotifier != null) {// null
mPendingSelectionNotifier.run();
}
} void selectionChanged() {
// 即将发布或运行选择通知程序,所以我们不需要一个挂起的通知器。
mPendingSelectionNotifier = null;
// 辅助功能服务AccessibilityService(无障碍服务)
// 如果spinner的监听为null则不触发,此时afm 不为null
// 如果spinner的监听不为null则触发,此时afm 不为null
if (mOnItemSelectedListener != null
|| AccessibilityManager.getInstance(mContext).isEnabled()) {
if (mInLayout || mBlockLayoutRequests) {
。。。。。。
if (mSelectionNotifier == null) {
mSelectionNotifier = new SelectionNotifier();
} else {
removeCallbacks(mSelectionNotifier);
}
post(mSelectionNotifier);
// 有监听,执行这句
} else {
dispatchOnItemSelected();
}
}
。。。。。。
}private class SelectionNotifier implements Runnable {
public void run() {
。。。。。。
if (mDataChanged && getViewRootImpl() != null
&& getViewRootImpl().isLayoutRequested()) {
。。。。。。
} else {
dispatchOnItemSelected();
// 执行这句
}
}
}private void dispatchOnItemSelected() {
fireOnSelected();
// 执行这句
performAccessibilityActionsOnSelected();
}private void fireOnSelected() {
if (mOnItemSelectedListener == null) {// 对接外部的Spinner监听
return;
}
final int selection = getSelectedItemPosition();
if (selection >= 0) {
View v = getSelectedView();
mOnItemSelectedListener.onItemSelected(this, v, selection,
getAdapter().getItemId(selection));
// 执行这句
} else {
mOnItemSelectedListener.onNothingSelected(this);
}
}
监听置null | setOnItemSelectedListener(null) |
---|---|
设置值 | setSelection(true) |
请求重新布局 重新测量布局绘制 会触发onLayout方法 |
requestLayout() ->View的测量 performMeasure() ->View的布局 performLayout() ->View的绘制 performDraw() |
刷新onDraw | invalidate() |
监听置this | setOnItemSelectedListener(this) |
延迟 | 延迟 |
设置自身和子布局的位置 调用子元素的layout方法 完成View树的layout过程 |
onLayout(boolean changed, int l, int t, int r, int b) |
确定View本身的位置 即设置View本身的四个顶点位置 |
layout(0, false) |
选项更新 | checkSelectionChanged() ->selectionChanged() ->有监听事件则post(mSelectionNotifier ->dispatchOnItemSelected() ->fireOnSelected() ->mOnItemSelectedListener.onItemSelected() |
触发外部监听 | onItemSelected() |
- 研究下源码setSelection(int position, boolean animate)
/**
* 直接跳转到适配器数据中的特定项目
*/
public void setSelection(int position, boolean animate) {
// 仅当请求的位置已经在屏幕上某处时才进行动画处理
boolean shouldAnimate = animate && mFirstPosition <= position &&
position <= mFirstPosition + getChildCount() - 1;
setSelectionInt(position, shouldAnimate);
}
/**
* 选中提供位置的项目
* @param position 要选择的位置
* @param animate 过渡是否动画
*/
void setSelectionInt(int position, boolean animate) {
if (position != mOldSelectedPosition) {// 3 != -1
mBlockLayoutRequests = true;
int delta= position - mSelectedPosition;
// 要设置的位置-上次选中的位置=3-0=3
setNextSelectedPositionInt(position);
// android.widget.AdapterView
layout(delta, animate);
// 执行这句 android.widget.Spinnerlayout(3, false)
mBlockLayoutRequests = false;
}
}
>>>>执行layout(delta, animate) 就和 之前的一样
监听置null | setOnItemSelectedListener(null) |
---|---|
设置值 | setSelection(true, false) ->setSelectionInt() |
确定View本身的位置 即设置View本身的四个顶点位置 |
layout(3, false) |
选项更新 | checkSelectionChanged() ->selectionChanged() ->无监听事跳过post(mSelectionNotifier) |
监听置this | setOnItemSelectedListener(this) |
- 此外 mSpinner.setAdapter(adapter) 也会触发requestLayout()->layout(delta, animate)
android.widget.Spinner
@Override
public void setAdapter(SpinnerAdapter adapter) {
。。。。。。
super.setAdapter(adapter);
mRecycler.clear();
。。。。。。
}
android.widget.AbsSpinner
@Override
public void setAdapter(SpinnerAdapter adapter) {
。。。。。。
requestLayout();
}
█ CheckBox不触发事件
- 正常初始化
mCheckBox.setOnCheckedChangeListener(this);
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
。。。。。。
}
- 刷新数据:习惯是先将监听置null,设置后,再重复赋值
mCheckBox.setOnCheckedChangeListener(null);
mCheckBox.setChecked(true);
// 不会触发监听
mCheckBox.setOnCheckedChangeListener(this);
>>>>结果OK
- 也可以参考SeekBar不触发事件,修改监听事件
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (buttonView.isPressed()) {
。。。。。。
}
}
█ SeekBar不触发事件
- 正常初始化
mSeekBar.setOnSeekBarChangeListener(this);
// 拖动条进度改变的时候调用(不触发监听,修改显示值)
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser) {
。。。。。。
}
}// 拖动条开始拖动的时候调用
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
。。。。。。
}// 拖动条停止拖动的时候调用(触发监听,修改参数)
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
。。。。。。
}
- 刷新数据:因为监听里面自带fromUser参数判断是否是手动拖动,因此可以直接修改
mSeekBar.setProgress(progress);
>>>>结果OK
█ 相关资料 提示:这里是参考的相关文章
- 2022-01-05 android spinner setSelection 不触发onItemSelected_sinat_42439340的博客-CSDN博客 >>>>替换成setSelection(index,false)
- 2013-03-20 更改复选框值而不触发onCheckChanged - 问答 - 腾讯云开发者社区-腾讯云 >>>>监听中判断checkbox.isPressed()或者 监听设置null
- 2021-04-07 自定义Spinner实现在set数据的时候不触发OnItemSelectedListener - 简书 >>>>继承Spinne+反射
- AppCompatSpinner >>>>官网
- 2017-12-26 浅析View的requestLayout()方法 - 简书 >>>>一句话,requestLayout()的效果是重新布局自己在父布局中的位置,invalidate()的效果是强制调用自己的onDraw()方法
- 2021-09-17 Android进阶之深入理解View的布局(Layout)流程原理-51CTO.COM >>>>View三大工作流程是从ViewRootImpl#performTraversals开始的,其中performMeasure、performLayout、performDraw方法分别对应了View的测量、布局、绘制;
- 2018-05-09 关于viewpager和fragment里面布局跳动的BUG。记录_迷人的脚毛的博客-CSDN博客 >>>>scrollview下的第一个子控件里加上 android:focusable=“true” android:focusableInTouchMode=“true”
博主分享的所有文章内容,部分参考网上教程,引用大神高论,部分亲身实践,记下笔录,内容可能存在诸多不实之处,还望海涵,本内容仅供学习研究使用,切勿用于商业用途,若您是部分内容的作者,不喜欢此内容被分享出来,可联系博主说明相关情况通知删除,感谢您的理解与支持! |
---|
https://blog.csdn.net/ljb568838953/article/details/126637399
推荐阅读
- Android开发笔记|【Android笔记28】Android中的数据存储技术之Shared Preferences
- Kotlin|【Kotlin基础系列】第2章 基本语法(1)
- Android安全|【原创工具】ADBGUI - GUI版ADB操作工具
- android|No version of NDK matched the requested version xxx 问题解决
- Java|项目总结--3(@Cacheable的使用方法和使用技巧)
- vue|适合Vue用户的React教程,你值得拥有
- 11_性能测试|性能分析(动态代理失败导致缓存策略失效)
- 面试复习|敖丙思维导图-Redis
- 代理模式|SpringIOC 和AOP 的理解