第二次做拖拽吸邊的功能了,但還是費(fèi)了不少時(shí)間蘑险,所以簡單記錄下!
1.參數(shù):
<declare-styleable name="MoveFrameLayout">
<!--是否需要自動(dòng)吸邊-->
<attr name="IsAttach" format="boolean" />
<!--是否可拖曳-->
<attr name="IsDrag" format="boolean" />
</declare-styleable>
2.代碼:
package 包名.weight;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.OvershootInterpolator;
import android.widget.FrameLayout;
import com.blankj.utilcode.util.SizeUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import 包名.R;
/**
* Des: 拖拽漠其、自吸邊控件
* Created by kele on 2021/1/6.
* E-mail:984127585@qq.com
*/
public class MoveFrameLayout extends FrameLayout {
public MoveFrameLayout(@NonNull Context context) {
this(context, null);
}
public MoveFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public MoveFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initAttrs(context, attrs);
}
private boolean isAttach;//是否自動(dòng)吸邊
private boolean isDrag;//是否可拖動(dòng)
/**
* 初始化自定義屬性
*/
private void initAttrs(Context context, AttributeSet attrs) {
TypedArray mTypedAttay = context.obtainStyledAttributes(attrs, R.styleable.MoveFrameLayout);
isAttach = mTypedAttay.getBoolean(R.styleable.MoveFrameLayout_IsAttach, true);
isDrag = mTypedAttay.getBoolean(R.styleable.MoveFrameLayout_IsDrag, true);
mTypedAttay.recycle();
}
private int mParentWidth = 0;//父控件的寬
private int mParentHeight = 0;//父控件的高
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//獲取父控件寬高
ViewGroup mViewGroup = (ViewGroup) getParent();
if (mViewGroup != null) {
int[] location = new int[2];
mViewGroup.getLocationOnScreen(location);
//獲取父布局的高度
mParentHeight = mViewGroup.getMeasuredHeight();
mParentWidth = mViewGroup.getMeasuredWidth();
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
//點(diǎn)擊點(diǎn)在控件區(qū)域內(nèi),接受事件
boolean inRangeOfView = inRangeOfView(this, ev);
return inRangeOfView;
}
private float mLastX;//按下位置x
private float mLastY;//按下位置Y
private boolean isDrug = false;//是否是拖動(dòng)
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (isDrag) {
//請求父控件將事件交由自己處理
getParent().requestDisallowInterceptTouchEvent(true);
float mX = ev.getX();
float mY = ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
//重置拖動(dòng)狀態(tài)
isDrug = false;
//記錄按下的位置
mLastX = mX;
mLastY = mY;
break;
case MotionEvent.ACTION_MOVE:
//手指X軸滑動(dòng)距離
float differenceValueX = mX - mLastX;
//手指Y軸滑動(dòng)距離
float differenceValueY = mY - mLastY;
//判斷是否為拖動(dòng)操作
if (!isDrug) {
if (Math.sqrt(differenceValueX * differenceValueX + differenceValueY * differenceValueY) < 2) {
isDrug = false;
} else {
isDrug = true;
}
}
//獲取手指按下的距離與控件本身X軸的距離
float ownX = getX();
//獲取手指按下的距離與控件本身Y軸的距離
float ownY = getY();
//理論中X軸拖動(dòng)的距離
float endX = ownX + differenceValueX;
//理論中Y軸拖動(dòng)的距離
float endY = ownY + differenceValueY;
//X軸可以拖動(dòng)的最大距離
float maxX = mParentWidth - getWidth();
//Y軸可以拖動(dòng)的最大距離
float maxY = mParentHeight - getHeight() - SizeUtils.dp2px(getPaddingBottom() + 15);
//X軸邊界限制---這里對X軸拖動(dòng)不做限制
//endX = endX < 0 ? 0 : endX > maxX ? maxX : endX;
//Y軸邊界限制
endY = endY < 0 ? 0 : endY > maxY ? maxY : endY;
//開始移動(dòng)
setX(endX);
setY(endY);
break;
case MotionEvent.ACTION_UP:
//根據(jù)自定義屬性判斷是否需要貼邊
if (isAttach) {
//判斷是否為點(diǎn)擊事件
float center = mParentWidth / 2;
//自動(dòng)貼邊
if (ev.getRawX() <= center) {
//向左貼邊
animate()
//.setInterpolator(new BounceInterpolator())
.setInterpolator(new OvershootInterpolator())
.setDuration(400)
.x(SizeUtils.dp2px(0))
.start();
} else {
//向右貼邊
animate()
.setInterpolator(new OvershootInterpolator())
.setDuration(400)
.x(mParentWidth - getWidth() - SizeUtils.dp2px(0))
.start();
}
}
break;
}
}
return isDrug ? true : super.onTouchEvent(ev);
}
/**
* 點(diǎn)擊點(diǎn)是否在控件區(qū)域
*
* @param view
* @param ev
* @return
*/
private boolean inRangeOfView(View view, MotionEvent ev) {
Rect rect = new Rect();
view.getGlobalVisibleRect(rect);
int rawX = (int) ev.getRawX();
int rawY = (int) ev.getRawY();
if (rect.contains(rawX, rawY)) {
return true;
}
return false;
}
}
3.使用:
<?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"
android:layout_width="match_parent"
android:paddingBottom="@dimen/dp_50"
android:layout_height="match_parent">
<包名.weight.MoveFrameLayout
android:id="@+id/mfl_fb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
app:IsAttach="true"
app:IsDrag="true"
android:layout_marginBottom="@dimen/dp_50"
android:padding="@dimen/dp_12">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_fb" />
</包名.weight.MoveFrameLayout>
</FrameLayout>
拖動(dòng)控件添加縮放功能
在上述MoveFrameLayout 類中添加方法和變量
private boolean isScaled;//是否已經(jīng)放大
private AnimatorSet mAnimatorSet;//動(dòng)畫集合
/**
* 執(zhí)行動(dòng)畫
*
* @param valueFrom
* @param valueTo
* @param duration
*/
private void startScaleAnimator(float valueFrom, float valueTo, int duration) {
ObjectAnimator scaleX = ObjectAnimator.ofFloat(this, "scaleX", valueFrom, valueTo);
ObjectAnimator scaleY = ObjectAnimator.ofFloat(this, "scaleY", valueFrom, valueTo);
if (null == mAnimatorSet) {
mAnimatorSet = new AnimatorSet();
}
mAnimatorSet.play(scaleX).with(scaleY);
mAnimatorSet.setDuration(duration);
mAnimatorSet.start();
}
在上述MoveFrameLayout-onTouchEvent();中添加代碼:
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
//...
isScaled = false;
break;
case MotionEvent.ACTION_MOVE:
//...
if (isDrug && !isScaled) {
//放大動(dòng)畫
startScaleAnimator(1.0f, 1.1f, 300);
//Animator scaleAnimator = AnimatorInflater.loadAnimator(getContext(), R.animator.anim_scale_up);
//scaleAnimator.setTarget(this);
//scaleAnimator.start();
isScaled = true;
}
break;
case MotionEvent.ACTION_UP:
//...
if (isScaled) {
//縮小動(dòng)畫
startScaleAnimator(1.1f, 1.0f, 300);
//Animator scaleAnimator = AnimatorInflater.loadAnimator(getContext(), R.animator.anim_scale_down);
//scaleAnimator.setTarget(this);
//scaleAnimator.start();
isScaled = false;
}
break;
}