仿造微信界面左右拖拽的UI

*知识点:
在右侧动画的时候只能用属性动画,并做兼容
float scale = 0.3f + left * 0.7f / mLeftWidth;
ViewHelper.setScaleX(mLeftView,
scale);
ViewHelper.setScaleY(mLeftView,
scale);
注:属性动画在3.0之后才有的
需要9oldAnimator



自定义的DragLayout
public class DragLayout
extends FrameLayout {
public static final int STATE_IDLE = 0;//空闲状态
public static final int STATE_DRAGING = 1;//正在拖拽的状态
public static final int STATE_FLING = 2;//快速的滑动的状态

private static final String TAG = "DragLayout";
private View mLeftView;// 左侧的view
private View mRightView;// 右侧的view
private View mMainView;//中间的View
private float mLeftWidth;//左侧view的宽度
private float mRightWidth;//右侧view的宽度
private boolean hasLeft;//有左侧的view
private boolean hasRight;//有右侧的view
private ViewDragHelper mDragHelper;
private int mMinVelocity;
private boolean isLeftOpened = false;//用来记录左侧是否打开
private boolean isRightOpened = false;//用来记录右侧是否打开
private int mCurrentState = STATE_IDLE;//默认为闲置状态

private boolean isTouching = false;//用来记录是否是正在触摸view
private OnLayoutDragListener mListener;//拖拽的监听

private boolean canOpenLeft = true;//默认左侧是可以打开的
private boolean canOpenRight = true;//默认右侧是可以打开的

public DragLayout(Context context) {
this(context,
null);
}

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


//常量类,不同的手机获取的值不同
ViewConfiguration configuration = ViewConfiguration.get(context);

//获取系统参考的最小速率
mMinVelocity = configuration.getScaledMinimumFlingVelocity();
}

@Override
protected void onFinishInflate() {
//当xml加载完成的回调
int count = getChildCount();

//不能超过三个View,不能少于2个
if (count > 3 || count < 2) {
throw new IllegalArgumentException("控件个数不对");
}

//找到三个自孩子,和左右view的宽度
for (int i = 0; i < count; i++) {
View view = getChildAt(i);
//分别找到三子孩子
FrameLayout.LayoutParams params = (LayoutParams) view.getLayoutParams();
switch (params.gravity) {
case Gravity.LEFT:
mLeftView = view;
mLeftWidth = params.width;
break;
case Gravity.RIGHT:
mRightView = view;
mRightWidth = params.width;
break;
default:
mMainView = view;
break;
}
}

//要有中间的view,左侧或右侧

if (mMainView == null) {
throw new IllegalArgumentException("没有主view");
}
//判断左右的view是否存在
hasLeft = mLeftView != null;
hasRight = mRightView != null;

//初始化ViewDraghelper----------
mDragHelper = ViewDragHelper.create(this,
new DragCallBack());
}

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
isTouching = true;
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
isTouching = false;
break;
}

return super.dispatchTouchEvent(ev);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
mDragHelper.processTouchEvent(event);
return true;
}

/**
* 左侧的View做动画
*/
private void leftAnimation(int left) {
// 1.从左往右拖动时
if (left < 0 || !hasLeft) {
return;
}

//让左侧的view做动画

//设置中轴
ViewHelper.setPivotX(mLeftView,
mLeftView.getLeft());

ViewHelper.setPivotY(mLeftView,
mLeftView.getMeasuredHeight() / 2);

//1. 缩放
float scale = 0.3f + left * 0.7f / mLeftWidth;
ViewHelper.setScaleX(mLeftView,
scale);
ViewHelper.setScaleY(mLeftView,
scale);

//2. 透明度
ViewHelper.setAlpha(mLeftView,
scale);
}

/**
* 中间的View做动画
*/
private void mainAnimation(int left) {

if (left > 0) {
//从左往右做动画

// 缩放
//设置中轴
ViewHelper.setPivotX(mMainView,
0);

ViewHelper.setPivotY(mMainView,
mMainView.getMeasuredHeight() / 2);

//1. 缩放
float scale = 1.0f - left * 0.2f / mLeftWidth;
ViewHelper.setScaleX(mMainView,
scale);
ViewHelper.setScaleY(mMainView,
scale);

} else {
//从右往左做动画
// 缩放
//设置中轴
ViewHelper.setPivotX(mMainView,
mMainView.getMeasuredWidth());

ViewHelper.setPivotY(mMainView,
mMainView.getMeasuredHeight() / 2);

//1. 缩放
float scale = 1.0f + left * 0.2f / mLeftWidth;
ViewHelper.setScaleX(mMainView,
scale);
ViewHelper.setScaleY(mMainView,
scale);
}
}

private void rightAnimation(int left) {

if (left > 0 || !hasRight) {
return;
}
//从右往左滑动


ViewHelper.setPivotX(mRightView,
mRightView.getMeasuredWidth());
ViewHelper.setPivotY(mRightView,
mRightView.getMeasuredHeight() / 2);
//1.缩放动画
float scale = 0.3f - left * 0.7f / mRightWidth;
ViewHelper.setScaleX(mRightView,
scale);
ViewHelper.setScaleY(mRightView,
scale);
}

@Override
public void computeScroll() {
if (mDragHelper.continueSettling(true)) {
invalidate();
}

}

public void setOnLayoutDragListener(OnLayoutDragListener listener) {
this.mListener = listener;
}

/**
* 设置左侧是否可以打开
*
* @param canOpenLeft
*/
public void setCanOpenLeft(boolean canOpenLeft) {
this.canOpenLeft = canOpenLeft;
}

/**
* 设置右侧是否可以打开
*
* @param canOpenRight
*/
public void setCanOpenRight(boolean canOpenRight) {
this.canOpenRight = canOpenRight;
}

public interface OnLayoutDragListener {
//用来暴露左侧是打开还是关闭的
void onLeftToggle(boolean open);

//用来暴露右侧是打开还是关闭的
void onRightToggle(boolean open);

//当状态改变时的回调
void onDragStateChanged(int state);
}

class DragCallBack
extends ViewDragHelper.Callback {


@Override
public boolean tryCaptureView(View view,
int i) {
return mMainView == view;
}

@Override
public int clampViewPositionHorizontal(View child,
int left,
int dx) {
if (child == mMainView) {
if (left > 0 && !canOpenLeft) {
//从左侧往右拖动,左侧不可以打开
return 0;
} else if (left < 0 && !canOpenRight) {
//从右往左侧拖动,右侧不可以打开
return 0;
} else if (left > 0 && left > mLeftWidth) {
// 从左往右拖动, 并且大于左侧的宽度
return (int) (mLeftWidth + 0.5f);
} else if (left < 0 && -left > mRightWidth) {
//从右往左侧拖动,并且拖出的距离大于右侧的宽度
return (int) -(mRightWidth + 0.5f);
}
}

return left;
}

@Override
public void onViewPositionChanged(View changedView,
int left,
int top,
int dx,
int dy) {
invalidate();

if (mMainView == changedView) {
//左侧的View需要做动画,中间的view也需要做动画
leftAnimation(left);

mainAnimation(left);

rightAnimation(left);
}

Log.d(TAG,
"left : " + left);

if (mCurrentState != STATE_DRAGING && mCurrentState != STATE_FLING) {

if (left != 0 && left != mLeftWidth && left != -mRightWidth) {
// 正在拖动 #####
mCurrentState = STATE_DRAGING;

// 回调接口
if (mListener != null) {
mListener.onDragStateChanged(mCurrentState);
}
}
}

// 闲置状态的逻辑-->关闭
if (left == 0 && !isTouching) {
if (dx < 0) {
// 关闭了左侧
//如果从右边往左边走,增量值为 负数
Log.d(TAG,
"关闭左侧");
isLeftOpened = false;

//接口回调
if (mListener != null) {
mListener.onLeftToggle(isLeftOpened);
}

} else {
// 关闭了右侧
Log.d(TAG,
"关闭右侧");

isRightOpened = false;

//回调方法
if (mListener != null) {
mListener.onRightToggle(isRightOpened);
}
}

//手指需要抬起,现在是闲置状态 ####
mCurrentState = STATE_IDLE;

// 回调接口
if (mListener != null) {
mListener.onDragStateChanged(mCurrentState);
}
}

// 闲置状态的逻辑-->左侧打开
if (left == mLeftWidth && !isTouching) {
// 打开了左侧
Log.d(TAG,
"打开了左侧");
isLeftOpened = true;
//接口回调
if (mListener != null) {
mListener.onLeftToggle(isLeftOpened);
}

//手指需要抬起,现在是闲置状态 ####
mCurrentState = STATE_IDLE;

// 回调接口
if (mListener != null) {
mListener.onDragStateChanged(mCurrentState);
}
}

// 闲置状态的逻辑-->右侧打开
if (left == -mRightWidth && !isTouching) {
// 打开了右侧
Log.d(TAG,
"打开了右侧");
isRightOpened = true;
//回调方法
if (mListener != null) {
mListener.onRightToggle(isRightOpened);
}

//手指需要抬起,现在是闲置状态 ####
mCurrentState = STATE_IDLE;

// 回调接口
if (mListener != null) {
mListener.onDragStateChanged(mCurrentState);
}
}

}


@Override
public void onViewReleased(View releasedChild,
float xvel,
float yvel) {
// 手指抬起时的回调

//速率xvel:x方向的速率
Log.d("dragLayout",
"xvel : " + xvel + " minX : " + mMinVelocity);

if (releasedChild == mMainView) {
int left = mMainView.getLeft();

if (left > 0) {
Log.d("DragLayout",
"left : " + left + " width : " + mLeftWidth);
//从左往右拖动的

if (xvel > mMinVelocity && canOpenLeft) {
//应该打开 左侧
//中间的View打开
mDragHelper.smoothSlideViewTo(mMainView,
(int) (mLeftWidth + 0.5f),
0);

//改变状态 ####
mCurrentState = STATE_FLING;

// 回调接口
if (mListener != null) {
mListener.onDragStateChanged(mCurrentState);
}

} else {
//如果拖动的距离不够左侧view的一半,就应该关闭mainView
if (left < mLeftWidth / 2) {


//中间的view关闭
mDragHelper.smoothSlideViewTo(mMainView,
0,
0);
} else {

//中间的View打开
mDragHelper.smoothSlideViewTo(mMainView,
(int) (mLeftWidth + 0.5f),
0);
}
}
} else {
Log.d("DragLayout",
"left : " + left + " width : " + mRightWidth);
if (-xvel > mMinVelocity && canOpenRight) {
//打开右侧
mDragHelper.smoothSlideViewTo(mMainView,
(int) (-mRightWidth - 0.5f),
0);


//改变状态 ####
mCurrentState = STATE_FLING;

// 回调接口
if (mListener != null) {
mListener.onDragStateChanged(mCurrentState);
}
} else {
if (-left > mRightWidth / 2) {
//如果拖动的距离大于 右侧的一半,打开mainView
mDragHelper.smoothSlideViewTo(mMainView,
(int) (-mRightWidth - 0.5f),
0);
} else {
//关闭
mDragHelper.smoothSlideViewTo(mMainView,
0,
0);
}
}
}
invalidate();

}

}
}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
抱歉,我无法编写代码,但我可以向您提供知乎登录界面的 HTML 和 CSS 代码示例。请参考以下代码: HTML 代码: ``` <!DOCTYPE html> <html> <head> <title>知乎登录</title> <link rel="stylesheet" type="text/css" href="style.css"> </head> <body> <div class="login-box"> <h1>知乎登录</h1> <form> <label for="username">用户名</label> <input type="text" id="username" name="username" placeholder="请输入用户名"> <label for="password">密码</label> <input type="password" id="password" name="password" placeholder="请输入密码"> <label class="checkbox-container"> <input type="checkbox" checked="checked"> <span class="checkmark"></span> 记住我 </label> <button type="submit">登录</button> <div class="other-options"> <a href="">手机验证码登录</a> | <a href="">忘记密码</a> </div> </form> </div> </body> </html> ``` CSS 代码: ``` * { box-sizing: border-box; margin: 0; padding: 0; } body { background-color: #f2f2f2; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; } .login-box { background-color: #fff; border-radius: 8px; box-shadow: 0px 15px 20px rgba(0, 0, 0, 0.1); margin: 100px auto; max-width: 500px; padding: 30px; } h1 { color: #333; font-size: 28px; margin-bottom: 30px; text-align: center; } form { display: flex; flex-direction: column; } label { color: #666; display: block; font-size: 16px; margin-bottom: 6px; } input[type="text"], input[type="password"] { border: none; border-radius: 4px; box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.1); font-size: 16px; margin-bottom: 20px; padding: 10px; } input[type="text"]:focus, input[type="password"]:focus { box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.2); outline: none; } .checkbox-container { display: inline-block; position: relative; padding-left: 30px; margin-bottom: 20px; cursor: pointer; font-size: 16px; user-select: none; } .checkbox-container input { position: absolute; opacity: 0; cursor: pointer; } .checkmark { position: absolute; top: 0; left: 0; height: 20px; width: 20px; background-color: #eee; } .checkbox-container:hover input ~ .checkmark { background-color: #ccc; } .checkbox-container input:checked ~ .checkmark { background-color: #2196F3; } .checkmark:after { content: ""; position: absolute; display: none; } .checkbox-container input:checked ~ .checkmark:after { display: block; } .checkbox-container .checkmark:after { left: 7px; top: 3px; width: 5px; height: 10px; border: solid white; border-width: 0 3px 3px 0; transform: rotate(45deg); } button[type="submit"] { background-color: #0084ff; border: none; border-radius: 4px; color: #fff; cursor: pointer; font-size: 16px; margin-top: 20px; padding: 10px; transition: background-color 0.2s ease; } button[type="submit"]:hover { background-color: #0071e6; } .other-options { color: #999; font-size: 14px; margin-top: 20px; text-align: center; } .other-options a { color: #999; text-decoration: none; } .other-options a:hover { color: #666; } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值