参考https://github.com/Sbingo/FreeRadioGroup
本文主要是根据自己的需求进行重新定义了一下,并且指出使用时的注意事项
demo下载链接:http://pan.baidu.com/s/1hrRoLla 密码:iuxi
效果图
使用时根布局使用FramLayout
布局
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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="sbingo.demo.Main2Activity">
<demo.kotlin.freelayout.FreeLayout
android:id="@+id/group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:autoBack="true"
app:iconButton="@mipmap/open_door"
app:millisInFuture="500"
app:moveable="true" />
</FrameLayout>
控件代码
package demo.kotlin.freelayout;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.CountDownTimer;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.LinearLayout;
/**
* Created by gjl on 2017/6/8.
*/
public class FreeLayout extends LinearLayout {
/**
* 倒计时过程中
* 回调{@link CountDownTimer#onTick(long)}的间隔时间
*/
private MyCountDownTimer countDownTimer;
/**
* 倒计时时间
*/
private long millisInFuture;
private long countDownInterval = 500;
private boolean moveable, autoBack;
private float toAlpha,
startX, startY,
currentX, currentY;
private int iconButton,
currentLeft, currentTop,
screenWidth, screenHeight,
parentWidth, parentHeight,
viewWidth, viewHight,
minLeftMargin, maxLeftMargin,
minTopMargin, maxTopMargin,
bottomDistance, rightDistance,
leftPadding, topPadding;
public FreeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
screenWidth = displayMetrics.widthPixels;
screenHeight = displayMetrics.heightPixels - getStatusBarHeight();
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.free);
millisInFuture = ta.getInt(R.styleable.free_millisInFuture, 3 * 1000);
toAlpha = ta.getFloat(R.styleable.free_toAlpha, 0.2f);
moveable = ta.getBoolean(R.styleable.free_moveable, false);
autoBack = ta.getBoolean(R.styleable.free_autoBack, false);
iconButton = ta.getResourceId(R.styleable.free_iconButton, -1);
ta.recycle();
//如果作为单一控件使用(没有子控件),手动添加一个子控件,并添加按钮图片
//注:这里使用ImageButton,如果需要文字,建议使用CheckBox,RadioButton,否则会导致拖动失灵(希望知道原理的大神可以回复指点)
if (getChildCount() == 0) {
ImageButton imageButton = new ImageButton(context);
imageButton.setBackgroundResource(iconButton);
addView(imageButton);
}
countDownTimer = new MyCountDownTimer(millisInFuture, countDownInterval);
countDownTimer.start();
//设置默认位置
post(new Runnable() {
@Override
public void run() {
MarginLayoutParams layoutParams = (MarginLayoutParams) getLayoutParams();
layoutParams.topMargin = screenHeight / 2 - getHeight();
layoutParams.leftMargin = screenWidth - getWidth();
setLayoutParams(layoutParams);
}
});
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
if (moveable) {
ViewGroup parentView = ((ViewGroup) getParent());
MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
viewWidth = getRight() - getLeft();
viewHight = getBottom() - getTop();
parentWidth = parentView.getMeasuredWidth();
parentHeight = parentView.getMeasuredHeight();
minLeftMargin = lp.leftMargin;
leftPadding = parentView.getPaddingLeft();
rightDistance = lp.rightMargin + parentView.getPaddingRight();
maxLeftMargin = parentWidth - rightDistance - viewWidth - leftPadding;
minTopMargin = lp.topMargin;
topPadding = parentView.getPaddingTop();
bottomDistance = lp.bottomMargin + parentView.getPaddingBottom();
maxTopMargin = parentHeight - bottomDistance - viewHight - topPadding;
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
startX = ev.getRawX();
startY = ev.getRawY();
setAlpha(1f);
countDownTimer.cancel();
if (moveable) {
MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
currentX = ev.getRawX();
currentY = ev.getRawY();
currentLeft = lp.leftMargin;
currentTop = lp.topMargin;
}
break;
case MotionEvent.ACTION_MOVE:
if (moveable) {
currentLeft += ev.getRawX() - currentX;
currentTop += ev.getRawY() - currentY;
//判断左边界
currentLeft = currentLeft < minLeftMargin ? minLeftMargin : currentLeft;
//判断右边界
currentLeft = (leftPadding + currentLeft + viewWidth + rightDistance) > parentWidth ? maxLeftMargin : currentLeft;
//判断上边界
currentTop = currentTop < minTopMargin ? minTopMargin : currentTop;
//判断下边界
currentTop = (topPadding + currentTop + viewHight + bottomDistance) > parentHeight ? maxTopMargin : currentTop;
MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
lp.leftMargin = currentLeft;
lp.topMargin = currentTop;
setLayoutParams(lp);
currentX = ev.getRawX();
currentY = ev.getRawY();
}
break;
case MotionEvent.ACTION_UP:
float endX = ev.getRawX();
float endY = ev.getRawY();
countDownTimer.start();
if (moveable && autoBack) {
MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
int fromLeftMargin = lp.leftMargin;
if (getLeft() < (parentWidth - getLeft() - viewWidth)) {
lp.leftMargin = minLeftMargin;
} else {
lp.leftMargin = maxLeftMargin;
}
ObjectAnimator marginChange = ObjectAnimator.ofInt(new Wrapper(this), "leftMargin", fromLeftMargin, lp.leftMargin);
marginChange.setDuration(500);
marginChange.start();
}
//滑动距离比较小,当作点击事件处理
if (Math.abs(startX - endX) < 1 && Math.abs(startY - endY) < 1 && mLister != null) {
mLister.onViewClick();
}
break;
default:
}
return super.dispatchTouchEvent(ev);
}
/**
* 点击事件
*/
private OnViewClickListener mLister;
public interface OnViewClickListener {
void onViewClick();
}
public void setOnViewClickListener(OnViewClickListener listener) {
this.mLister = listener;
}
/**
* 包装类
*/
class Wrapper {
private ViewGroup mTarget;
public Wrapper(ViewGroup mTarget) {
this.mTarget = mTarget;
}
public int getLeftMargin() {
MarginLayoutParams lp = (MarginLayoutParams) mTarget.getLayoutParams();
return lp.leftMargin;
}
public void setLeftMargin(int leftMargin) {
MarginLayoutParams lp = (MarginLayoutParams) mTarget.getLayoutParams();
lp.leftMargin = leftMargin;
mTarget.requestLayout();
}
}
public class MyCountDownTimer extends CountDownTimer {
public MyCountDownTimer(long millisInFuture, long countDownInterval) {
super(millisInFuture, countDownInterval);
}
@Override
public void onTick(long millisUntilFinished) {
}
@Override
public void onFinish() {
setAlpha(toAlpha);
}
}
/**
* * 获取状态栏的高度
* * @return 状态栏高度
*
*/
public int getStatusBarHeight() {
int result = 0;
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = getResources().getDimensionPixelSize(resourceId);
}
return result;
}
}