您的位置:首页 > 其它

彷QQ滑动删除效果—侧滑删除(置顶、标记、未读标记功能)

2017-01-12 14:29 573 查看


1:功能介绍

QQ条目删除,主要实现的主条目和删除条目的 摆放,使用的类是ViewDragHelper

2、布局页面



1.main的xml文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="itheima.com.qqitemdelete.MainActivity">
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="#ccc"
android:dividerHeight="2dp"
/>
</RelativeLayout>




2.delete的删除条目——layout_delete
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="60dp"
android:orientation="horizontal" >
<TextView android:layout_width="70dp"
android:layout_height="match_parent"
android:textSize="18sp"
android:textColor="#ffffff"
android:gravity="center"
android:background="#D4D4D7"
android:text="置顶"/>
<TextView android:layout_width="120dp"
android:layout_height="match_parent"
android:textSize="18sp"
android:textColor="#ffffff"
android:gravity="center"
android:background="#FF9901"
android:text="标记未读"/>

<TextView android:layout_width="70dp"
android:layout_height="match_parent"
android:textSize="18sp"
android:textColor="#ffffff"
android:id="@+id/tv_delete"
android:gravity="center"
android:background="#FF3A30"
android:text="删除"/>
</LinearLayout>




3.主条目的布局文件——layout_content
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="60dp"
android:paddingLeft="15dp"
android:background="#fff"
android:gravity="center_vertical"
android:orientation="horizontal" >

<ImageView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@mipmap/head_1"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#99000000"
android:id="@+id/tv_name"
android:layout_marginLeft="10dp"
android:textSize="20sp"
android:text="名称"/>
</LinearLayout>
</LinearLayout>


4.adapter的item的布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<itheima.com.qqitemdelete.SwipeLayout
android:id="@+id/swipeLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!--content的布局-->
<include layout="@layout/layout_content"/>
<!--delete的布局-->
<include layout="@layout/layout_delete"/>
</itheima.com.qqitemdelete.SwipeLayout>


3、先写一个类SwipeLayout,继承自FrameLayout

public class SwipeLayout extends FrameLayout{
private ViewDragHelper viewDragHelper;
private View content;
private View delete;
private long mDownTime;
private boolean isOpen = true;
//实现三个构造函数
public SwipeLayout(Context context) {
this(context, null);
}
public SwipeLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SwipeLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
viewDragHelper = ViewDragHelper.create(this, callback);
}
//在主页xml页面读取界面时候获取子View
@Override
protected void onFinishInflate() {
super.onFinishInflate();
content = getChildAt(0);
delete = getChildAt(1);
}


4、在onLayout方法中对contentView和deleteView进行摆放

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
//摆放内容控件
content.layout(0,0,content.getMeasuredWidth(),content.getMeasuredHeight());
//获取内容控件的宽
int L= content.getRight();
delete.layout(L,0,L+delete.getMeasuredWidth(),delete.getMeasuredHeight());
}
//把触摸事件传给ViewDragHelper处理,经常两种方法
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean b = viewDragHelper.shouldInterceptTouchEvent(ev);
return b;
}
6.1、解决第一个bug的思路是:在onTouchEvent方法判断当前手指移动的方向到底是偏向于水平还是偏向于垂直,如果是偏向于水平那么就认为用户是希望滑动item,那么则请求父View不要去拦截事件
float downX,downY;
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//获取按下的坐标
downX = event.getX();
downY = event.getY();
//获取按下的时间
mDownTime = System.currentTimeMillis();
break;
case MotionEvent.ACTION_MOVE:
float moveX = event.getX();
float moveY = event.getY();
//计算移动距离
float dx = moveX-downX;
float dy = moveY-downY;
//判断到底偏向哪个方向
if(Math.abs(dx)>Math.abs(dy)){
//说明是偏向水平方向,那么就认为用户想滑动条目,此时应该让listview不要拦截
requestDisallowInterceptTouchEvent(true);
}
break;
6.2、由于我们重写onTouchEvent处理了事件,导致ListView的条目点击无效了,此时最有效最简单的做法是自己去判断触摸事件实现点击行为,思路是:记录按下的坐标和时间,在抬起的时候计算整个按下抬起的时间和距离,如果时间小于400毫秒,并且距离小于touchSlop,则认为是点击事件,事实上系统也是这样实现点击事件的:
case MotionEvent.ACTION_UP:
//1.计算按下抬起的时间
long duration = System.currentTimeMillis()-mDownTime;
//2.计算按下抬起的距离
float deltaX = event.getX() - downX;
float deltaY = event.getY() - downY;
//deltaX的平方+deltaY的平方和进行开放,获得点与点的直线距离
float distance = (float) Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2));
//3. 配置,如果duration小于500,并且distance小于8px
ViewConfiguration viewConfiguration = ViewConfiguration.get(getContext());
if (duration < getLongPressTimeout() && distance < viewConfiguration.getScaledTouchSlop()) {//8dp
//作用就是执行OnClickListener的onClick方法
if(isOpen){
openDeleteMenu();
isOpen=false;
}else{
closeDeleteMenu();
isOpen=true;
}
}
break;
}
viewDragHelper.processTouchEvent(event);
return true;
}


5、利用ViewDragHelper实现让SwipeLayout的2个子View进行拖拽移动,主要是Callback的实现

ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {
//1.捕获子View,谁为true,下面的方法就执行谁
@Override
public boolean tryCaptureView(View child, int pointerId) {
return true;
}
//2.鸡肋的方法,大于1就行
@Override
public int getViewHorizontalDragRange(View child) {
return 1;
}
//修改水平方向滑动的距离
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
//限制content
if(child==content){
if(left>0){
left=0;
}else if(left<-delete.getMeasuredWidth()){
left=-delete.getMeasuredWidth();
}
}else if(child==delete){
//限制delete
if(left>content.getMeasuredWidth()){
left = content.getMeasuredWidth();
}else if(left<(content.getMeasuredWidth()-delete.getMeasuredWidth())){
left = (content.getMeasuredWidth()-delete.getMeasuredWidth());
}
}
return left;
}
//经常执行一些伴随动画
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
//如果移动的是content,那么让delete伴随移动
if(changedView==content){
ViewCompat.offsetLeftAndRight(delete,dx);
}else if(changedView==delete){
//让content进行伴随移动
ViewCompat.offsetLeftAndRight(content,dx);
}
//回调接口的方法
if(listener!=null){
if(content.getLeft()==0){
listener.onClose(SwipeLayout.this);
}else if(content.getLeft()==-delete.getMeasuredWidth()){
listener.onOpen(SwipeLayout.this);
}
}
}
//手指离开执行的方法
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
if(content.getLeft()>-delete.getMeasuredWidth()/2){
//关闭
closeDeleteMenu();
isOpen=true;
}else{
//打开
openDeleteMenu();
isOpen=false;
}
}
};
/**
* 打开页面
*/
public void openDeleteMenu() {
viewDragHelper.smoothSlideViewTo(content,-delete.getMeasuredWidth(),0);
ViewCompat.postInvalidateOnAnimation(this);
}
/**
* 关闭页面
*/
public void closeDeleteMenu() {
//平滑至
viewDragHelper.smoothSlideViewTo(content,0,0);
//动画更新
ViewCompat.postInvalidateOnAnimation(this);
}
//如果动画还没有结束,继续刷新  compute计算
@Override
public void computeScroll() {
super.computeScroll();
if(viewDragHelper.continueSettling(true)){
ViewCompat.postInvalidateOnAnimation(this);
}
}
//创建回调,把SwipeLayout返回Activity中
private OnSwipeListener listener;
public void setOnSwipeListener(OnSwipeListener listener){
this.listener=listener;
}
public interface OnSwipeListener{
void onOpen(SwipeLayout currentLayout);
void onClose(SwipeLayout currentLayout);
}
}


6、然后将实现好的可滑动的SwipeLayout放入ListView的adapter的布局中,此时我们遇到2个bug:

1、当我们左右拖动item滑动时,再上下滑动会遇到事件被ListView捕获并处理,导致我们无法继续控制item的滑动;
2、我们可以同时滑动出多个item,而需求是只能允许一个item是打开的;


public class MainActivity extends AppCompatActivity {
@BindView(R.id.list_view)
ListView listView;
boolean isOpen=true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
//设置适配器
listView.setAdapter(new MyAdapter());
//监听listView的滚动,实现功能:当条目滑动的时候,菜单关闭
listView.setOnScrollListener(new AbsListView.OnScrollListener(){
@Override
public void onScrollStateChanged(AbsListView absListView, int i) {
if(openedLayout!=null){
openedLayout.closeDeleteMenu();
}
}
@Override
public void onScroll(AbsListView absListView, int i, int i1, int i2) {
}
});
}
//用来记录打开的SwipeLayout
SwipeLayout openedLayout;
class MyAdapter extends BaseAdapter implements SwipeLayout.OnSwipeListener {
@Override
public int getCount() {
return Constant.NAMES.length;
}
@Override
public Object getItem(int i) {
return null;
}
@Override
public long getItemId(int i) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
MyHolder myHolder = null;
//简单的复用缓存
if (convertView == null) {
convertView = View.inflate(parent.getContext(), R.layout.adapter_list_item, null);
myHolder = new MyHolder(convertView);
convertView.setTag(myHolder);
} else {
myHolder = (MyHolder) convertView.getTag();
}
//绑定数据
myHolder.tvName.setText(Constant.NAMES[position]);
//设置监听器
myHolder.swipeLayout.setOnSwipeListener(this);
return convertView;
}
@Override
public void onOpen(SwipeLayout currentLayout) {
//判断SwipeLayout不为空并且不等于当前条目,就关闭其他条目
if (openedLayout!= null && openedLayout!=currentLayout) {
openedLayout.closeDeleteMenu();
}
openedLayout=currentLayout;
}
@Override
public void onClose(SwipeLayout currentLayout) {
//如果传来的条目是自己,就把SwipeLayout置为空
if (openedLayout == currentLayout) {
openedLayout = null;
}
}
}
static class MyHolder {
@BindView(R.id.tv_name)
TextView tvName;
@BindView(R.id.tv_delete)
TextView tvDelete;
@BindView(R.id.swipeLayout)
SwipeLayout swipeLayout;
MyHolder(View view) {
ButterKnife.bind(this, view);
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  QQ条目删除