一、内容概述
今天突然碰到这么一个需求:用户可以在任意页面点击某个东西回到首页。拿到这个需求的时候我首先想到的是做一个浮动窗口,这样逻辑不用管在每个页面都可见且可点击。但是突然又想到这该死的机型适配,7.0以上的系统需要开启浮窗权限,且不同的机型有不同的权限控制,且这个权限需要用户手动开启,因为这个功能非常重要,业务部提出来的,而且使用频率相当高,果断放弃了这个方案。考虑来考虑去,还是决定自定义一个ViewGroup,然后控制这个ViewGroup可以在屏幕范围内拖动,通过把这个自定义View放到BaseActivity和BaseFragment的布局中,来实现每个页面都可以触达的这么一个功能,虽然方案上有些瑕疵(每个页面的按钮虽然初始位置相同,但是经过拖动后再到下一个页面按钮就和前一个页面的有所不同了),但是好在需要屏幕适配的东西不多,且比较稳定。下面看看如何来实现这个需求。
二、代码实战
自定义View的实现:package com.source.weight;import android.content.Context;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.widget.LinearLayout;import com.source.utils.PxUtils;import androidx.annotation.Nullable;/** * 自定义一个可以全屏拖动的LinearLayout * create by yangwei * on 2020/8/6 14:11 * @author yangwei */public class DragLinearLayout extends LinearLayout { /** * LinearLayout本身的宽度 */ private int width; /** * LinearLayout本身的高度 */ private int height; /** * 屏幕的宽度 */ private int screenWidth; /** * 屏幕的高度 */ private int screenHeight; /** * 手指点击屏幕时的x坐标 */ private float downX; /** * 手指点击屏幕时的y坐标 */ private float downY; public DragLinearLayout(Context context) { super(context); initScreenWH(); } public DragLinearLayout(Context context, @Nullable AttributeSet attrs) { super(context, attrs); initScreenWH(); } /** * 初始化屏幕的宽高 */ private void initScreenWH() { screenWidth = PxUtils.getScreenWidth(getContext()); screenHeight = PxUtils.getScreenHeight(getContext()); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); width = getMeasuredWidth(); height = getMeasuredHeight(); } @Override public boolean onTouchEvent(MotionEvent event) { super.onTouchEvent(event); if (this.isEnabled()) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: downX = event.getX(); downY = event.getY(); break; case MotionEvent.ACTION_MOVE: final float xDistance = event.getX() - downX; final float yDistance = event.getY() - downY; /** * 如果水平拖动距离或者横向拖动距离大于15的时候才会执行拖动 */ if (Math.abs(xDistance) > 15 || Math.abs(yDistance) > 15) { //左上右下位置 int l, t, r, b; l = (int) (getLeft() + xDistance); r = l + width; t = (int) (getTop() + yDistance); b = t + height; //如果左边超出屏幕 if (l <= 0) { l = 0; r = l + width; //如果右边超出屏幕 } else if (r >= screenWidth) { r = screenWidth; l = r - width; } //如果上面超出屏幕 if (t <= 0) { t = 0; b = t + height; //如果下面超出屏幕 } else if (b >= screenHeight) { b = screenHeight; t = b - height; } this.layout(l, t, r, b); } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: setPressed(false); break; default: Log.e("DragLinearLayout", "执行了未知Action"); break; } return true; } return super.onTouchEvent(event); }}
在BaseActivity中的调用过程: @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.base_layout); LinearLayout view = findViewById(R.id.linear_continer); view.addView(LayoutInflater.from(this).inflate(getLayoutId(), null)); initViews(); findViewById(R.id.btn_window).setOnClickListener(v -> { Toast.makeText(BaseActivity.this, "我点击了", Toast.LENGTH_SHORT).show(); }); }
布局部分:base_layout.xml<?xml version="1.0" encoding="utf-8"?>
三、总结
这样算下来一个简单的可在所有页面全屏拖动的布局就形成了,在布局中还可以放置其他控件,用户可以在其内部实现自己的自定义功能。有些时候在追求绚丽的时候也需要考虑下稳定性,如果没有稳定性,再好的功能也会被用户舍弃。