Android 仿QQListView侧滑删除

先上效果图

分析下整个效果如何实现
1.每一个item的界面如何去控制,做出类似动画一样的效果
2.对onTouchEvent事件的处理

想下,是不是只要每一个item处理好,做出这种效果之后,那么剩下的问题就相对简单多了

那好首先我们来看下,如何来实现item的侧滑效果

我们的每一个itme的布局都是由两部分组成一个是主view一个是侧滑view,所以这两个view我们需要在构造方法中传入,并且,我们不需要在xml文件中添加,所以只需要实现一参的构造方法就行了

package wkk.demo15;

import android.support.v4.widget.ScrollerCompat;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.LinearLayout;

/**
 * Created by wkk on 2016/6/12.
 */
public class SlidingMenuViewGroup extends LinearLayout {

    //实现滚动操作
    private ScrollerCompat scrollerCompat;
    //按下的x点坐标
    private int xDown;
    //主布局以及菜单布局
    private View contentView;
    private View menuView;
    //菜单是否打开
    private boolean isOpen;

    //1构造方法,这里传入两个view,一个是主布局,一个是侧面的布局
    public SlidingMenuViewGroup(View contentView, View menuView) {
        super(contentView.getContext());
        this.contentView = contentView;
        this.menuView = menuView;
        init();
    }

    private void init() {
        setLayoutParams(new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
        scrollerCompat = ScrollerCompat.create(getContext());
        contentView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
        menuView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
        addView(contentView);
        addView(menuView);
    }


}

以上代码并不难理解,不过是定义一些所必要的参数,并且初始化一些数据,这里就不多做赘述

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //设置menuView的测量模式
        //此处宽必须设置为UNSPECIFIED 否则将不会绘制
        menuView.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
                , MeasureSpec.makeMeasureSpec(menuView.getMeasuredHeight(), MeasureSpec.EXACTLY));
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        //指定contentview和menuView的位置
        contentView.layout(0, 0, getMeasuredWidth(), getMeasuredHeight());
        menuView.layout(getMeasuredWidth(), 0, getMeasuredWidth() + menuView.getMeasuredWidth(), getMeasuredHeight());
    }

MainActivity代码:

package wkk.demo15;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.ViewGroup;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        setView();
    }

    private void setView() {
        TextView textView1 = new TextView(this);
        textView1.setText("content");
        textView1.setGravity(Gravity.CENTER);
        textView1.setBackgroundColor(0xffff123f);
        textView1.setHeight(dp2px(70));

        TextView textView2 = new TextView(this);
        textView2.setGravity(Gravity.CENTER);
        textView2.setWidth(dp2px(70));
        textView2.setHeight(dp2px(70));
        textView2.setText("menu");
        textView2.setBackgroundColor(0xbbbbbbbb);

        SlidingMenuViewGroup sildingMenuViewGroup = new SlidingMenuViewGroup(textView1, textView2);

        addContentView(sildingMenuViewGroup, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dp2px(70)));
    }

    //尺寸转化
    private int dp2px(int dp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
                getResources().getDisplayMetrics());
    }
}

此时效果如上图,如果用鼠标点击,并没有任何作用,当然,这些都是准备工作,接下来进行关键的onTouchEvent的事件处理


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        onEvent(event);
        return true;
    }


    public void onEvent(MotionEvent event) {

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                //记录坐标
                xDown = (int) event.getX();
                break;
            case MotionEvent.ACTION_MOVE:
                //滑动的距离
                int distance = (int) (xDown - event.getX());
                if (isOpen) {
                    //此处需要弄清楚getWidth和getMeasuredWidth的区别
                    //getWidth的到的宽度是显示在屏幕中的部分
                    //getMeasuredWidth,如果有屏幕之外的部分即,未显示的部分,那么getMeasuredWidth的到宽度是
                    //实际宽度即显示在屏幕中的宽度和屏幕之外的宽度,如果在屏幕中已近完全显示,那么得到的部分即和getWidth相同
                    distance += menuView.getWidth();
                }
                sliding(distance);
                break;
            case MotionEvent.ACTION_UP:


                break;
        }
    }

    //滑动
    private void sliding(int distance) {
        //控制最大最小距离
        if (distance < 0) {
            distance = 0;
        }
        if (distance > menuView.getMeasuredWidth()) {
            distance = menuView.getMeasuredWidth();
        }

        //重新设置位置
        contentView.layout(-distance, 0, getWidth() - distance, getHeight());
        menuView.layout(getWidth() - distance, 0, getWidth() + menuView.getMeasuredWidth() - distance, getHeight());
    }

效果如图:

可以看到,我们所要的效果已经有了雏形,但是,滑动并不流畅,当然,或许你已经注意到我们在上边定义的ScrollerCompat,并没有用到,那么接下来,对up事件进行处理的时候就会用到ScrollerCompat

            case MotionEvent.ACTION_UP:
                //判断滑动距离是否大于menuview的一半
                // 如果大于就打开,否则就关闭菜单
                if ((xDown - event.getX()) > (menuView.getMeasuredWidth() / 2)) {
                    openMenu();
                } else {
                    closeMenu();
                }
                break;

    public void closeMenu() {
        isOpen = false;
        //参数:x轴起始位置,y轴起始位置,x轴偏移量,y轴偏移量,时间
        //偏移量为正时,向右移动,偏移量为负时,向右移动
        scrollerCompat.startScroll(contentView.getLeft(), 0, -contentView.getLeft(), 0, 300);
        //通知重绘
        postInvalidate();
    }

    public void openMenu() {
        isOpen = true;
        //再调用这个方法后view就会调用computeScroll方法,所以要对其进行重写
        scrollerCompat.startScroll(contentView.getLeft(), 0, -(menuView.getWidth() + contentView.getLeft()), 0, 300);
        postInvalidate();
    }

    @Override
    public void computeScroll() {
        super.computeScroll();
        //判断是否完成滑动
        if (scrollerCompat.computeScrollOffset()) {
            //scrollerCompat.getCurrX() 获取当前x轴的滑动位置
            sliding(Math.abs(scrollerCompat.getCurrX()));
            postInvalidate();
        }
    }

效果如下图

可以看到效果已经是我们想要的效果了,而且滑动也很流畅了,这里在添加下对外部提供的方法:


    //对外部提供方法
    public boolean isOpen() {
        return isOpen;
    }


    public View getMenuView(){
        return menuView;
    }

好,至此,item已经基本完成了,可是,这当然不是最终效果,对onTouchEvent事件,我们当然不可以全部拦截,还有如果你设置了contentview的点击事件,你会发现,滑动就失效了,所以,我门还要自定义listview,如果你对listview的onTouchEvent事件不去处理的话,直接使用原生,就会发现很多奇怪的现象,并不是我们想要的效果,如果有兴趣,可以去试试看呐

首先将SlidingMenuViewGroup的onTouchEvent事件还原


    @Override
    public boolean onTouchEvent(MotionEvent event) {

        return super.onTouchEvent(event);
    }

重写ListView,定义相关变量

package wkk.demo15;

import android.content.Context;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.ListView;

/**
 * Created by wkk on 2016/6/12.
 */
public class SlidingMenuListView extends ListView {

    //滑动最小值
    private int MIN_X;
    private int MIN_Y;

    //按下点坐标
    private int xDown;
    private int yDown;

    //当前的子view
    private SlidingMenuViewGroup sildingMenu;

    //手指所在item位置
    private int position;
    private int oldposition;

    //移动状态
    private int touchState;
    private int TOUCK_STATE_NULL = 0;
    private int TOUCH_STATE_X = 1;
    private int TOUCH_STATE_Y = 2;

    //移动距离
    private int dx;
    private int dy;

    private boolean is;

    public SlidingMenuListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        MIN_X = dp2px(3);
        MIN_Y = dp2px(5);
    }

    //尺寸转化
    private int dp2px(int dp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
                getResources().getDisplayMetrics());
    }


}

最关键的onTouchEvent事件处理:

  @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {

            case MotionEvent.ACTION_DOWN:
                //首先记录坐标
                xDown = (int) ev.getX();
                yDown = (int) ev.getY();
                oldposition = position;
                //状态设置为null
                touchState = TOUCK_STATE_NULL;
                is = false;
                position = pointToPosition(xDown, yDown);
                //如果两次按下时的位置相同,并且菜单是打开的
                if (oldposition == position && sildingMenu != null && sildingMenu.isOpen()) {
                    touchState = TOUCH_STATE_X;
                    sildingMenu.onEvent(ev);
                    return true;
                }
                //如果两次按下的位置不同,并且菜单是打开的
                if (sildingMenu != null && sildingMenu.isOpen()) {
                    //直接关闭菜单
                    sildingMenu.closeMenu();
                    sildingMenu = null;
                    //这种情况下,不去响应up事件
                    is = true;
                    return true;
                }
                //得到view
                View view = getChildAt(position - getFirstVisiblePosition());
                //判断view是否是SildingMenuViewGroup的对象
                if (view instanceof SlidingMenuViewGroup) {
                    sildingMenu = (SlidingMenuViewGroup) view;
                }
                if (sildingMenu != null) {
                    sildingMenu.onEvent(ev);
                }
                break;

            case MotionEvent.ACTION_MOVE:
                //取得xy轴的偏移量
                dx = (int) (xDown - ev.getX());
                dy = (int) Math.abs(ev.getY() - yDown);
                if (touchState == TOUCK_STATE_NULL) {
                    //判断 设置状态 优先判断Y
                    if (dy > MIN_Y) {
                        touchState = TOUCH_STATE_Y;
                    } else if (dx > MIN_X) {
                        touchState = TOUCH_STATE_X;
                    }
                } else if (touchState == TOUCH_STATE_X) {
                    //将事件传递给sildingMenu
                    if (sildingMenu != null) {
                        sildingMenu.onEvent(ev);
                    }
                    //取消,不处理
                    ev.setAction(MotionEvent.ACTION_CANCEL);
                    super.onTouchEvent(ev);
                    return true;
                }
                break;
            case MotionEvent.ACTION_UP:
                if (touchState == TOUCH_STATE_X) {
                    if (sildingMenu != null) {
                        sildingMenu.onEvent(ev);
                        //如果菜单是关的 重置
                        if (!sildingMenu.isOpen()) {
                            position = -1;
                            sildingMenu = null;
                        }
                    }
                    ev.setAction(MotionEvent.ACTION_CANCEL);
                    super.onTouchEvent(ev);
                    return true;
                }
                if (touchState == TOUCK_STATE_NULL) {
                    if (is) {
                        ev.setAction(MotionEvent.ACTION_CANCEL);
                        super.onTouchEvent(ev);
                        return true;
                    }
                }
                break;
        }
        return super.onTouchEvent(ev);
    }

至此,我们的ListView已经编写完成了,但是这样就行了吗?No,我们还有去对适配器进行修改,并且响应删除菜单的点击事件

MyBaseAdapter:

http://blog.csdn.net/w18756901575/article/details/51028177

Adapter:

package wkk.demo15;

import android.content.Context;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;

/**
 * Created by wkk on 2016/6/12.
 */
public class DemoAdapter extends MyBaseAdapter<String> {

    private MenuClickListener listener;

    public DemoAdapter(Context context) {
        super(context);
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        SlidingMenuViewGroup viewGroup = null;
        ViewHold viewHold = null;
        if (convertView == null) {
            viewHold = new ViewHold();
            View contentview = inflater.inflate(R.layout.item, null);

            TextView textView = new TextView(context);
            textView.setWidth(getDp(80));
            textView.setHeight(getDp(80));
            textView.setBackgroundColor(0xbbbbbbbb);
            textView.setGravity(Gravity.CENTER);
            textView.setText("删除");
            textView.setTag(0);

            TextView textView1 = new TextView(context);
            textView1.setWidth(getDp(80));
            textView1.setHeight(getDp(80));
            textView1.setBackgroundColor(0xbbff123f);
            textView1.setGravity(Gravity.CENTER);
            textView1.setText("测试");
            textView1.setTag(1);

            LinearLayout menu = new LinearLayout(context);
            menu.setLayoutParams(new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT
                    , ViewGroup.LayoutParams.MATCH_PARENT));
            menu.addView(textView);
            menu.addView(textView1);

            viewHold.text = (TextView) contentview.findViewById(R.id.text);
            viewGroup = new SlidingMenuViewGroup(contentview, menu);
            viewGroup.setTag(viewHold);

        } else {
            viewGroup = (SlidingMenuViewGroup) convertView;
            viewHold = (ViewHold) viewGroup.getTag();
        }

        viewHold.text.setText(getItem(position));


        final ViewGroup menu = (ViewGroup) viewGroup.getMenuView();
        final SlidingMenuViewGroup finalViewGroup = viewGroup;

        int count = menu.getChildCount();
        for (int i = 0; i < count; i++) {
            View v = menu.getChildAt(i);
            v.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    //将点击事件传递出去
                    if (listener != null) {
                        int tag = (int) v.getTag();
                        listener.onMenuClickListener(v, position, finalViewGroup, tag);
                        finalViewGroup.closeMenu();
                    }
                }
            });
        }

        return viewGroup;
    }


    private class ViewHold {
        TextView text;
    }

    //提供外部监听
    public void setListener(MenuClickListener listener) {
        this.listener = listener;
    }

    public interface MenuClickListener {
        void onMenuClickListener(View menu, int position, SlidingMenuViewGroup viewGroup, int tag);
    }

    private int getDp(int dp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
                context.getResources().getDisplayMetrics());
    }
}

MainActivity:

package wkk.demo15;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.TextView;
import android.widget.Toast;

import java.util.List;

public class MainActivity extends AppCompatActivity implements AdapterView.OnItemClickListener {

    private SlidingMenuListView listView;
    private DemoAdapter adapter;
    private List<String> list;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        listView = (SlidingMenuListView) findViewById(R.id.listview);
        adapter = new DemoAdapter(this);
        list = adapter.getList();
        for (int i = 0; i < 20; i++) {
            list.add("这是一条测试数据:  " + String.valueOf(i));
        }
        listView.setAdapter(adapter);
        listView.setOnItemClickListener(this);

        adapter.setListener(new DemoAdapter.MenuClickListener() {
            @Override
            public void onMenuClickListener(View menu, int position, SlidingMenuViewGroup viewGroup, int tag) {
                TextView textView = (TextView) menu;
                Toast.makeText(MainActivity.this, "点击了" + textView.getText(), Toast.LENGTH_SHORT).show();
                if (tag == 0) {
                    list.remove(position);
                    adapter.notifyDataSetChanged();
                }
            }
        });

    }

    private int dp2px(int dp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
                getResources().getDisplayMetrics());
    }

    private void setView() {
        TextView textView1 = new TextView(this);
        textView1.setText("content");
        textView1.setGravity(Gravity.CENTER);
        textView1.setBackgroundColor(0xffff123f);
        textView1.setHeight(dp2px(70));

        TextView textView2 = new TextView(this);
        textView2.setGravity(Gravity.CENTER);
        textView2.setWidth(dp2px(70));
        textView2.setHeight(dp2px(70));
        textView2.setText("menu");
        textView2.setBackgroundColor(0xbbbbbbbb);

        SlidingMenuViewGroup sildingMenuViewGroup = new SlidingMenuViewGroup(textView1, textView2);

        addContentView(sildingMenuViewGroup, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dp2px(70)));
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        Toast.makeText(this, "点击了" + String.valueOf(position), Toast.LENGTH_SHORT).show();
    }
}

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="wkk.demo14.MainActivity">

    <wkk.demo15.SlidingMenuListView
        android:id="@+id/listview"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </wkk.demo15.SlidingMenuListView>

</LinearLayout>

item.xml:

<?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="80dp"
    android:gravity="center">

    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="这是一条测试数据" />

</LinearLayout>

至此,整个代码就编写完成了,效果图已经在一开始就贴了出来,这里就不贴了,当然,有兴趣的可以对整个Demo进行进一步的封装

Demo下载地址:
http://download.csdn.net/detail/w18756901575/9546912

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值