自定义View实现手机qq5.X的抽屉特效和聊天界面联系人左滑功能
来源:互联网 发布:数据库测试方法 编辑:程序博客网 时间:2024/05/29 18:59
主要是ViewDragHelper的使用(ViewDragHelper: Google2013年IO大会提出的, 解决界面控件拖拽移动问题),就是把两个View叠加在一起拖动顶层View时加上一些伴随动画可实现抽屉特效。
效果图:
抽屉特效:
首先是抽屉特效的自定义View,为了方便使用就继承FrameLayout(继承FrameLayout的原因就是省事,因为FrameLayout自动测量和摆放位置了,而且FrameLayout是上下层叠关系,没有位置相对的关系,正是这样方便了使用)
自定义View(抽屉)代码:
public class DragLayout extends FrameLayout { private String TAG = DragLayout.class.getName(); private ViewDragHelper mDragHelper; private ViewGroup mLeftContent; private ViewGroup mMainContent; private int mWidth; private int mHeight; private int mRange; public DragStatus getStatus() { return mStatus; } public void setStatus(DragStatus mStatus) { this.mStatus = mStatus; } public DragStatus mStatus = DragStatus.Close; public OnDragStatusChangeListener mDragChange; public DragLayout(Context context) { this(context, null); } public DragLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public DragLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); // a.初始化操作 //参数2 最小敏感范围, 值越小, 越敏感 mDragHelper = ViewDragHelper.create(this, 0.5f, mCallback); } public enum DragStatus { Close, Open, Draging; } public interface OnDragStatusChangeListener { void onOpen(); void onClose(); void onDraging(float percent); } public void setOnDragStatusChangeListener(OnDragStatusChangeListener mDragChange) { this.mDragChange = mDragChange; } ViewDragHelper.Callback mCallback = new ViewDragHelper.Callback() { // 根据返回结果决定是否可以拖拽 @Override public boolean tryCaptureView(View child, int pointerId) { return true; } // 2. 根据建议值 修正将要移动到的(横向)位置 @Override public int clampViewPositionHorizontal(View child, int left, int dx) { if (child == mMainContent) { left = fixLeft(left); } return left; }// 返回拖拽的范围, 不对拖拽进行的限制. 只是动画执行速度 @Override public int getViewHorizontalDragRange(View child) { return mRange; } // View已经发生了位置的改变,更新状态, 伴随动画, 重绘界面 @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { super.onViewPositionChanged(changedView, left, top, dx, dy); int newLeft = left; if (changedView == mLeftContent) { newLeft = fixLeft(mMainContent.getLeft() + dx); mLeftContent.layout(0, 0, mWidth, mHeight); mMainContent.layout(newLeft, 0, mWidth + newLeft, mHeight); } // 更新状态,执行伴随动画 dispatchDragEvent(newLeft); } // 4. 当View被释放的时候, 执行平滑动画 @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { super.onViewReleased(releasedChild, xvel, yvel); // 判断执行 关闭/开启 Log.d(TAG, "onViewReleased:" + "xvel" + xvel); if (xvel == 0 && mMainContent.getLeft() > mRange / 2.0f) { open(); } else if (xvel > 0) { open(); } else { close(); } } }; private int fixLeft(int left) { if (left < 0) { return 0; } else if (left > mRange) { return mRange; } return left; } protected void dispatchDragEvent(int newLeft) { //主界面从开始到结束的百分比 float percent = newLeft * 1.0f / mRange; if (mDragChange != null) { mDragChange.onDraging(percent); } // 更新状态, 执行回调 DragStatus preStetus = mStatus; mStatus = upDateStetus(percent); if (mStatus != preStetus) { if (mStatus == DragStatus.Close) { if (mDragChange != null) { mDragChange.onClose(); } } else if (mStatus == DragStatus.Open) { if (mDragChange != null) { mDragChange.onOpen(); } } } //伴随动画: animViews(percent); } private DragStatus upDateStetus(float percent) { if (percent == 0f) { return DragStatus.Close; } else if (percent == 1.0f) { return DragStatus.Open; } return DragStatus.Draging; } private void animViews(float percent) { // 缩放动画 mLeftContent.setScaleX(UiUtils.evaluate(percent, 0.5, 1.0f)); mLeftContent.setScaleY(UiUtils.evaluate(percent, 0.5, 1.0f)); // 平移动画: mLeftContent.setTranslationX(UiUtils.evaluate(percent, -mWidth / 2.0f, 0f)); // 透明度: mLeftContent.setAlpha(UiUtils.evaluate(percent, 0.5, 1.0f)); // 主界面: 缩放动画 // 1.0f -> 0.8f mMainContent.setScaleX(UiUtils.evaluate(percent, 1.0, 0.8f)); mMainContent.setScaleY(UiUtils.evaluate(percent, 1.0, 0.8f)); // 背景动画: 亮度变化 getBackground().setColorFilter((Integer) UiUtils.evaluateColor(percent, Color.BLACK, Color.TRANSPARENT), PorterDuff.Mode.SRC_OVER); } // 持续平滑动画 @Override public void computeScroll() { super.computeScroll(); if (mDragHelper.continueSettling(true)) { // 如果返回true, 动画还需要继续执行 ViewCompat.postInvalidateOnAnimation(this); } } public void open() { open(true); } public void open(boolean isSmoot) { int fianlLeft = mRange; // 触发一个平滑动画 if (isSmoot) { if (mDragHelper.smoothSlideViewTo(mMainContent, fianlLeft, 0)) { // 返回true代表还没有移动到指定位置, 需要刷新界面. // 参数传this(child所在的ViewGroup) ViewCompat.postInvalidateOnAnimation(this); } } else { mMainContent.layout(fianlLeft, 0, fianlLeft + mWidth, mHeight); } } public void close() { close(true); } public void close(boolean isSmoot) { int fianlLeft = 0; if (isSmoot) { if (mDragHelper.smoothSlideViewTo(mMainContent, fianlLeft, 0)) { ViewCompat.postInvalidateOnAnimation(this); } } else { mMainContent.layout(fianlLeft, 0, fianlLeft + mWidth, mHeight); } } // 传递触摸事件 @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (SwipeLayout.mStatus == SwipeLayout.SwipeStatus.Close) { return mDragHelper.shouldInterceptTouchEvent(ev); } return false; } @Override public boolean onTouchEvent(MotionEvent event) { try { mDragHelper.processTouchEvent(event); } catch (Exception e) { e.printStackTrace(); } return true; } //获取当前View的子View @Override protected void onFinishInflate() { super.onFinishInflate(); mLeftContent = (ViewGroup) getChildAt(0); mMainContent = (ViewGroup) getChildAt(1); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); // 当尺寸有变化的时候调用 mWidth = getMeasuredWidth(); mHeight = getMeasuredHeight(); // 向右滑的范围 mRange = (int) (mWidth * 0.6f); }}
MainActivity代码:
public class MainActivity extends AppCompatActivity { private String TAG = MainActivity.class.getName(); private MyLinearLayout mLayout; private DragLayout dl_layout; private ListView mMainList; private ImageView mHeaderImage; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); setStatusBar(); dl_layout = (DragLayout) findViewById(R.id.dl_layout); mMainList = (ListView) findViewById(R.id.lv_main); mHeaderImage = (ImageView) findViewById(R.id.iv_header); mLayout = (MyLinearLayout) findViewById(R.id.rl_main); mLayout.setmDragLayout(dl_layout); //打开或关闭抽屉的回调 mHeaderImage.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dl_layout.open(); } }); dl_layout.setOnDragStatusChangeListener(new DragLayout.OnDragStatusChangeListener() { @Override public void onOpen() { UiUtils.showToast(MainActivity.this,"onOpen"); } @Override public void onClose() { UiUtils.showToast(MainActivity.this, "onClose"); ObjectAnimator mAnim = ObjectAnimator.ofFloat(mHeaderImage, "translationX", 25.0f); mAnim.setInterpolator(new CycleInterpolator(3)); mAnim.setDuration(500); mAnim.start(); } @Override public void onDraging(float percent) { // 更新图标的透明度 // 1.0 -> 0.0 mHeaderImage.setAlpha(1 - percent); } }); mMainList.setAdapter(new MyAdapter(getApplicationContext())); } protected void setStatusBar() { StatusBarUtil.setTranslucentForImageView(this, 60, mLayout); }}
聊天界面联系人左滑功能:
自定义View代码:
public class SwipeLayout extends FrameLayout { private View operateView; private View itmeView; private int mHeight; private int mWidth; private int mRange; private ViewDragHelper mDragHelper; private OnSwipeLayoutStateChangeListener onStateChangeListener; public static SwipeStatus mStatus = SwipeStatus.Close; public SwipeLayout(Context context) { this(context, null); } public SwipeLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SwipeLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mDragHelper = ViewDragHelper.create(this, 1f, mCallback); } ViewDragHelper.Callback mCallback = new ViewDragHelper.Callback() { @Override public boolean tryCaptureView(View child, int pointerId) { return true; } @Override public int clampViewPositionHorizontal(View child, int left, int dx) { // 限定移动范围 if (child == itmeView) { if (left > 0) { return 0; } else if (left < -mRange) { return -mRange; } } else if (child == operateView) { if (left > mWidth) { return mWidth; } else if (left < mWidth - mRange) { return mWidth - mRange; } } return left; } @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { super.onViewPositionChanged(changedView, left, top, dx, dy); if(changedView == itmeView){ operateView.offsetLeftAndRight(dx); }else if (changedView == operateView) { itmeView.offsetLeftAndRight(dx); } dispatchSwipeEvent(); } @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { super.onViewReleased(releasedChild, xvel, yvel); if (xvel == 0 && itmeView.getLeft() < -mRange / 2.0f) { open(); } else if (xvel < 0) { open(); } else { close(); } } }; public enum SwipeStatus { Close, Open, Draging; } public interface OnSwipeLayoutStateChangeListener { void onClose(SwipeLayout mSwipeLayout); void onOpen(SwipeLayout mSwipeLayout); void onDraging(SwipeLayout mSwipeLayout); // 准备关闭 void onStartClose(SwipeLayout mSwipeLayout); // 准备开启 void onStartOpen(SwipeLayout mSwipeLayout); } public void setOnSwipeLayoutStateChangeListener(OnSwipeLayoutStateChangeListener onStateChangeListener) { this.onStateChangeListener = onStateChangeListener; } private void dispatchSwipeEvent() { if (onStateChangeListener != null) { onStateChangeListener.onDraging(this); } SwipeStatus preStatus = mStatus; mStatus = upDateStatus(); if (preStatus != mStatus && onStateChangeListener != null) { if (mStatus == SwipeStatus.Close) { onStateChangeListener.onClose(this); } else if (mStatus == SwipeStatus.Open) { onStateChangeListener.onOpen(this); } else if (mStatus == SwipeStatus.Draging) { if(preStatus == SwipeStatus.Close){ onStateChangeListener.onStartOpen(this); }else if (preStatus == SwipeStatus.Open) { onStateChangeListener.onStartClose(this); } } } } private SwipeStatus upDateStatus() { int left = itmeView.getLeft(); if (left == 0) { return SwipeStatus.Close; } else if (left == -mRange) { return SwipeStatus.Open; } return SwipeStatus.Draging; } @Override public void computeScroll() { super.computeScroll(); if (mDragHelper.continueSettling(true)) { ViewCompat.postInvalidateOnAnimation(this); } } public void open() { open(true); } private void open(boolean isSmooth) { int fianlLeft = -mRange; if (isSmooth) { if (mDragHelper.smoothSlideViewTo(itmeView, fianlLeft, 0)) { ViewCompat.postInvalidateOnAnimation(this); } } else { layoutContent(true); } } public void close() { close(true); } private void close(boolean isSmooth) { int fianlLeft = 0; if (isSmooth) { if (mDragHelper.smoothSlideViewTo(itmeView, fianlLeft, 0)) { ViewCompat.postInvalidateOnAnimation(this); } } else { layoutContent(false); } } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); layoutContent(false); } private void layoutContent(boolean isOpen) { Rect itemRect = computeFrontViewRect(isOpen); itmeView.layout(itemRect.left, itemRect.top, itemRect.right, itemRect.bottom); Rect OperateRect = computeOperateViewFront(itemRect); operateView.layout(OperateRect.left, OperateRect.top, OperateRect.right, OperateRect.bottom); // 调整顺序, 把itemRect前置 bringChildToFront(itmeView); } private Rect computeOperateViewFront(Rect itemRect) { int left = itemRect.right; return new Rect(left, 0, left + mRange, mHeight); } private Rect computeFrontViewRect(boolean isOpen) { int left = 0; if (isOpen) { left = -mRange; } return new Rect(left, 0, mWidth + left, mHeight); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return mDragHelper.shouldInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { try { mDragHelper.processTouchEvent(event); } catch (Exception e) { e.printStackTrace(); } return true; } @Override protected void onFinishInflate() { super.onFinishInflate(); operateView = getChildAt(0); itmeView = getChildAt(1); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mHeight = itmeView.getMeasuredHeight(); mWidth = itmeView.getMeasuredWidth(); mRange = operateView.getMeasuredWidth(); }}
listView的Adaper:
public class MyAdapter extends BaseAdapter { protected static final String TAG = "TAG"; public MyAdapter(Context context) { super(); this.context = context; opendItems = new ArrayList<>(); } private Context context; private ArrayList<SwipeLayout> opendItems; @Override public int getCount() { return Cheeses.NAMES.length; } @Override public Object getItem(int position) { return NAMES[position]; } @Override public long getItemId(int position) { // TODO Auto-generated method stub return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { View view = convertView; if(convertView == null){ view = View.inflate(context, R.layout.item_list, null); } ViewHolder mHolder = ViewHolder.getHolder(view); SwipeLayout sl = (SwipeLayout)view; sl.setOnSwipeLayoutStateChangeListener(new SwipeLayout.OnSwipeLayoutStateChangeListener() { @Override public void onStartOpen(SwipeLayout mSwipeLayout) { Log.d(TAG, "onStartOpen"); } @Override public void onStartClose(SwipeLayout mSwipeLayout) { Log.d(TAG, "onStartClose"); } @Override public void onOpen(SwipeLayout mSwipeLayout) { Log.d(TAG, "onOpen"); } @Override public void onDraging(SwipeLayout mSwipeLayout) { } @Override public void onClose(SwipeLayout mSwipeLayout) { Log.d(TAG, "onClose"); } }); return view; } static class ViewHolder { TextView tv_call; TextView tv_del; public static ViewHolder getHolder(View view) { Object tag = view.getTag(); if(tag == null){ ViewHolder viewHolder = new ViewHolder(); viewHolder.tv_call = (TextView)view.findViewById(R.id.tv_call); viewHolder.tv_del = (TextView)view.findViewById(R.id.tv_del); tag = viewHolder; view.setTag(tag); } return (ViewHolder)tag; } }}
0 0
- 自定义View实现手机qq5.X的抽屉特效和聊天界面联系人左滑功能
- 自定义view实现联系人界面
- qq5.0侧滑抽屉式菜单的实现
- 自定义View以及QQ5.0侧滑菜单实现
- android自定义实现抽屉SlidingDrawer的功能
- android自定义实现抽屉SlidingDrawer的功能
- 抽屉组件SlidingDrawer的实现,自定义View的配置和实用
- 抽屉功能的实现
- 手机QQ5.0红点拖拽消除的实现
- iOS开发中应用内跳转到QQ聊天界面和指定的联系人聊天
- iOS左滑菜单类似抽屉的实现
- 自定义View 实现抽屉式侧滑菜单 MySlidingMenu
- 自定义View实现视差特效
- 自定义View实现视差特效
- 实现登录界面的自定义view
- 阿里云旺自定义消息和首次打开聊天界面自动发送消息的实现
- 自定义View实现listView的左划删除
- 自定义View ----QQ5.0左边侧滑 + 动画
- linux安装Django1.9
- wait notify 锁机制
- 斐波那契数列-递归初步
- 洛谷 P1202 [USACO1.1]黑色星期五Friday the Thirteenth
- 使用 Docker 搭建 Java Web 运行环境
- 自定义View实现手机qq5.X的抽屉特效和聊天界面联系人左滑功能
- 20161106学习心得
- linux UDP网络编程
- 1057. 数零壹(20)
- Spring各jar包的作用(转载)
- 122. Best Time to Buy and Sell Stock II 类别:贪心算法 难度:medium
- Form表单验证之使用session做交互
- Spring Boot 2.0.0参考手册_中文版_Part III_14-18
- 响应式侧边导航栏(刚入门的菜鸟,勿喷...)