Android 拖拽按钮总是回到初始位置的坑

记录下自己干的蠢事儿

需求是有个拖动按钮,有个定时器显示时间实现代码如下

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.touch.TouchEventConflictActivity">

    <TextView
        android:id="@+id/tv_time"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:textSize="18sp" />

    <Button
        android:id="@+id/fab"
        android:layout_width="@dimen/size_80"
        android:layout_height="@dimen/size_80"
        android:layout_margin="@dimen/fab_margin"
        android:background="@mipmap/ic_launcher_round"/>

</RelativeLayout>
package com.jennifer.jennifer.ui.touch;

import android.annotation.SuppressLint;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import com.jennifer.jennifer.R;

public class TouchEventConflictActivity extends AppCompatActivity {

    private static final int MSG_TIME = 1001;
    private TextView tvTime;
    private Button fab;
    private long time = 0;
    private Handler mHandler = new Handler() {
        @SuppressLint("HandlerLeak")
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            time += 1000;
            tvTime.setText(getDoublePlaceholderMinSecond(time));
            mHandler.sendEmptyMessageDelayed(MSG_TIME, 1000);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_touch_event_conflict);
        initView();
    }

    private void initView() {
        DisplayMetrics dm = getResources().getDisplayMetrics();
        mScreenWidth = dm.widthPixels;
        mScreenHeight = dm.heightPixels - 450;

        tvTime = findViewById(R.id.tv_time);
        fab = findViewById(R.id.fab);

        tvTime.setText(getDoublePlaceholderMinSecond(time));
        fab.setOnTouchListener(fbTouchListener);

        mHandler.sendEmptyMessageDelayed(MSG_TIME, 1000);
    }

    private long startTime = 0;
    private long endTime = 0;
    private int mScreenWidth;
    private int mScreenHeight;
    private int mLastX;
    private int mLastY;
    private boolean isTuodong;
    View.OnTouchListener fbTouchListener = new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            int action = event.getAction();
            switch (action) {
                case MotionEvent.ACTION_DOWN:
                    isTuodong = false;
                    mLastX = (int) event.getRawX();
                    mLastY = (int) event.getRawY();
                    startTime = System.currentTimeMillis();
                    break;
                case MotionEvent.ACTION_MOVE:
                    isTuodong = false;
                    int dx = (int) event.getRawX() - mLastX;
                    int dy = (int) event.getRawY() - mLastY;

                    int left = v.getLeft() + dx;
                    int top = v.getTop() + dy;
                    int right = v.getRight() + dx;
                    int bottom = v.getBottom() + dy;
                    if (left < 0) {
                        left = 0;
                        right = left + v.getWidth();
                    }
                    if (right > mScreenWidth) {
                        right = mScreenWidth;
                        left = right - v.getWidth();
                    }
                    if (top < 0) {
                        top = 0;
                        bottom = top + v.getHeight();
                    }
                    if (bottom > mScreenHeight) {
                        bottom = mScreenHeight;
                        top = bottom - v.getHeight();
                    }
                    v.layout(left, top, right, bottom);
                    mLastX = (int) event.getRawX();
                    mLastY = (int) event.getRawY();
                    break;
                case MotionEvent.ACTION_UP:
                    endTime = System.currentTimeMillis();
//                //当从点击到弹起小于半秒的时候,则判断为点击,如果超过则不响应点击事件
                    if ((endTime - startTime) > 0.1 * 1000L) {
                        isTuodong = true;
                    } else {
                        isTuodong = false;
                    }
                    break;
            }
            return isTuodong;
        }
    };

    public String getDoublePlaceholderMinSecond(long lTime) {
        StringBuilder strTime = new StringBuilder();
        long day = 0;
        long hour = 0;
        long min = 0;
        long sec = 0;
        day = lTime / (24 * 60 * 60 * 1000);
        hour = (lTime / (60 * 60 * 1000) - day * 24);
        min = ((lTime / (60 * 1000)) - day * 24 * 60 - hour * 60);
        sec = (lTime / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - min * 60);
//		strTime.append(min).append("分").append(sec).append("秒");
        if (min < 10) {
            strTime.append("0");
        }
        strTime.append(min).append("'");
        if (sec < 10) {
            strTime.append("0");
        }
        strTime.append(sec).append("''");
        return strTime.toString();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mHandler != null) {
            if (mHandler.hasMessages(MSG_TIME)) {
                mHandler.removeMessages(MSG_TIME);
            }
            mHandler = null;
        }
    }
}

本以为完美实现,结果运行起来,只要计时器一刷新拖动走的button还会回到原来的位置,看规律就是每次setText()就会导致Button复位

苦思冥想找原因,原因就是TextView的宽高设置成了wrap_content,每次setText()后都会重绘界面
直接说结果,给TextView设置固定宽高,这样每次setText就不需要去重新计算TextView所需宽高,就OK了,其他同。。。

改过之后ok了,随便拖

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
android开发秘籍完整版清晰版 第1 章 android 概述   1 1.1 android 演化史  1 1.2 android 的两面性  2 1.3 运行android 的设备   2 1.3.1 htc 系列机型   4 1.3.2 摩托罗拉系列机型   4 1.3.3 三星系列机型   4 1.3.4 平板电脑   5 1.3.5 其他设备   5 1.4 android 设备的硬件差异   5 1.4.1 屏幕  5 1.4.2 用户输入方式   6 1.4.3 传感器   6 1.5 android 的特点   8 1.5.1 多进程和应用程序微件   8 1.5.2 触摸、手势和多点触控   8 1.5.3 硬键盘和软键盘   8 1.6 android 开发   8 1.6.1 如何使用本书中的秘诀   8 .1.6.2 好好设计应用程序   9 1.6.3 保持向前兼容   9 1.6.4 健壮性  10 1.7 软件开发工具包   10 1.7.1 安装与更新   10 1.7.2 软件特性和api 级别   11 1.7.3 利用模拟器或真机调试程序   12 1.7.4 使用android 调试桥   13 1.7.5 签名和发布应用   14 1.8 android market    14 1.8.1 最终用户许可协议   14 1.8.2 提升应用程序的曝光率   15 1.8.3 脱颖而出   15 1.8.4 为应用程序收费   15 1.8.5 管理评论和更新   16 1.8.6 android market 的候补之选   17 第2 章 应用程序基础知识:activity 和intent    18 2.1 android 应用程序预览   18 2.1.1 秘诀1:创建工程并新建activity    19 2.1.2 工程目录结构及自动生成内容   20 2.1.3 android 包和manifest 清单文件   22 2.1.4 重命名应用程序中的部分文件   23 2.2 activity 的生命周期   23 2.2.1 秘诀2:使用其他的生命周期方法  24 2.2.2 秘诀3:强制执行单任务模式   26 2.2.3 秘诀4:强制屏幕方向   26 2.2.4 秘诀5:保存和恢复activity信息   27 2.3 多个activity   28 2.3.1 秘诀6:使用按钮和文本框   28 2.3.2 秘诀7:通过事件启动另外一个activity    29 2.3.3 秘诀8:将语音转换成文本并启动activity 显示结果   32 2.3.4 秘诀9:实现选择列表   34 2.3.5 秘诀10:使用隐式intent 创建activity    35 2.3.6 秘诀11:在activity 间传递基本数据类型   37 第3 章 线程、服务、receiver 以及alert 对话框   40 3.1 线程   40 3.1.1 秘诀12:启动一个辅助线程  40 3.1.2 秘诀13:创建实现runnable接口的activity    44 3.1.3 秘诀14:设置线程优先级   45 3.1.4 秘诀15:取消线程   45 3.1.5 秘诀16:在两个应用程序之间共享线程   46 3.2 线程之间的消息机制:handler   46 3.2.1 秘诀17:从主线程调度runnable 任务   46 3.2.2 秘诀18:使用倒数计时器   49 3.2.3 秘诀19:处理耗时的初始化工作  50 3.3 服务  51 3.4 添加broadcast receiver    56 3.5 应用微件   58 3.6 alert 对话框   60 3.6.1 秘诀23:使用toast 在屏幕上显示简短消息   61 3.6.2 秘诀24:使用alert 对话框   61 3.6.3 秘诀25:在状态栏中显示通知   62 第4 章 用户界面布局   65 4.1 资源目录及其基本属性   65 4.2 view 和viewgroup   67 4.2.1 秘诀27:利用eclipse 编辑器生成布局   68 4.2.2 秘诀28:控制ui 元素的宽度和高度   71 4.2.3 秘诀29:设置相对布局和布局id   73 4.2.4 秘诀30:通过编程声明布局   74 4.2.5 秘诀31:使用独立线程更新布局  75 4.3 文本操作   78 4.3.1 秘诀32:设置和更改文本属性   79 4.3.2 秘诀33:提供文本输入   81 4.3.3 秘诀34:创建表单  82 4.4 其他控件:从按钮到拖动条   83 4.4.1 秘诀35:在表格布局中使用图像按钮   83 4.4.2 秘诀36:使用复选框和开关按钮   86 4.4.3 秘诀37:使用单选按钮   90 4.4.4 秘诀38:创建下拉菜单   90 4.4.5 秘诀39:使用进度条   92 4.4.6 秘诀40:使用拖动条   94 第5 章 用户界面事件   97 5.1 事件处理器和事件监听器   97 5.1.1 秘诀41:截取物理按键事件  97 5.1.2 秘诀42:创建菜单   100 5.1.3 秘诀43:在xml 文件中定义菜单   104 5.1.4 秘诀44:使用搜索键   105 5.1.5 秘诀45:响应触摸事件   107 5.1.6 秘诀46:监听滑动手势   109 5.1.7 秘诀47:使用多点触控   110 5.2 高级用户界面库  113 5.2.1 秘诀48:使用手势   114 5.2.2 秘诀49:绘制3d 图像   117 第6 章 多媒体技术   122 6.1 图像   123 6.2 音频   128 6.2.1 秘诀51:选取和播放音频文件   128 6.2.2 秘诀52:录制音频文件   131 6.2.3 秘诀53:处理原始音频   132 6.2.4 秘诀54:有效使用音频资源   136 6.2.5 秘诀55:添加媒体资源并更新路径   137 6.3 视频  138 第7 章 硬件接口   140 7.1 照相机   140 7.2 其他传感器   145 7.2.1 秘诀57:获取设备旋转姿态  146 7.2.2 秘诀58:使用温度传感器和光传感器   149 7.3 电话  150 7.3.1 秘诀59:使用电话管理器  150 7.3.2 秘诀60:监听电话状态   152 7.3.3 秘诀61:拨打电话号码   154 7.4 蓝牙  154 7.4.1 秘诀62:打开蓝牙   155 7.4.2 秘诀63:搜索蓝牙设备   155 7.4.3 秘诀64:与已绑定的蓝牙设备配对   156 7.4.4 秘诀65:打开蓝牙套接字  156 7.4.5 秘诀66:使用设备振动功能   159 7.4.6 秘诀67:访问无线网络   159 第8 章 网络通信   161 8.1 使用短信息   161 8.2 使用web 内容   169 8.2.1 秘诀69:定制web 浏览器   169 8.2.2 秘诀70:使用http get请求  170 8.2.3 秘诀71:使用http post请求  174 8.3 社交网络  174 第9 章 数据存储方法   184 9.1 shared preferences   184 9.1.1 秘诀73:创建和检索sharedpreferences    184 9.1.2 秘诀74:使用preferences框架   185 9.1.3 秘诀75:基于stored data改变用户界面   187 9.1.4 秘诀76:添加最终用户许可协议   190 9.2 sqlite 数据库   194 9.2.1 秘诀77:创建一个独立的数据库包   194 9.2.2 秘诀78:使用独立的数据库包   197 9.2.3 秘诀79:创建个人日记   200 9.3 内容提供器  204 9.4 保存和载入文件   209 第10 章 基于位置的服务   210 10.1 位置服务入门   210 10.1.1 秘诀81:获取最新位置   212 10.1.2 秘诀82:在位置改变时更新信息   212 10.1.3 秘诀83:列出所有可用的提供器   214 10.1.4 秘诀84:将位置解析为地址(反向地理编码)   216 10.1.5 秘诀85:将地址解析为位置(地理编码)    218 10.2 使用谷歌地图   220 10.2.1 秘诀86:在应用程序中添加谷歌地图   222 10.2.2 秘诀87:在地图上添加标记   224 10.2.3 秘诀88:在地图上添加视图   228 10.2.4 秘诀89:在地图上标记设备的当前位置   230 10.2.5 秘诀90:设置临近警告   231 第11 章 android 高级开发技术   232 11.1 android 的自定义视图   232 11.2 android 的原生组件   238 11.3 android 的安全机制   241 11.4 android 的进程间通信   242 11.5 android 的备份管理器   247 11.5.1 秘诀95:备份运行时数据   247 11.5.2 秘诀96:备份文件到云端   248 11.5.3 秘诀97:触发备份与还原操作   249 11.6 android 的动画功能   250 第12 章 调试   255 12.1 eclipse 内置的调试工具   255 12.1.1 秘诀99:设置运行配置   255 12.1.2 秘诀100:使用ddms   256 12.1.3 秘诀101:断点调试   257 12.2 android sdk 中的调试工具   258 12.2.1 秘诀102:使用android debug bridge 工具   258 12.2.2 秘诀103:使用logcat 工具   259 12.2.3 秘诀104:使用hierarchyviewer 工具   261 12.2.4 秘诀105:使用traceview工具   262 12.3 android 系统调试工具   264
要实现可拖动悬浮按钮,可以使用Android中的WindowManager类和LayoutParams类。 首先,在你的Activity或Fragment中创建一个悬浮按钮,例如: ```java Button button = new Button(this); button.setText("悬浮按钮"); ``` 然后,使用WindowManager将该按钮添加到屏幕上,并设置其LayoutParams: ```java WindowManager.LayoutParams params = new WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT ); params.gravity = Gravity.TOP | Gravity.START; params.x = 0; params.y = 0; WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE); windowManager.addView(button, params); ``` 这里的LayoutParams设置了按钮的宽度和高度为自适应,类型为TYPE_APPLICATION_OVERLAY(表示悬浮在其他应用程序上方),不可获得焦点,透明度为半透明。gravity设置了按钮在屏幕上的位置,x和y设置了按钮初始位置。 接下来,可以为按钮添加一个触摸监听器,以便用户可以通过拖动来移动按钮: ```java button.setOnTouchListener(new View.OnTouchListener() { private int initialX; private int initialY; private float initialTouchX; private float initialTouchY; @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: initialX = params.x; initialY = params.y; initialTouchX = event.getRawX(); initialTouchY = event.getRawY(); return true; case MotionEvent.ACTION_UP: return true; case MotionEvent.ACTION_MOVE: params.x = initialX + (int) (event.getRawX() - initialTouchX); params.y = initialY + (int) (event.getRawY() - initialTouchY); windowManager.updateViewLayout(button, params); return true; } return false; } }); ``` 在这个触摸监听器中,我们记录了按钮初始位置和触摸位置,并在移动事件中更新了LayoutParams中的x和y值,并通过WindowManager的updateViewLayout方法更新了按钮位置。 这样就可以实现一个可拖动的悬浮按钮了。记得要在不需要的时候将其从屏幕上移除: ```java windowManager.removeView(button); ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值