Android 仿QQListView侧滑删除

来源:互联网 发布:java 微信发送消息 编辑:程序博客网 时间:2024/04/25 14:41

先上效果图

分析下整个效果如何实现
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

1 0
原创粉丝点击