Android项目:手机安全卫士(11)—— 归属地提示框拖拽效果

Android项目:手机安全卫士(11)—— 归属地提示框拖拽效果

1 介绍

关于号码归属地显示的功能,还差的就是它的拖拽效果了。它在两个地方可以拖拽,一个是在提示框位置的设置界面,一个是在来电界面,如图所示。

设置归属地显示位置 归属地提示框显示效果

关于项目相关文章,请访问:

项目源码地址(实时更新):https://github.com/xwdoor/MobileSafe

2 添加设置入口

创建一个自定义 View,用于设置界面,作为设置提示框位置的二级入口菜单,自定义控件与前面讲过的自定义组合控件类似,只是做了一定的简化,代码如下:


    /**
     * Created by XWdoor on 2016/3/1 001 16:19.
     * 博客:http://blog.csdn.net/xwdoor
     */
    public class SettingClickView extends RelativeLayout {

        public static final String NAMESPACE_XWDOOR = "http://schemas.android.com/apk/res-auto";


        private TextView tvTitle;
        private TextView tvDesc;
        private CheckBox cbCheck;

        public SettingClickView(Context context) {
            super(context);
            initView();
        }

        public SettingClickView(Context context, AttributeSet attrs) {
            super(context, attrs);

            initView();
        }

        public SettingClickView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            initView();
        }

        private void initView(){
            View view = View.inflate(getContext(), R.layout.item_setting_click,null);
            tvTitle = (TextView) view.findViewById(R.id.tv_title);
            tvDesc = (TextView) view.findViewById(R.id.tv_desc);

            addView(view);
        }

        /** 设置标题 */
        public void setTitle(String title){
            tvTitle.setText(title);
        }

        /** 设置描述 */
        public void setDesc(String desc){
            tvDesc.setText(desc);
        }
    }

它的布局文件我就贴出来了,与之前的 Item 惊人类似。然后在后台代码中设置它的标题与描述,代码如下:


    scvLocation.setTitle("归属地提示框位置");
    scvLocation.setDesc("设置归属地提示框位置");
    scvLocation.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            DragViewActivity.startAct(SettingActivity.this);
        }
    });

3 创建 Activity:DragViewActivity

该 Activity 用于实现拖拽效果,并作为归属地提示框的显示位置,效果如图:

设置归属地显示位置

这是它的布局文件:


    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#0a000000">

        <TextView
            android:id="@+id/tv_top"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@drawable/call_locate_blue"
            android:gravity="center"
            android:padding="20dp"
            android:text="按住提示框拖动到任意位置,按手机返回键立即生效"
            android:textColor="#000"
            android:textSize="20sp" />

        <TextView
            android:id="@+id/tv_bottom"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:background="@drawable/call_locate_blue"
            android:gravity="center"
            android:padding="20dp"
            android:text="按住提示框拖动到任意位置,按手机返回键立即生效"
            android:textColor="#000"
            android:textSize="20sp"
            android:visibility="invisible" />

        <TextView
            android:id="@+id/tv_drag_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="190dp"
            android:background="@drawable/call_locate_white"
            android:drawableLeft="@android:drawable/ic_menu_call"
            android:gravity="center"
            android:text="未知号码"
            android:textColor="#fff"
            android:textSize="18sp" />

    </RelativeLayout>

后台代码如下,重点是提示框的触摸事件的处理逻辑:


    /**
     * Created by XWdoor on 2016/3/15 015 10:02.
     * 博客:http://blog.csdn.net/xwdoor
     */
    public class DragViewActivity extends BaseActivity {

        private int startX;
        private int startY;
        private int mScreenWidth;
        private int mScreenHeight;

        public static void startAct(Context context) {
            Intent intent = new Intent(context, DragViewActivity.class);
            context.startActivity(intent);
        }

        @Override
        public void initVariables() {
            //得到屏幕宽高度
            WindowManager wm = getWindowManager();
            Point size = new Point();
            wm.getDefaultDisplay().getSize(size);
            mScreenWidth = size.x;
            mScreenHeight = size.y;

            //获取归属地提示框位置
            startX = PrefUtils.getInt(PREF_LAST_X,0,this);
            startY = PrefUtils.getInt(PREF_LAST_Y,0,this);
        }

        @Override
        protected void initViews(Bundle savedInstanceState) {
            setContentView(R.layout.activity_drag_view);
            final TextView tvDragView = (TextView) findViewById(R.id.tv_drag_view);
            final TextView tvTop = (TextView) findViewById(R.id.tv_top);
            final TextView tvBottom = (TextView) findViewById(R.id.tv_bottom);
            // 根据布局位置,更新提示框位置
            if (startY > mScreenHeight / 2) {// 屏幕下方
                tvTop.setVisibility(View.VISIBLE);
                tvBottom.setVisibility(View.INVISIBLE);
            } else {
                tvTop.setVisibility(View.INVISIBLE);
                tvBottom.setVisibility(View.VISIBLE);
            }
            // 获取当前控件的布局参数
            // 父控件是谁,就拿谁定义的布局参数
            RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) tvDragView.getLayoutParams();
            // 临时修改布局参数
            layoutParams.leftMargin = startX;
            layoutParams.topMargin = startY;

            // 设置触摸监听
            tvDragView.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    switch (event.getAction()) {
                        case MotionEvent.ACTION_DOWN:
                            // 记录起点坐标
                            startX = (int) event.getRawX();
                            startY = (int) event.getRawY();
                            break;
                        case MotionEvent.ACTION_MOVE:
                            // 获取移动后坐标
                            int endX = (int) event.getRawX();
                            int endY = (int) event.getRawY();

                            // 计算偏移量
                            int dx = endX - startX;
                            int dy = endY - startY;

                            // 根据偏移量,更新位置
                            int l = tvDragView.getLeft() + dx;
                            int t = tvDragView.getTop() + dy;
                            int r = tvDragView.getRight() + dx;
                            int b = tvDragView.getBottom() + dy;

                            //避免布局超出屏幕边界
                            if (l >= 0 && r <= mScreenWidth && t >= 0 && b <= mScreenHeight - 20) {//减去状态栏,大概20像素
                                // 根据布局位置,更新提示框位置
                                if (t > mScreenHeight / 2) {// 屏幕下方
                                    tvTop.setVisibility(View.VISIBLE);
                                    tvBottom.setVisibility(View.INVISIBLE);
                                } else {
                                    tvTop.setVisibility(View.INVISIBLE);
                                    tvBottom.setVisibility(View.VISIBLE);
                                }

                                tvDragView.layout(l, t, r, b);

                                startX = (int) event.getRawX();
                                startY = (int) event.getRawY();
                            }
                            break;
                        case MotionEvent.ACTION_UP:
                            //记录最终坐标
                            PrefUtils.putInt(PREF_LAST_X, tvDragView.getLeft(), DragViewActivity.this);
                            PrefUtils.putInt(PREF_LAST_Y, tvDragView.getTop(), DragViewActivity.this);
                            break;
                    }
                    return true;
                }
            });
        }

        @Override
        protected void loadData() {

        }
    }

触摸事件中处理控件的移动偏移量,并移动控件,重要的是它的边界判断,超出边界后不处理它的移动,代码中都有很好的提示,没有多少难度。这里需要说明的是,在刚进入 Activity 时,要初始化上次保存的位置,这个时候不能直接使用 getWidth() 与 getHeight() 方法,因为这些方法只有在系统完成绘制之后才会返回有效值,而 measure(测量)->layout(设置位置)->onDraw(绘制), 这些方法必须在oncreate方法执行完了之后才会调用,所以我们只有获取相应的布局参数 LayoutParams,通过修改 LayoutParams 来达到初始化的目的。

4 来电和去电显示归属地提示框

来电时显示的归属地提示框也需要实现拖拽效果,它与 Activity 中实现还有一定区别,因为它显示在 WindowManager 中,这里主要给出触摸事件处理的代码,添加在 PhoneListener 中:


    tvAddress.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    // 记录起点坐标
                    startX = (int) event.getRawX();
                    startY = (int) event.getRawY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    // 获取移动后坐标
                    int endX = (int) event.getRawX();
                    int endY = (int) event.getRawY();

                    // 计算偏移量
                    int dx = endX - startX;
                    int dy = endY - startY;

                    // 根据偏移量,更新位置
                    // 根据偏移量,更新位置
                    params.x = params.x + dx;
                    params.y = params.y + dy;


                    // 防止坐标越界
                    if (params.x < 0) {
                        params.x = 0;
                    }

                    // 防止坐标越界
                    if (params.x + mView.getWidth() > mScreenWidth) {
                        params.x = mScreenWidth - mView.getWidth();
                    }

                    // 防止坐标越界
                    if (params.y < 0) {
                        params.y = 0;
                    }

                    // 防止坐标越界
                    if (params.y + mView.getHeight() > mScreenHeight - 20) {
                        params.y = mScreenHeight - 20 - mView.getHeight();
                    }
                    //减去状态栏,大概20像素
                    //根据布局位置,更新提示框位置
                     mWM.updateViewLayout(mView,params);

                        startX = (int) event.getRawX();
                        startY = (int) event.getRawY();
                    break;
                case MotionEvent.ACTION_UP:
                    //记录最终坐标
                    PrefUtils.putInt(BaseActivity.PREF_LAST_X, params.x, mContext);
                    PrefUtils.putInt(BaseActivity.PREF_LAST_Y, params.y, mContext);
                    break;
            }
            return true;
        }
    });

主要区别在于控件超出界面时的处理,以及跟新控件布局是调用的 api 为 WindowManager 的方法:updateViewLayout()。对了,要让 WindowManager 中的控件可触摸,需提高布局参数的类型级别:params.type = WindowManager.LayoutParams.TYPE_PHONE; ,这样才保证可以触摸移动。当然,这个是需要权限的:<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

5 总结

今天的任务算是上次的遗留问题,拖拽效果并不难实现,关键是一些细节的处理。关于自定义风格,代码就不给出了,具体实现可以下载源代码查看,主要思路是:不同风格就设置不同的背景图片。

关于项目相关文章,请访问:

项目源码地址(实时更新):https://github.com/xwdoor/MobileSafe

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值