android "动画重叠",Android提高第十九篇之"多方向"抽屉

本文介绍了如何解决android-misc-widgets中的Panel控件在某些版本下出现的间歇性闪烁问题,并分享了替换OnTouchListener以修复bug的方法。同时,改进后的代码还保持了OnClickListener的功能,提升用户体验。

本文来自[http://blog.youkuaiyun.com/hellogv/](http://blog.youkuaiyun.com/hellogv/) ,引用必须注明出处!

在android上要实现类似Launch的抽屉效果,大家一定首先会想起SlidingDrawer。SlidingDrawer是android官方控件之一,本文的主角不是它,而是民间的控件工具集合~~~android-misc-widgets。android-misc-widgets里面包含几个widget:Panel、SmoothButton、Switcher、VirtualKeyboard,还有一些动画特效,本文主要介绍抽屉容器Panel的用法。android-misc-widgets的google工程地址:[-widgets/](http://code.google.com/p/android-misc)[http://code.google.com/p/android-misc](http://code.google.com/p/android-misc-widgets/),工程代码中Panel的演示效果如下:

![](https://box.kancloud.cn/2016-06-24_576cb0a6b128c.gif)

这个Panel控件可以轻易实现不同方向的抽屉效果,比SlidingDrawer有更强的扩展性!

在多次使用Panel的过程中,发现Panel有个bug,会间断性出现“闪烁”,也就是在onTouchListener里面的触发ACTION_DOWN后,抽屉瞬间弹出然后瞬间回收(版本日期为[Feb 3, 2009](http://code.google.com/p/android-misc-widgets/source/browse/trunk/android-misc-widgets/src/org/miscwidgets/widget/Panel.java))。把原Panel的OnTouchListener,即以下代码:

~~~

OnTouchListener touchListener = new OnTouchListener() { int initX; int initY; boolean setInitialPosition; public boolean onTouch(View v, MotionEvent event) { if (mState == State.ANIMATING) { // we are animating return false; }// Log.d(TAG, "state: " + mState + " x: " + event.getX() + " y: " + event.getY()); int action = event.getAction(); if (action == MotionEvent.ACTION_DOWN) { if (mBringToFront) { bringToFront(); } initX = 0; initY = 0; if (mContent.getVisibility() == GONE) { // since we may not know content dimensions we use factors here if (mOrientation == VERTICAL) { initY = mPosition == TOP? -1 : 1; } else { initX = mPosition == LEFT? -1 : 1; } } setInitialPosition = true; } else { if (setInitialPosition) { // now we know content dimensions, so we multiply factors... initX *= mContentWidth; initY *= mContentHeight; // ... and set initial panel's position mGestureListener.setScroll(initX, initY); setInitialPosition = false; // for offsetLocation we have to invert values initX = -initX; initY = -initY; } // offset every ACTION_MOVE & ACTION_UP event event.offsetLocation(initX, initY); } if (!mGestureDetector.onTouchEvent(event)) { if (action == MotionEvent.ACTION_UP) { // tup up after scrolling post(startAnimation); } } return false; } };

~~~

替换为:

~~~

OnTouchListener touchListener = new OnTouchListener() { float touchX, touchY; public boolean onTouch(View v, MotionEvent event) { if (mState == State.ANIMATING) { // we are animating return false; } int action = event.getAction(); if (action == MotionEvent.ACTION_DOWN) { if (mBringToFront) { bringToFront(); } touchX = event.getX(); touchY = event.getY(); } if (!mGestureDetector.onTouchEvent(event)) { if (action == MotionEvent.ACTION_UP) { // tup up after scrolling int size = (int) (Math.abs(touchX - event.getX()) + Math .abs(touchY - event.getY())); if (size == mContentWidth || size == mContentHeight) { mState = State.ABOUT_TO_ANIMATE; //Log.e("size", String.valueOf(size)); //Log.e(String.valueOf(mContentWidth),String.valueOf(mContentHeight)); } post(startAnimation); } } return false; } };

~~~

即可修复这个bug,并且也同样实现了OnClickListener的功能,可以把原Panel的OnClickListener给删掉了!

<think>好的,用户遇到了C++程序中的段错误,具体是在使用getline和replace方法替换字符串中的字符时出现的。我需要一步步分析可能的原因,并给出解决方案。 首先,段错误通常是由于访问了非法内存地址,比如空指针、越界访问或修改只读内存等。用户提到了使用getline和replace方法,可能涉及到字符串的操作。 首先考虑输入是否正确。使用getline读取输入时,确保字符串变量已经正确初始化。例如,如果声明了string s;然后使用getline(cin, s),这是没问题的。但如果用户忘记包含头文件<string>或<iostream>,可能导致未定义行为。但通常这种情况下编译会报错,而不是运行时错误,所以可能性较低。 接下来,检查replace方法的使用。string的replace函数需要确保参数在有效范围内。比如,replace(pos, len, str)中的pos必须小于等于字符串长度。如果pos超出范围,会导致未定义行为,可能引发段错误。例如,假设用户试图替换的位置是s.length(),这会导致越界,因为有效索引是0到length()-1。需要确认用户代码中的pos是否正确计算。 然后,考虑字符串是否被修改为只读内存。例如,如果用户通过某种方式获取了一个指向常量字符串的指针,并尝试修改它,这会导致段错误。例如,char* str = &quot;hello&quot;; str[0] = 'H'; 这种写法是错误的,因为&quot;hello&quot;是常量字符串。但在C++中,使用string类通常不会遇到这个问题,除非用户错误地使用了c_str()返回的指针进行修改。但用户代码中使用的是replace方法,所以这种情况可能性较小,除非用户代码有间接的问题。 另外,检查循环中的条件。例如,用户在循环中替换字符时,可能循环次数超过了字符串的长度。比如,使用for循环遍历每个字符,但终止条件错误地使用了<= length()而不是< length(),导致访问越界。 此外,查看是否有其他指针或引用操作。例如,如果用户代码中涉及指针操作,可能存在野指针或悬挂指针的问题,但问题描述中未提到指针,可能主要集中在string的操作上。 可能的代码示例问题:假设用户代码类似以下情况: string s; getline(cin, s); for (int i = 0; i <= s.length(); i++) { // 这里应该是i < s.length() s.replace(i, 1, &quot;X&quot;); } 这里循环条件错误地用了<=,导致i的值可能等于s.length(),此时replace的pos参数越界,导致段错误。 或者,用户可能错误地处理了空字符串的情况。如果getline读取到空字符串(比如输入了EOF或空行),那么s.length()为0,此时任何replace操作都会导致越界。 此外,检查是否正确地包含了必要的头文件,如<string>和<iostream>,以及是否使用了正确的命名空间(例如using namespace std; 或std::string等)。缺少头文件可能导致未定义行为,但通常编译时会报错,所以可能性较低。 总结可能的原因: 1. 字符串替换的位置参数越界。 2. 循环条件错误导致越界访问。 3. 处理空字符串时的非法操作。 4. 修改了只读内存(可能性较低,但需确认字符串来源)。 建议用户检查这些方面,添加边界条件判断,例如在替换前检查字符串是否为空,确保pos在有效范围内。可以使用s.size()来获取长度,并在访问前判断i是否小于s.size()。另外,使用调试工具如gdb来跟踪段错误发生的具体位置,或者添加输出语句打印字符串长度和替换的位置,帮助定位问题。</think>在C++程序中使用`getline`和`replace`方法时出现段错误(Segmentation Fault),通常由以下原因引起: --- ### 1. **字符串越界访问** - **问题**:`std::string::replace`的参数若超出字符串长度会导致未定义行为。例如,`pos`必须满足`0 <= pos <= str.size()`,而`count`(替换长度)需满足`pos + count <= str.size()`[^1]。 - **示例**: ```cpp std::string s = &quot;abc&quot;; s.replace(3, 1, &quot;X&quot;); // 越界:s.size()=3,但有效索引为0-2 ``` **修复**:替换前检查索引有效性: ```cpp if (pos < s.size()) { s.replace(pos, 1, &quot;X&quot;); } ``` --- ### 2. **空字符串处理** - **问题**:若通过`getline`读取到空字符串(如输入仅换行符),直接调用`replace`会触发越界。 - **示例**: ```cpp std::string s; getline(std::cin, s); // 输入空行 s.replace(0, 1, &quot;X&quot;); // s.size()=0,导致段错误 ``` **修复**:添加空字符串检查: ```cpp if (!s.empty()) { s.replace(0, 1, &quot;X&quot;); } ``` --- ### 3. **输入流未正确初始化** - **问题**:未正确关联输入流或多次读取导致状态错误,可能使`getline`读取无效内存。 - **示例**: ```cpp std::ifstream file(&quot;test.txt&quot;); if (!file) { /* 未检查文件是否打开成功 */ } std::string s; getline(file, s); // 若文件未打开,s可能无效 ``` **修复**:检查流状态: ```cpp if (file.is_open()) { while (getline(file, s)) { /* 操作 */ } } ``` --- ### 4. **内存重叠或非法修改** - **问题**:若尝试修改常量字符串或通过`c_str()`返回的指针写数据,会触发段错误。 - **示例**: ```cpp const char* str = &quot;hello&quot;; // 常量字符串 std::string s(str); s.replace(0, 1, &quot;H&quot;); // 合法,因为s是拷贝 ``` **注意**:直接修改`str`会非法,但`std::string`会创建副本,此处安全。 --- ### 调试建议 1. **使用GDB定位崩溃点**: ```bash g++ -g program.cpp -o program gdb ./program (gdb) run ``` 崩溃后输入`bt`查看调用栈。 2. **打印调试信息**: ```cpp std::cout << &quot;String size: &quot; << s.size() << std::endl; std::cout << &quot;Replace position: &quot; << pos << std::endl; ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值